Commit be7be2b8 authored by Ian Hickson's avatar Ian Hickson Committed by Adam Barth

Test service extensions (#7849)

...and fix bugs that the tests uncovered.

WRITE TEST FIND BUG
parent cce70d70
......@@ -2,9 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
class TestTestBinding extends AutomatedTestWidgetsFlutterBinding {
@override
DebugPrintCallback get debugPrintOverride => testPrint;
static void testPrint(String message, { int wrapWidth }) { print(message); }
}
Future<Null> guardedHelper(WidgetTester tester) {
return TestAsyncUtils.guard(() async {
await tester.pumpWidget(new Text('Hello'));
......@@ -12,8 +19,8 @@ Future<Null> guardedHelper(WidgetTester tester) {
}
void main() {
new TestTestBinding();
testWidgets('TestAsyncUtils - custom guarded sections', (WidgetTester tester) async {
debugPrint = (String message, { int wrapWidth }) { print(message); };
await tester.pumpWidget(new Container());
expect(find.byElementType(Container), isNotNull);
guardedHelper(tester);
......
......@@ -2,16 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
class TestTestBinding extends AutomatedTestWidgetsFlutterBinding {
@override
DebugPrintCallback get debugPrintOverride => testPrint;
static void testPrint(String message, { int wrapWidth }) { print(message); }
}
Future<Null> helperFunction(WidgetTester tester) async {
await tester.pump();
}
void main() {
new TestTestBinding();
testWidgets('TestAsyncUtils - handling unguarded async helper functions', (WidgetTester tester) async {
debugPrint = (String message, { int wrapWidth }) { print(message); };
helperFunction(tester);
helperFunction(tester);
// this should fail
......
......@@ -46,7 +46,7 @@ class TestAssetBundle extends AssetBundle {
}
@override
Future<dynamic> loadStructuredData<T>(String key, Future<T> parser(String value)) async {
Future<T> loadStructuredData<T>(String key, Future<T> parser(String value)) async {
return parser(await loadString(key));
}
......
......@@ -13,6 +13,7 @@ export 'src/foundation/assertions.dart';
export 'src/foundation/basic_types.dart';
export 'src/foundation/binding.dart';
export 'src/foundation/change_notifier.dart';
export 'src/foundation/debug.dart';
export 'src/foundation/licenses.dart';
export 'src/foundation/observer_list.dart';
export 'src/foundation/platform.dart';
......
......@@ -19,7 +19,7 @@ import 'basic_types.dart';
/// "type" key will be set to the string `_extensionType` to indicate
/// that this is a return value from a service extension, and the
/// "method" key will be set to the full name of the method.
typedef Future<Map<String, dynamic>> ServiceExtensionCallback(Map<String, String> parameters);
typedef Future<Map<String, String>> ServiceExtensionCallback(Map<String, String> parameters);
/// Base class for mixins that provide singleton services (also known as
/// "bindings").
......@@ -56,7 +56,7 @@ abstract class BindingBase {
initServiceExtensions();
assert(_debugServiceExtensionsRegistered);
developer.postEvent('Flutter.FrameworkInitialization', <String, dynamic>{});
developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
developer.Timeline.finishSync();
}
......@@ -150,7 +150,7 @@ abstract class BindingBase {
name: name,
callback: (Map<String, String> parameters) async {
await callback();
return <String, dynamic>{};
return <String, String>{};
}
);
}
......@@ -181,7 +181,7 @@ abstract class BindingBase {
callback: (Map<String, String> parameters) async {
if (parameters.containsKey('enabled'))
await setter(parameters['enabled'] == 'true');
return <String, dynamic>{ 'enabled': await getter() };
return <String, String>{ 'enabled': await getter() ? 'true' : 'false' };
}
);
}
......@@ -211,7 +211,7 @@ abstract class BindingBase {
callback: (Map<String, String> parameters) async {
if (parameters.containsKey(name))
await setter(double.parse(parameters[name]));
return <String, dynamic>{ name: await getter() };
return <String, String>{ name: (await getter()).toString() };
}
);
}
......@@ -240,7 +240,7 @@ abstract class BindingBase {
callback: (Map<String, String> parameters) async {
if (parameters.containsKey('value'))
await setter(parameters['value']);
return <String, dynamic>{ 'value': await getter() };
return <String, String>{ 'value': await getter() };
}
);
}
......@@ -267,7 +267,7 @@ abstract class BindingBase {
assert(method == methodName);
dynamic caughtException;
StackTrace caughtStack;
Map<String, dynamic> result;
Map<String, String> result;
try {
result = await callback(parameters);
} catch (exception, stack) {
......@@ -286,10 +286,10 @@ abstract class BindingBase {
));
return new developer.ServiceExtensionResponse.error(
developer.ServiceExtensionResponse.extensionError,
JSON.encode(<String, dynamic>{
JSON.encode(<String, String>{
'exception': caughtException.toString(),
'stack': caughtStack.toString(),
'method': method
'method': method,
})
);
}
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'assertions.dart';
import 'print.dart';
/// Returns true if none of the foundation library debug variables have been
/// changed.
///
/// This function is used by the test framework to ensure that debug variables
/// haven't been inadvertently changed.
///
/// The `debugPrintOverride` argument can be specified to indicate the expected
/// value of the [debugPrint] variable. This is useful for test frameworks that
/// override [debugPrint] themselves and want to check that their own custom
/// value wasn't overridden by a test.
///
/// See [https://docs.flutter.io/flutter/foundation/foundation-library.html] for
/// a complete list.
bool debugAssertAllFoundationVarsUnset(String reason, { DebugPrintCallback debugPrintOverride: debugPrintThrottled }) {
assert(() {
if (debugPrint != debugPrintOverride)
throw new FlutterError(reason);
return true;
});
return true;
}
......@@ -57,8 +57,7 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding,
if (debugPaintSizeEnabled == value)
return new Future<Null>.value();
debugPaintSizeEnabled = value;
_forceRepaint();
return endOfFrame;
return _forceRepaint();
}
);
return true;
......@@ -78,8 +77,8 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding,
bool repaint = debugRepaintRainbowEnabled && !value;
debugRepaintRainbowEnabled = value;
if (repaint)
_forceRepaint();
return endOfFrame;
return _forceRepaint();
return new Future<Null>.value();
}
);
return true;
......@@ -249,13 +248,14 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding,
super.hitTest(result, position); // ignore: abstract_super_member_reference
}
void _forceRepaint() {
Future<Null> _forceRepaint() {
RenderObjectVisitor visitor;
visitor = (RenderObject child) {
child.markNeedsPaint();
child.visitChildren(visitor);
};
instance?.renderView?.visitChildren(visitor);
return endOfFrame;
}
}
......
......@@ -66,7 +66,7 @@ abstract class AssetBundle {
///
/// Implementations may cache the result, so a particular key should only be
/// used with one parser for the lifetime of the asset bundle.
Future<dynamic> loadStructuredData<T>(String key, Future<T> parser(String value));
Future<T> loadStructuredData<T>(String key, Future<T> parser(String value));
/// If this is a caching asset bundle, and the given key describes a cached
/// asset, then evict the asset from the cache so that the next time it is
......@@ -110,7 +110,7 @@ class NetworkAssetBundle extends AssetBundle {
/// The result is not cached. The parser is run each time the resource is
/// fetched.
@override
Future<dynamic> loadStructuredData<T>(String key, Future<T> parser(String value)) async {
Future<T> loadStructuredData<T>(String key, Future<T> parser(String value)) async {
assert(key != null);
assert(parser != null);
return parser(await loadString(key));
......@@ -159,7 +159,7 @@ abstract class CachingAssetBundle extends AssetBundle {
/// subsequent calls will be a [SynchronousFuture], which resolves its
/// callback synchronously.
@override
Future<dynamic> loadStructuredData<T>(String key, Future<T> parser(String value)) {
Future<T> loadStructuredData<T>(String key, Future<T> parser(String value)) {
assert(key != null);
assert(parser != null);
if (_structuredDataCache.containsKey(key))
......
......@@ -86,8 +86,7 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren
if (WidgetsApp.showPerformanceOverlayOverride == value)
return new Future<Null>.value();
WidgetsApp.showPerformanceOverlayOverride = value;
buildOwner.reassemble(renderViewElement);
return endOfFrame;
return _forceRebuild();
}
);
......@@ -98,12 +97,19 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren
if (WidgetsApp.debugAllowBannerOverride == value)
return new Future<Null>.value();
WidgetsApp.debugAllowBannerOverride = value;
buildOwner.reassemble(renderViewElement);
return endOfFrame;
return _forceRebuild();
}
);
}
Future<Null> _forceRebuild() {
if (renderViewElement != null) {
buildOwner.reassemble(renderViewElement);
return endOfFrame;
}
return new Future<Null>.value();
}
/// The [BuildOwner] in charge of executing the build pipeline for the
/// widget tree rooted at this binding.
BuildOwner get buildOwner => _buildOwner;
......
This diff is collapsed.
......@@ -86,6 +86,14 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
RendererBinding,
// Services binding omitted to avoid dragging in the licenses code.
WidgetsBinding {
TestWidgetsFlutterBinding() {
debugPrint = debugPrintOverride;
}
@protected
DebugPrintCallback get debugPrintOverride => debugPrint;
/// Creates and initializes the binding. This function is
/// idempotent; calling it a second time will just return the
/// previously-created instance.
......@@ -399,6 +407,10 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
assert(debugAssertNoTransientCallbacks(
'An animation is still running even after the widget tree was disposed.'
));
assert(debugAssertAllFoundationVarsUnset(
'The value of a foundation debug variable was changed by the test.',
debugPrintOverride: debugPrintOverride,
));
assert(debugAssertAllRenderVarsUnset(
'The value of a rendering debug variable was changed by the test.'
));
......@@ -431,7 +443,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override
void initInstances() {
debugPrint = debugPrintSynchronously;
super.initInstances();
ui.window.onBeginFrame = null;
}
......@@ -439,6 +450,9 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
FakeAsync _fakeAsync;
Clock _clock;
@override
DebugPrintCallback get debugPrintOverride => debugPrintSynchronously;
@override
test_package.Timeout get defaultTestTimeout => const test_package.Timeout(const Duration(seconds: 5));
......
......@@ -82,7 +82,7 @@ Future<Null> _testFile(String testName, int wantedExitCode, String workingDirect
expect(haveSeenStdErrMarker, isFalse);
haveSeenStdErrMarker = true;
}
expect(outputLine, matches(expectationLine));
expect(outputLine, matches(expectationLine), verbose: true, reason: 'Full output:\n- - - -----8<----- - - -\n${output.join("\n")}\n- - - -----8<----- - - -');
expectationLineNumber += 1;
outputLineNumber += 1;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment