diff --git a/dev/automated_tests/flutter_test/test_async_utils_guarded_test.dart b/dev/automated_tests/flutter_test/test_async_utils_guarded_test.dart index f700bb0abf..dad80530a7 100644 --- a/dev/automated_tests/flutter_test/test_async_utils_guarded_test.dart +++ b/dev/automated_tests/flutter_test/test_async_utils_guarded_test.dart @@ -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 guardedHelper(WidgetTester tester) { return TestAsyncUtils.guard(() async { await tester.pumpWidget(new Text('Hello')); @@ -12,8 +19,8 @@ Future 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); diff --git a/dev/automated_tests/flutter_test/test_async_utils_unguarded_test.dart b/dev/automated_tests/flutter_test/test_async_utils_unguarded_test.dart index 9974cd3dbd..3f2ce913e8 100644 --- a/dev/automated_tests/flutter_test/test_async_utils_unguarded_test.dart +++ b/dev/automated_tests/flutter_test/test_async_utils_unguarded_test.dart @@ -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 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 diff --git a/examples/flutter_gallery/test/example_code_parser_test.dart b/examples/flutter_gallery/test/example_code_parser_test.dart index 61e2e169e3..94b1d9c411 100644 --- a/examples/flutter_gallery/test/example_code_parser_test.dart +++ b/examples/flutter_gallery/test/example_code_parser_test.dart @@ -46,7 +46,7 @@ class TestAssetBundle extends AssetBundle { } @override - Future loadStructuredData(String key, Future parser(String value)) async { + Future loadStructuredData(String key, Future parser(String value)) async { return parser(await loadString(key)); } diff --git a/packages/flutter/lib/foundation.dart b/packages/flutter/lib/foundation.dart index 3a50f12b7f..357d7a7b79 100644 --- a/packages/flutter/lib/foundation.dart +++ b/packages/flutter/lib/foundation.dart @@ -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'; diff --git a/packages/flutter/lib/src/foundation/binding.dart b/packages/flutter/lib/src/foundation/binding.dart index b73ab860cc..2e070ed29c 100644 --- a/packages/flutter/lib/src/foundation/binding.dart +++ b/packages/flutter/lib/src/foundation/binding.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> ServiceExtensionCallback(Map parameters); +typedef Future> ServiceExtensionCallback(Map 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', {}); + developer.postEvent('Flutter.FrameworkInitialization', {}); developer.Timeline.finishSync(); } @@ -150,7 +150,7 @@ abstract class BindingBase { name: name, callback: (Map parameters) async { await callback(); - return {}; + return {}; } ); } @@ -181,7 +181,7 @@ abstract class BindingBase { callback: (Map parameters) async { if (parameters.containsKey('enabled')) await setter(parameters['enabled'] == 'true'); - return { 'enabled': await getter() }; + return { 'enabled': await getter() ? 'true' : 'false' }; } ); } @@ -211,7 +211,7 @@ abstract class BindingBase { callback: (Map parameters) async { if (parameters.containsKey(name)) await setter(double.parse(parameters[name])); - return { name: await getter() }; + return { name: (await getter()).toString() }; } ); } @@ -240,7 +240,7 @@ abstract class BindingBase { callback: (Map parameters) async { if (parameters.containsKey('value')) await setter(parameters['value']); - return { 'value': await getter() }; + return { 'value': await getter() }; } ); } @@ -267,7 +267,7 @@ abstract class BindingBase { assert(method == methodName); dynamic caughtException; StackTrace caughtStack; - Map result; + Map 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({ + JSON.encode({ 'exception': caughtException.toString(), 'stack': caughtStack.toString(), - 'method': method + 'method': method, }) ); } diff --git a/packages/flutter/lib/src/foundation/debug.dart b/packages/flutter/lib/src/foundation/debug.dart new file mode 100644 index 0000000000..8adfc3d2ae --- /dev/null +++ b/packages/flutter/lib/src/foundation/debug.dart @@ -0,0 +1,28 @@ +// 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; +} diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart index f857349f5c..05ddda40cf 100644 --- a/packages/flutter/lib/src/rendering/binding.dart +++ b/packages/flutter/lib/src/rendering/binding.dart @@ -57,8 +57,7 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding, if (debugPaintSizeEnabled == value) return new Future.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.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 _forceRepaint() { RenderObjectVisitor visitor; visitor = (RenderObject child) { child.markNeedsPaint(); child.visitChildren(visitor); }; instance?.renderView?.visitChildren(visitor); + return endOfFrame; } } diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart index 4a03ed206b..dbbb24c1b5 100644 --- a/packages/flutter/lib/src/services/asset_bundle.dart +++ b/packages/flutter/lib/src/services/asset_bundle.dart @@ -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 loadStructuredData(String key, Future parser(String value)); + Future loadStructuredData(String key, Future 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 loadStructuredData(String key, Future parser(String value)) async { + Future loadStructuredData(String key, Future 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 loadStructuredData(String key, Future parser(String value)) { + Future loadStructuredData(String key, Future parser(String value)) { assert(key != null); assert(parser != null); if (_structuredDataCache.containsKey(key)) diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart index 82410b8c3a..1ede4c64d5 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -86,8 +86,7 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren if (WidgetsApp.showPerformanceOverlayOverride == value) return new Future.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.value(); WidgetsApp.debugAllowBannerOverride = value; - buildOwner.reassemble(renderViewElement); - return endOfFrame; + return _forceRebuild(); } ); } + Future _forceRebuild() { + if (renderViewElement != null) { + buildOwner.reassemble(renderViewElement); + return endOfFrame; + } + return new Future.value(); + } + /// The [BuildOwner] in charge of executing the build pipeline for the /// widget tree rooted at this binding. BuildOwner get buildOwner => _buildOwner; diff --git a/packages/flutter/test/foundation/service_extensions_test.dart b/packages/flutter/test/foundation/service_extensions_test.dart new file mode 100644 index 0000000000..40c9003a3f --- /dev/null +++ b/packages/flutter/test/foundation/service_extensions_test.dart @@ -0,0 +1,336 @@ +// 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 'dart:async'; +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:test/test.dart'; + +class TestServiceExtensionsBinding extends BindingBase + with SchedulerBinding, + ServicesBinding, + GestureBinding, + RendererBinding, + WidgetsBinding { + + final Map extensions = {}; + + @override + void registerServiceExtension({ + @required String name, + @required ServiceExtensionCallback callback + }) { + expect(extensions.containsKey(name), isFalse); + extensions[name] = callback; + } + + Future> testExtension(String name, Map arguments) { + expect(extensions.containsKey(name), isTrue); + return extensions[name](arguments); + } + + int reassembled = 0; + @override + Future reassembleApplication() { + reassembled += 1; + return super.reassembleApplication(); + } + + bool frameScheduled = false; + @override + void scheduleFrame() { + frameScheduled = true; + } + void doFrame() { + frameScheduled = false; + if (ui.window.onBeginFrame != null) + ui.window.onBeginFrame(const Duration()); + } + + Future flushMicrotasks() { + Completer completer = new Completer(); + new Timer(const Duration(), () { + completer.complete(); + }); + return completer.future; + } +} + +void main() { + TestServiceExtensionsBinding binding; + List console = []; + + test('Service extensions - pretest', () async { + binding = new TestServiceExtensionsBinding(); + expect(binding.frameScheduled, isTrue); + binding.doFrame(); // initial frame scheduled by creating the binding + expect(binding.frameScheduled, isFalse); + + expect(debugPrint, equals(debugPrintThrottled)); + debugPrint = (String message, { int wrapWidth }) { + console.add(message); + }; + }); + + // The following list is alphabetical, one test per extension. + // + // The order doesn't really matter except that the pretest and posttest tests + // must be first and last respectively. + + test('Service extensions - debugAllowBanner', () async { + Map result; + + expect(binding.frameScheduled, isFalse); + expect(WidgetsApp.debugAllowBannerOverride, true); + result = await binding.testExtension('debugAllowBanner', {}); + expect(result, { 'enabled': 'true' }); + expect(WidgetsApp.debugAllowBannerOverride, true); + result = await binding.testExtension('debugAllowBanner', { 'enabled': 'false' }); + expect(result, { 'enabled': 'false' }); + expect(WidgetsApp.debugAllowBannerOverride, false); + result = await binding.testExtension('debugAllowBanner', {}); + expect(result, { 'enabled': 'false' }); + expect(WidgetsApp.debugAllowBannerOverride, false); + result = await binding.testExtension('debugAllowBanner', { 'enabled': 'true' }); + expect(result, { 'enabled': 'true' }); + expect(WidgetsApp.debugAllowBannerOverride, true); + result = await binding.testExtension('debugAllowBanner', {}); + expect(result, { 'enabled': 'true' }); + expect(WidgetsApp.debugAllowBannerOverride, true); + expect(binding.frameScheduled, isFalse); + }); + + test('Service extensions - debugDumpApp', () async { + Map result; + + result = await binding.testExtension('debugDumpApp', {}); + expect(result, {}); + expect(console, ['TestServiceExtensionsBinding - CHECKED MODE', '']); + console.clear(); + }); + + test('Service extensions - debugDumpRenderTree', () async { + Map result; + + result = await binding.testExtension('debugDumpRenderTree', {}); + expect(result, {}); + expect(console, [ + 'RenderView\n' + ' debug mode enabled - linux\n' + ' window size: Size(800.0, 600.0) (in physical pixels)\n' + ' device pixel ratio: 1.0 (physical pixels per logical pixel)\n' + ' configuration: Size(800.0, 600.0) at 1.0x (in logical pixels)\n' + '\n' + ]); + console.clear(); + }); + + test('Service extensions - debugPaint', () async { + Map result; + Future> pendingResult; + bool completed; + + expect(binding.frameScheduled, isFalse); + expect(debugPaintSizeEnabled, false); + result = await binding.testExtension('debugPaint', {}); + expect(result, { 'enabled': 'false' }); + expect(debugPaintSizeEnabled, false); + expect(binding.frameScheduled, isFalse); + pendingResult = binding.testExtension('debugPaint', { 'enabled': 'true' }); + completed = false; + pendingResult.whenComplete(() { completed = true; }); + await binding.flushMicrotasks(); + expect(binding.frameScheduled, isTrue); + expect(completed, isFalse); + binding.doFrame(); + await binding.flushMicrotasks(); + expect(completed, isTrue); + expect(binding.frameScheduled, isFalse); + result = await pendingResult; + expect(result, { 'enabled': 'true' }); + expect(debugPaintSizeEnabled, true); + result = await binding.testExtension('debugPaint', {}); + expect(result, { 'enabled': 'true' }); + expect(debugPaintSizeEnabled, true); + expect(binding.frameScheduled, isFalse); + pendingResult = binding.testExtension('debugPaint', { 'enabled': 'false' }); + await binding.flushMicrotasks(); + expect(binding.frameScheduled, isTrue); + binding.doFrame(); + expect(binding.frameScheduled, isFalse); + result = await pendingResult; + expect(result, { 'enabled': 'false' }); + expect(debugPaintSizeEnabled, false); + result = await binding.testExtension('debugPaint', {}); + expect(result, { 'enabled': 'false' }); + expect(debugPaintSizeEnabled, false); + expect(binding.frameScheduled, isFalse); + }); + + test('Service extensions - evict', () async { + Map result; + bool completed; + + completed = false; + PlatformMessages.setMockBinaryMessageHandler('flutter/assets', (ByteData message) async { + expect(UTF8.decode(message.buffer.asUint8List()), 'test'); + completed = true; + return new ByteData(5); // 0x0000000000 + }); + bool data; + data = await rootBundle.loadStructuredData('test', (String value) async { expect(value, '\x00\x00\x00\x00\x00'); return true; }); + expect(data, isTrue); + expect(completed, isTrue); + completed = false; + data = await rootBundle.loadStructuredData('test', (String value) async { expect(true, isFalse); return null; }); + expect(data, isTrue); + expect(completed, isFalse); + result = await binding.testExtension('evict', { 'value': 'test' }); + expect(result, { 'value': '' }); + expect(completed, isFalse); + data = await rootBundle.loadStructuredData('test', (String value) async { expect(value, '\x00\x00\x00\x00\x00'); return false; }); + expect(data, isFalse); + expect(completed, isTrue); + PlatformMessages.setMockBinaryMessageHandler('flutter/assets', null); + }); + + test('Service extensions - exit', () async { + // no test for _calling_ 'exit', because that should terminate the process! + expect(binding.extensions.containsKey('exit'), isTrue); + }); + + test('Service extensions - frameworkPresent', () async { + Map result; + + result = await binding.testExtension('frameworkPresent', {}); + expect(result, {}); + }); + + test('Service extensions - repaintRainbow', () async { + Map result; + Future> pendingResult; + bool completed; + + expect(binding.frameScheduled, isFalse); + expect(debugRepaintRainbowEnabled, false); + result = await binding.testExtension('repaintRainbow', {}); + expect(result, { 'enabled': 'false' }); + expect(debugRepaintRainbowEnabled, false); + expect(binding.frameScheduled, isFalse); + pendingResult = binding.testExtension('repaintRainbow', { 'enabled': 'true' }); + completed = false; + pendingResult.whenComplete(() { completed = true; }); + await binding.flushMicrotasks(); + expect(completed, true); + expect(binding.frameScheduled, isFalse); + result = await pendingResult; + expect(result, { 'enabled': 'true' }); + expect(debugRepaintRainbowEnabled, true); + result = await binding.testExtension('repaintRainbow', {}); + expect(result, { 'enabled': 'true' }); + expect(debugRepaintRainbowEnabled, true); + expect(binding.frameScheduled, isFalse); + pendingResult = binding.testExtension('repaintRainbow', { 'enabled': 'false' }); + completed = false; + pendingResult.whenComplete(() { completed = true; }); + await binding.flushMicrotasks(); + expect(completed, false); + expect(binding.frameScheduled, isTrue); + binding.doFrame(); + await binding.flushMicrotasks(); + expect(completed, true); + expect(binding.frameScheduled, isFalse); + result = await pendingResult; + expect(result, { 'enabled': 'false' }); + expect(debugRepaintRainbowEnabled, false); + result = await binding.testExtension('repaintRainbow', {}); + expect(result, { 'enabled': 'false' }); + expect(debugRepaintRainbowEnabled, false); + expect(binding.frameScheduled, isFalse); + }); + + test('Service extensions - reassemble', () async { + Map result; + Future> pendingResult; + bool completed; + + completed = false; + expect(binding.reassembled, 0); + pendingResult = binding.testExtension('reassemble', {}); + pendingResult.whenComplete(() { completed = true; }); + await binding.flushMicrotasks(); + expect(binding.frameScheduled, isTrue); + expect(completed, false); + binding.doFrame(); + await binding.flushMicrotasks(); + expect(completed, true); + expect(binding.frameScheduled, isFalse); + result = await pendingResult; + expect(result, {}); + expect(binding.reassembled, 1); + }); + + test('Service extensions - showPerformanceOverlay', () async { + Map result; + + expect(binding.frameScheduled, isFalse); + expect(WidgetsApp.showPerformanceOverlayOverride, false); + result = await binding.testExtension('showPerformanceOverlay', {}); + expect(result, { 'enabled': 'false' }); + expect(WidgetsApp.showPerformanceOverlayOverride, false); + result = await binding.testExtension('showPerformanceOverlay', { 'enabled': 'true' }); + expect(result, { 'enabled': 'true' }); + expect(WidgetsApp.showPerformanceOverlayOverride, true); + result = await binding.testExtension('showPerformanceOverlay', {}); + expect(result, { 'enabled': 'true' }); + expect(WidgetsApp.showPerformanceOverlayOverride, true); + result = await binding.testExtension('showPerformanceOverlay', { 'enabled': 'false' }); + expect(result, { 'enabled': 'false' }); + expect(WidgetsApp.showPerformanceOverlayOverride, false); + result = await binding.testExtension('showPerformanceOverlay', {}); + expect(result, { 'enabled': 'false' }); + expect(WidgetsApp.showPerformanceOverlayOverride, false); + expect(binding.frameScheduled, isFalse); + }); + + test('Service extensions - timeDilation', () async { + Map result; + + expect(binding.frameScheduled, isFalse); + expect(timeDilation, 1.0); + result = await binding.testExtension('timeDilation', {}); + expect(result, { 'timeDilation': '1.0' }); + expect(timeDilation, 1.0); + result = await binding.testExtension('timeDilation', { 'timeDilation': '100.0' }); + expect(result, { 'timeDilation': '100.0' }); + expect(timeDilation, 100.0); + result = await binding.testExtension('timeDilation', {}); + expect(result, { 'timeDilation': '100.0' }); + expect(timeDilation, 100.0); + result = await binding.testExtension('timeDilation', { 'timeDilation': '1.0' }); + expect(result, { 'timeDilation': '1.0' }); + expect(timeDilation, 1.0); + result = await binding.testExtension('timeDilation', {}); + expect(result, { 'timeDilation': '1.0' }); + expect(timeDilation, 1.0); + expect(binding.frameScheduled, isFalse); + }); + + test('Service extensions - posttest', () async { + // If you add a service extension... TEST IT! :-) + // ...then increment this number. + expect(binding.extensions.length, 11); + + expect(console, isEmpty); + debugPrint = debugPrintThrottled; + }); +} diff --git a/packages/flutter_test/lib/src/binding.dart b/packages/flutter_test/lib/src/binding.dart index 4c76384456..26f96e3d08 100644 --- a/packages/flutter_test/lib/src/binding.dart +++ b/packages/flutter_test/lib/src/binding.dart @@ -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)); diff --git a/packages/flutter_tools/test/test_test.dart b/packages/flutter_tools/test/test_test.dart index 70ac6e1ced..587bcb7f87 100644 --- a/packages/flutter_tools/test/test_test.dart +++ b/packages/flutter_tools/test/test_test.dart @@ -82,7 +82,7 @@ Future _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; }