1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'dart:io';
6///
7/// @docImport 'controller.dart';
8/// @docImport 'test_pointer.dart';
9/// @docImport 'widget_tester.dart';
10library;
11
12import 'dart:async';
13import 'dart:ui' as ui;
14
15import 'package:clock/clock.dart';
16import 'package:fake_async/fake_async.dart';
17import 'package:flutter/foundation.dart';
18import 'package:flutter/gestures.dart';
19import 'package:flutter/rendering.dart';
20import 'package:flutter/scheduler.dart';
21import 'package:flutter/services.dart';
22import 'package:flutter/widgets.dart';
23import 'package:matcher/expect.dart' show fail;
24import 'package:stack_trace/stack_trace.dart' as stack_trace;
25import 'package:test_api/scaffolding.dart' as test_package show Timeout;
26import 'package:vector_math/vector_math_64.dart';
27
28import '_binding_io.dart' if (dart.library.js_interop) '_binding_web.dart' as binding;
29import 'goldens.dart';
30import 'platform.dart';
31import 'restoration.dart';
32import 'stack_manipulation.dart';
33import 'test_async_utils.dart';
34import 'test_default_binary_messenger.dart';
35import 'test_exception_reporter.dart';
36import 'test_text_input.dart';
37import 'window.dart';
38
39/// Phases that can be reached by [WidgetTester.pumpWidget] and
40/// [TestWidgetsFlutterBinding.pump].
41///
42/// See [WidgetsBinding.drawFrame] for a more detailed description of some of
43/// these phases.
44enum EnginePhase {
45 /// The build phase in the widgets library. See [BuildOwner.buildScope].
46 build,
47
48 /// The layout phase in the rendering library. See [PipelineOwner.flushLayout].
49 layout,
50
51 /// The compositing bits update phase in the rendering library. See
52 /// [PipelineOwner.flushCompositingBits].
53 compositingBits,
54
55 /// The paint phase in the rendering library. See [PipelineOwner.flushPaint].
56 paint,
57
58 /// The compositing phase in the rendering library. See
59 /// [RenderView.compositeFrame]. This is the phase in which data is sent to
60 /// the GPU. If semantics are not enabled, then this is the last phase.
61 composite,
62
63 /// The semantics building phase in the rendering library. See
64 /// [PipelineOwner.flushSemantics].
65 flushSemantics,
66
67 /// The final phase in the rendering library, wherein semantics information is
68 /// sent to the embedder. See [SemanticsOwner.sendSemanticsUpdate].
69 sendSemanticsUpdate,
70}
71
72/// Signature of callbacks used to intercept messages on a given channel.
73///
74/// See [TestDefaultBinaryMessenger.setMockDecodedMessageHandler] for more details.
75typedef _MockMessageHandler = Future<void> Function(Object?);
76
77/// Parts of the system that can generate pointer events that reach the test
78/// binding.
79///
80/// This is used to identify how to handle events in the
81/// [LiveTestWidgetsFlutterBinding]. See
82/// [TestWidgetsFlutterBinding.dispatchEvent].
83enum TestBindingEventSource {
84 /// The pointer event came from the test framework itself, e.g. from a
85 /// [TestGesture] created by [WidgetTester.startGesture].
86 test,
87
88 /// The pointer event came from the system, presumably as a result of the user
89 /// interactive directly with the device while the test was running.
90 device,
91}
92
93const Size _kDefaultTestViewportSize = Size(800.0, 600.0);
94
95/// Overrides the [ServicesBinding]'s binary messenger logic to use
96/// [TestDefaultBinaryMessenger].
97///
98/// Test bindings that are used by tests that mock message handlers for plugins
99/// should mix in this binding to enable the use of the
100/// [TestDefaultBinaryMessenger] APIs.
101mixin TestDefaultBinaryMessengerBinding on BindingBase, ServicesBinding {
102 @override
103 void initInstances() {
104 super.initInstances();
105 _instance = this;
106 }
107
108 /// The current [TestDefaultBinaryMessengerBinding], if one has been created.
109 static TestDefaultBinaryMessengerBinding get instance => BindingBase.checkInstance(_instance);
110 static TestDefaultBinaryMessengerBinding? _instance;
111
112 @override
113 TestDefaultBinaryMessenger get defaultBinaryMessenger =>
114 super.defaultBinaryMessenger as TestDefaultBinaryMessenger;
115
116 @override
117 TestDefaultBinaryMessenger createBinaryMessenger() {
118 Future<ByteData?> keyboardHandler(ByteData? message) async {
119 return const StandardMethodCodec().encodeSuccessEnvelope(<int, int>{});
120 }
121
122 return TestDefaultBinaryMessenger(
123 super.createBinaryMessenger(),
124 outboundHandlers: <String, MessageHandler>{'flutter/keyboard': keyboardHandler},
125 );
126 }
127}
128
129/// Accessibility announcement data passed to [SemanticsService.announce] captured in a test.
130///
131/// This class is intended to be used by the testing API to store the announcements
132/// in a structured form so that tests can verify announcement details. The fields
133/// of this class correspond to parameters of the [SemanticsService.announce] method.
134///
135/// See also:
136///
137/// * [WidgetTester.takeAnnouncements], which is the test API that uses this class.
138class CapturedAccessibilityAnnouncement {
139 const CapturedAccessibilityAnnouncement._(this.message, this.textDirection, this.assertiveness);
140
141 /// The accessibility message announced by the framework.
142 final String message;
143
144 /// The direction in which the text of the [message] flows.
145 final TextDirection textDirection;
146
147 /// Determines the assertiveness level of the accessibility announcement.
148 final Assertiveness assertiveness;
149}
150
151// Examples can assume:
152// late TestWidgetsFlutterBinding binding;
153// late Size someSize;
154
155/// Base class for bindings used by widgets library tests.
156///
157/// The [ensureInitialized] method creates (if necessary) and returns an
158/// instance of the appropriate subclass. (If one is already created, it returns
159/// that one, even if it's not the one that it would normally create. This
160/// allows tests to force the use of [LiveTestWidgetsFlutterBinding] even in a
161/// normal unit test environment, e.g. to test that binding.)
162///
163/// When using these bindings, certain features are disabled. For
164/// example, [timeDilation] is reset to 1.0 on initialization.
165///
166/// In non-browser tests, the binding overrides `HttpClient` creation with a
167/// fake client that always returns a status code of 400. This is to prevent
168/// tests from making network calls, which could introduce flakiness. A test
169/// that actually needs to make a network call should provide its own
170/// `HttpClient` to the code making the call, so that it can appropriately mock
171/// or fake responses.
172///
173/// ### Coordinate spaces
174///
175/// [TestWidgetsFlutterBinding] might be run on devices of different screen
176/// sizes, while the testing widget is still told the same size to ensure
177/// consistent results. Consequently, code that deals with positions (such as
178/// pointer events or painting) must distinguish between two coordinate spaces:
179///
180/// * The _local coordinate space_ is the one used by the testing widget
181/// (typically an 800 by 600 window, but can be altered by [setSurfaceSize]).
182/// * The _global coordinate space_ is the one used by the device.
183///
184/// Positions can be transformed between coordinate spaces with [localToGlobal]
185/// and [globalToLocal].
186abstract class TestWidgetsFlutterBinding extends BindingBase
187 with
188 SchedulerBinding,
189 ServicesBinding,
190 GestureBinding,
191 SemanticsBinding,
192 RendererBinding,
193 PaintingBinding,
194 WidgetsBinding,
195 TestDefaultBinaryMessengerBinding {
196 /// Constructor for [TestWidgetsFlutterBinding].
197 ///
198 /// This constructor overrides the [debugPrint] global hook to point to
199 /// [debugPrintOverride], which can be overridden by subclasses.
200 TestWidgetsFlutterBinding()
201 : platformDispatcher = TestPlatformDispatcher(platformDispatcher: PlatformDispatcher.instance) {
202 platformDispatcher.defaultRouteNameTestValue = '/';
203 debugPrint = debugPrintOverride;
204 debugDisableShadows = disableShadows;
205 }
206
207 /// Deprecated. Will be removed in a future version of Flutter.
208 ///
209 /// This property has been deprecated to prepare for Flutter's upcoming
210 /// support for multiple views and multiple windows.
211 ///
212 /// This represents a combination of a [TestPlatformDispatcher] and a singular
213 /// [TestFlutterView]. Platform-specific test values can be set through
214 /// [WidgetTester.platformDispatcher] instead. When testing individual widgets
215 /// or applications using [WidgetTester.pumpWidget], view-specific test values
216 /// can be set through [WidgetTester.view]. If multiple views are defined, the
217 /// appropriate view can be found using [WidgetTester.viewOf] if a sub-view
218 /// is needed.
219 ///
220 /// See also:
221 ///
222 /// * [WidgetTester.platformDispatcher] for changing platform-specific values
223 /// for testing.
224 /// * [WidgetTester.view] and [WidgetTester.viewOf] for changing view-specific
225 /// values for testing.
226 /// * [BindingBase.window] for guidance dealing with this property outside of
227 /// a testing context.
228 @Deprecated(
229 'Use WidgetTester.platformDispatcher or WidgetTester.view instead. '
230 'Deprecated to prepare for the upcoming multi-window support. '
231 'This feature was deprecated after v3.9.0-0.1.pre.',
232 )
233 @override
234 late final TestWindow window;
235
236 @override
237 final TestPlatformDispatcher platformDispatcher;
238
239 @override
240 TestRestorationManager get restorationManager {
241 _restorationManager ??= createRestorationManager();
242 return _restorationManager!;
243 }
244
245 TestRestorationManager? _restorationManager;
246
247 /// Called by the test framework at the beginning of a widget test to
248 /// prepare the binding for the next test.
249 ///
250 /// If [registerTestTextInput] returns true when this method is called,
251 /// the [testTextInput] is configured to simulate the keyboard.
252 void reset() {
253 _restorationManager?.dispose();
254 _restorationManager = null;
255 platformDispatcher.defaultRouteNameTestValue = '/';
256 resetGestureBinding();
257 testTextInput.reset();
258 if (registerTestTextInput) {
259 _testTextInput.register();
260 }
261 CustomSemanticsAction.resetForTests(); // ignore: invalid_use_of_visible_for_testing_member
262 _enableFocusManagerLifecycleAwarenessIfSupported();
263 }
264
265 void _enableFocusManagerLifecycleAwarenessIfSupported() {
266 if (buildOwner == null) {
267 return;
268 }
269 buildOwner!.focusManager
270 .listenToApplicationLifecycleChangesIfSupported(); // ignore: invalid_use_of_visible_for_testing_member
271 }
272
273 @override
274 TestRestorationManager createRestorationManager() {
275 return TestRestorationManager();
276 }
277
278 /// The value to set [debugPrint] to while tests are running.
279 ///
280 /// This can be used to redirect console output from the framework, or to
281 /// change the behavior of [debugPrint]. For example,
282 /// [AutomatedTestWidgetsFlutterBinding] uses it to make [debugPrint]
283 /// synchronous, disabling its normal throttling behavior.
284 ///
285 /// It is also used by some other parts of the test framework (e.g.
286 /// [WidgetTester.printToConsole]) to ensure that messages from the
287 /// test framework are displayed to the developer rather than logged
288 /// by whatever code is overriding [debugPrint].
289 DebugPrintCallback get debugPrintOverride => debugPrint;
290
291 /// The value to set [debugDisableShadows] to while tests are running.
292 ///
293 /// This can be used to reduce the likelihood of golden file tests being
294 /// flaky, because shadow rendering is not always deterministic. The
295 /// [AutomatedTestWidgetsFlutterBinding] sets this to true, so that all tests
296 /// always run with shadows disabled.
297 @protected
298 bool get disableShadows => false;
299
300 /// Determines whether the Dart [HttpClient] class should be overridden to
301 /// always return a failure response.
302 ///
303 /// By default, this value is true, so that unit tests will not become flaky
304 /// due to intermittent network errors. The value may be overridden by a
305 /// binding intended for use in integration tests that do end to end
306 /// application testing, including working with real network responses.
307 @protected
308 bool get overrideHttpClient => true;
309
310 /// Determines whether the binding automatically registers [testTextInput] as
311 /// a fake keyboard implementation.
312 ///
313 /// Unit tests make use of this to mock out text input communication for
314 /// widgets. An integration test would set this to false, to test real IME
315 /// or keyboard input.
316 ///
317 /// [TestTextInput.isRegistered] reports whether the text input mock is
318 /// registered or not.
319 ///
320 /// Some of the properties and methods on [testTextInput] are only valid if
321 /// [registerTestTextInput] returns true when a test starts. If those
322 /// members are accessed when using a binding that sets this flag to false,
323 /// they will throw.
324 ///
325 /// If this property returns true when a test ends, the [testTextInput] is
326 /// unregistered.
327 ///
328 /// This property should not change the value it returns during the lifetime
329 /// of the binding. Changing the value of this property risks very confusing
330 /// behavior as the [TestTextInput] may be inconsistently registered or
331 /// unregistered.
332 @protected
333 bool get registerTestTextInput => true;
334
335 /// Delay for `duration` of time.
336 ///
337 /// In the automated test environment ([AutomatedTestWidgetsFlutterBinding],
338 /// typically used in `flutter test`), this advances the fake [clock] for the
339 /// period.
340 ///
341 /// In the live test environment ([LiveTestWidgetsFlutterBinding], typically
342 /// used for `flutter run` and for [e2e](https://pub.dev/packages/e2e)), it is
343 /// equivalent to [Future.delayed].
344 Future<void> delayed(Duration duration);
345
346 /// The current [TestWidgetsFlutterBinding], if one has been created.
347 ///
348 /// Provides access to the features exposed by this binding. The binding must
349 /// be initialized before using this getter; this is typically done by calling
350 /// [testWidgets] or [TestWidgetsFlutterBinding.ensureInitialized].
351 static TestWidgetsFlutterBinding get instance => BindingBase.checkInstance(_instance);
352 static TestWidgetsFlutterBinding? _instance;
353
354 /// Creates and initializes the binding. This function is
355 /// idempotent; calling it a second time will just return the
356 /// previously-created instance.
357 ///
358 /// This function will use [AutomatedTestWidgetsFlutterBinding] if
359 /// the test was run using `flutter test`, and
360 /// [LiveTestWidgetsFlutterBinding] otherwise (e.g. if it was run
361 /// using `flutter run`). This is determined by looking at the
362 /// environment variables for a variable called `FLUTTER_TEST`.
363 ///
364 /// If `FLUTTER_TEST` is set with a value of 'true', then this test was
365 /// invoked by `flutter test`. If `FLUTTER_TEST` is not set, or if it is set
366 /// to 'false', then this test was invoked by `flutter run`.
367 ///
368 /// Browser environments do not currently support the
369 /// [LiveTestWidgetsFlutterBinding], so this function will always set up an
370 /// [AutomatedTestWidgetsFlutterBinding] when run in a web browser.
371 ///
372 /// The parameter `environment` is used to test the test framework
373 /// itself by checking how it reacts to different environment
374 /// variable values, and should not be used outside of this context.
375 ///
376 /// If a [TestWidgetsFlutterBinding] subclass was explicitly initialized
377 /// before calling [ensureInitialized], then that version of the binding is
378 /// returned regardless of the logic described above. This allows tests to
379 /// force a specific test binding to be used.
380 ///
381 /// This is called automatically by [testWidgets].
382 static TestWidgetsFlutterBinding ensureInitialized([
383 @visibleForTesting Map<String, String>? environment,
384 ]) {
385 return _instance ?? binding.ensureInitialized(environment);
386 }
387
388 @override
389 void initInstances() {
390 // This is initialized here because it's needed for the `super.initInstances`
391 // call. It can't be handled as a ctor initializer because it's dependent
392 // on `platformDispatcher`. It can't be handled in the ctor itself because
393 // the base class ctor is called first and calls `initInstances`.
394 window = TestWindow.fromPlatformDispatcher(platformDispatcher: platformDispatcher);
395
396 super.initInstances();
397 _instance = this;
398 timeDilation = 1.0; // just in case the developer has artificially changed it for development
399 if (overrideHttpClient) {
400 binding.setupHttpOverrides();
401 }
402 _testTextInput = TestTextInput(onCleared: _resetFocusedEditable);
403 }
404
405 @override
406 // ignore: must_call_super
407 void initLicenses() {
408 // Do not include any licenses, because we're a test, and the LICENSE file
409 // doesn't get generated for tests.
410 }
411
412 @override
413 bool debugCheckZone(String entryPoint) {
414 // We skip all the zone checks in tests because the test framework makes heavy use
415 // of zones and so the zones never quite match the way the framework expects.
416 return true;
417 }
418
419 /// Whether there is currently a test executing.
420 bool get inTest;
421
422 /// The number of outstanding microtasks in the queue.
423 int get microtaskCount;
424
425 /// The default test timeout for tests when using this binding.
426 ///
427 /// This controls the default for the `timeout` argument on [testWidgets]. It
428 /// is 10 minutes for [AutomatedTestWidgetsFlutterBinding] (tests running
429 /// using `flutter test`), and unlimited for tests using
430 /// [LiveTestWidgetsFlutterBinding] (tests running using `flutter run`).
431 test_package.Timeout get defaultTestTimeout;
432
433 /// The current time.
434 ///
435 /// In the automated test environment (`flutter test`), this is a fake clock
436 /// that begins in January 2015 at the start of the test and advances each
437 /// time [pump] is called with a non-zero duration.
438 ///
439 /// In the live testing environment (`flutter run`), this object shows the
440 /// actual current wall-clock time.
441 Clock get clock;
442
443 @override
444 SamplingClock? get debugSamplingClock => _TestSamplingClock(clock);
445
446 /// Triggers a frame sequence (build/layout/paint/etc),
447 /// then flushes microtasks.
448 ///
449 /// If duration is set, then advances the clock by that much first.
450 /// Doing this flushes microtasks.
451 ///
452 /// The supplied EnginePhase is the final phase reached during the pump pass;
453 /// if not supplied, the whole pass is executed.
454 ///
455 /// See also [LiveTestWidgetsFlutterBindingFramePolicy], which affects how
456 /// this method works when the test is run with `flutter run`.
457 Future<void> pump([Duration? duration, EnginePhase newPhase = EnginePhase.sendSemanticsUpdate]);
458
459 /// Runs a `callback` that performs real asynchronous work.
460 ///
461 /// This is intended for callers who need to call asynchronous methods where
462 /// the methods spawn isolates or OS threads and thus cannot be executed
463 /// synchronously by calling [pump].
464 ///
465 /// The `callback` must return a [Future] that completes to a value of type
466 /// `T`.
467 ///
468 /// If `callback` completes successfully, this will return the future
469 /// returned by `callback`.
470 ///
471 /// If `callback` completes with an error, the error will be caught by the
472 /// Flutter framework and made available via [takeException], and this method
473 /// will return a future that completes with `null`.
474 ///
475 /// Re-entrant calls to this method are not allowed; callers of this method
476 /// are required to wait for the returned future to complete before calling
477 /// this method again. Attempts to do otherwise will result in a
478 /// [TestFailure] error being thrown.
479 Future<T?> runAsync<T>(Future<T> Function() callback);
480
481 /// Artificially calls dispatchLocalesChanged on the Widget binding,
482 /// then flushes microtasks.
483 ///
484 /// Passes only one single Locale. Use [setLocales] to pass a full preferred
485 /// locales list.
486 Future<void> setLocale(String languageCode, String countryCode) {
487 return TestAsyncUtils.guard<void>(() async {
488 assert(inTest);
489 final Locale locale = Locale(languageCode, countryCode == '' ? null : countryCode);
490 dispatchLocalesChanged(<Locale>[locale]);
491 });
492 }
493
494 /// Artificially calls dispatchLocalesChanged on the Widget binding,
495 /// then flushes microtasks.
496 Future<void> setLocales(List<Locale> locales) {
497 return TestAsyncUtils.guard<void>(() async {
498 assert(inTest);
499 dispatchLocalesChanged(locales);
500 });
501 }
502
503 @override
504 Future<ui.AppExitResponse> exitApplication(ui.AppExitType exitType, [int exitCode = 0]) async {
505 switch (exitType) {
506 case ui.AppExitType.cancelable:
507 // The test framework shouldn't actually exit when requested.
508 return ui.AppExitResponse.cancel;
509 case ui.AppExitType.required:
510 throw FlutterError('Unexpected application exit request while running test');
511 }
512 }
513
514 /// Re-attempts the initialization of the lifecycle state after providing
515 /// test values in [TestPlatformDispatcher.initialLifecycleStateTestValue].
516 void readTestInitialLifecycleStateFromNativeWindow() {
517 readInitialLifecycleStateFromNativeWindow();
518 }
519
520 Size? _surfaceSize;
521
522 /// Artificially changes the logical size of [WidgetTester.view] to the
523 /// specified size, then flushes microtasks.
524 ///
525 /// Set to null to use the default surface size.
526 ///
527 /// To avoid affecting other tests by leaking state, a test that
528 /// uses this method should always reset the surface size to the default.
529 /// For example, using `addTearDown`:
530 /// ```dart
531 /// await binding.setSurfaceSize(someSize);
532 /// addTearDown(() => binding.setSurfaceSize(null));
533 /// ```
534 ///
535 /// This method only affects the size of the [WidgetTester.view]. It does not
536 /// affect the size of any other views. Instead of this method, consider
537 /// setting [TestFlutterView.physicalSize], which works for any view,
538 /// including [WidgetTester.view].
539 // TODO(pdblasi-google): Deprecate this. https://github.com/flutter/flutter/issues/123881
540 Future<void> setSurfaceSize(Size? size) {
541 return TestAsyncUtils.guard<void>(() async {
542 assert(inTest);
543 if (_surfaceSize == size) {
544 return;
545 }
546 _surfaceSize = size;
547 handleMetricsChanged();
548 });
549 }
550
551 @override
552 void addRenderView(RenderView view) {
553 _insideAddRenderView = true;
554 try {
555 super.addRenderView(view);
556 } finally {
557 _insideAddRenderView = false;
558 }
559 }
560
561 bool _insideAddRenderView = false;
562
563 @override
564 ViewConfiguration createViewConfigurationFor(RenderView renderView) {
565 if (_insideAddRenderView &&
566 renderView.hasConfiguration &&
567 renderView.configuration is TestViewConfiguration &&
568 renderView == this.renderView) {
569 // If a test has reached out to the now deprecated renderView property to set a custom TestViewConfiguration
570 // we are not replacing it. This is to maintain backwards compatibility with how things worked prior to the
571 // deprecation of that property.
572 // TODO(goderbauer): Remove this "if" when the deprecated renderView property is removed.
573 return renderView.configuration;
574 }
575 final FlutterView view = renderView.flutterView;
576 if (_surfaceSize != null && view == platformDispatcher.implicitView) {
577 final BoxConstraints constraints = BoxConstraints.tight(_surfaceSize!);
578 return ViewConfiguration(
579 logicalConstraints: constraints,
580 physicalConstraints: constraints * view.devicePixelRatio,
581 devicePixelRatio: view.devicePixelRatio,
582 );
583 }
584 return super.createViewConfigurationFor(renderView);
585 }
586
587 /// Acts as if the application went idle.
588 ///
589 /// Runs all remaining microtasks, including those scheduled as a result of
590 /// running them, until there are no more microtasks scheduled. Then, runs any
591 /// previously scheduled timers with zero time, and completes the returned future.
592 ///
593 /// May result in an infinite loop or run out of memory if microtasks continue
594 /// to recursively schedule new microtasks. Will not run any timers scheduled
595 /// after this method was invoked, even if they are zero-time timers.
596 Future<void> idle() {
597 return TestAsyncUtils.guard<void>(() {
598 final Completer<void> completer = Completer<void>();
599 Timer.run(() {
600 completer.complete();
601 });
602 return completer.future;
603 });
604 }
605
606 /// Convert the given point from the global coordinate space of the provided
607 /// [RenderView] to its local one.
608 ///
609 /// This method operates in logical pixels for both coordinate spaces. It does
610 /// not apply the device pixel ratio (used to translate to/from physical
611 /// pixels).
612 ///
613 /// For definitions for coordinate spaces, see [TestWidgetsFlutterBinding].
614 Offset globalToLocal(Offset point, RenderView view) => point;
615
616 /// Convert the given point from the local coordinate space to the global
617 /// coordinate space of the [RenderView].
618 ///
619 /// This method operates in logical pixels for both coordinate spaces. It does
620 /// not apply the device pixel ratio to translate to physical pixels.
621 ///
622 /// For definitions for coordinate spaces, see [TestWidgetsFlutterBinding].
623 Offset localToGlobal(Offset point, RenderView view) => point;
624
625 /// The source of the current pointer event.
626 ///
627 /// The [pointerEventSource] is set as the `source` parameter of
628 /// [handlePointerEventForSource] and can be used in the immediate enclosing
629 /// [dispatchEvent].
630 ///
631 /// When [handlePointerEvent] is called directly, [pointerEventSource]
632 /// is [TestBindingEventSource.device].
633 ///
634 /// This means that pointer events triggered by the [WidgetController] (e.g.
635 /// via [WidgetController.tap]) will result in actual interactions with the
636 /// UI, but other pointer events such as those from physical taps will be
637 /// dropped. See also [shouldPropagateDevicePointerEvents] if this is
638 /// undesired.
639 TestBindingEventSource get pointerEventSource => _pointerEventSource;
640 TestBindingEventSource _pointerEventSource = TestBindingEventSource.device;
641
642 /// Whether pointer events from [TestBindingEventSource.device] will be
643 /// propagated to the framework, or dropped.
644 ///
645 /// Setting this can be useful to interact with the app in some other way
646 /// besides through the [WidgetController], such as with `adb shell input tap`
647 /// on Android.
648 ///
649 /// See also [pointerEventSource].
650 bool shouldPropagateDevicePointerEvents = false;
651
652 /// Dispatch an event to the targets found by a hit test on its position,
653 /// and remember its source as [pointerEventSource].
654 ///
655 /// This method sets [pointerEventSource] to `source`, forwards the call to
656 /// [handlePointerEvent], then resets [pointerEventSource] to the previous
657 /// value.
658 ///
659 /// If `source` is [TestBindingEventSource.device], then the `event` is based
660 /// in the global coordinate space (for definitions for coordinate spaces,
661 /// see [TestWidgetsFlutterBinding]) and the event is likely triggered by the
662 /// user physically interacting with the screen during a live test on a real
663 /// device (see [LiveTestWidgetsFlutterBinding]).
664 ///
665 /// If `source` is [TestBindingEventSource.test], then the `event` is based
666 /// in the local coordinate space and the event is likely triggered by
667 /// programmatically simulated pointer events, such as:
668 ///
669 /// * [WidgetController.tap] and alike methods, as well as directly using
670 /// [TestGesture]. They are usually used in
671 /// [AutomatedTestWidgetsFlutterBinding] but sometimes in live tests too.
672 /// * [WidgetController.timedDrag] and alike methods. They are usually used
673 /// in macrobenchmarks.
674 void handlePointerEventForSource(
675 PointerEvent event, {
676 TestBindingEventSource source = TestBindingEventSource.device,
677 }) {
678 withPointerEventSource(source, () => handlePointerEvent(event));
679 }
680
681 /// Sets [pointerEventSource] to `source`, runs `task`, then resets `source`
682 /// to the previous value.
683 @protected
684 void withPointerEventSource(TestBindingEventSource source, VoidCallback task) {
685 final TestBindingEventSource previousSource = _pointerEventSource;
686 _pointerEventSource = source;
687 try {
688 task();
689 } finally {
690 _pointerEventSource = previousSource;
691 }
692 }
693
694 /// A stub for the system's onscreen keyboard. Callers must set the
695 /// [focusedEditable] before using this value.
696 TestTextInput get testTextInput => _testTextInput;
697 late TestTextInput _testTextInput;
698
699 /// The [State] of the current [EditableText] client of the onscreen keyboard.
700 ///
701 /// Setting this property to a new value causes the given [EditableTextState]
702 /// to focus itself and request the keyboard to establish a
703 /// [TextInputConnection].
704 ///
705 /// Callers must pump an additional frame after setting this property to
706 /// complete the focus change.
707 ///
708 /// Instead of setting this directly, consider using
709 /// [WidgetTester.showKeyboard].
710 //
711 // TODO(ianh): We should just remove this property and move the call to
712 // requestKeyboard to the WidgetTester.showKeyboard method.
713 EditableTextState? get focusedEditable => _focusedEditable;
714 EditableTextState? _focusedEditable;
715 set focusedEditable(EditableTextState? value) {
716 if (_focusedEditable != value) {
717 _focusedEditable = value;
718 value?.requestKeyboard();
719 }
720 }
721
722 void _resetFocusedEditable() {
723 _focusedEditable = null;
724 }
725
726 /// Returns the exception most recently caught by the Flutter framework.
727 ///
728 /// Call this if you expect an exception during a test. If an exception is
729 /// thrown and this is not called, then the exception is rethrown when
730 /// the [testWidgets] call completes.
731 ///
732 /// If two exceptions are thrown in a row without the first one being
733 /// acknowledged with a call to this method, then when the second exception is
734 /// thrown, they are both dumped to the console and then the second is
735 /// rethrown from the exception handler. This will likely result in the
736 /// framework entering a highly unstable state and everything collapsing.
737 ///
738 /// It's safe to call this when there's no pending exception; it will return
739 /// null in that case.
740 dynamic takeException() {
741 assert(inTest);
742 final dynamic result = _pendingExceptionDetails?.exception;
743 _pendingExceptionDetails = null;
744 return result;
745 }
746
747 FlutterExceptionHandler? _oldExceptionHandler;
748 late StackTraceDemangler _oldStackTraceDemangler;
749 FlutterErrorDetails? _pendingExceptionDetails;
750
751 _MockMessageHandler? _announcementHandler;
752 List<CapturedAccessibilityAnnouncement> _announcements = <CapturedAccessibilityAnnouncement>[];
753
754 /// {@template flutter.flutter_test.TakeAccessibilityAnnouncements}
755 /// Returns a list of all the accessibility announcements made by the Flutter
756 /// framework since the last time this function was called.
757 ///
758 /// It's safe to call this when there hasn't been any announcements; it will return
759 /// an empty list in that case.
760 /// {@endtemplate}
761 List<CapturedAccessibilityAnnouncement> takeAnnouncements() {
762 assert(inTest);
763 final List<CapturedAccessibilityAnnouncement> announcements = _announcements;
764 _announcements = <CapturedAccessibilityAnnouncement>[];
765 return announcements;
766 }
767
768 static const TextStyle _messageStyle = TextStyle(color: Color(0xFF917FFF), fontSize: 40.0);
769
770 static const Widget _preTestMessage = Center(
771 child: Text('Test starting...', style: _messageStyle, textDirection: TextDirection.ltr),
772 );
773
774 static const Widget _postTestMessage = Center(
775 child: Text('Test finished.', style: _messageStyle, textDirection: TextDirection.ltr),
776 );
777
778 /// Whether to include the output of debugDumpApp() when reporting
779 /// test failures.
780 bool showAppDumpInErrors = false;
781
782 /// Call the testBody inside a [FakeAsync] scope on which [pump] can
783 /// advance time.
784 ///
785 /// Returns a future which completes when the test has run.
786 ///
787 /// Called by the [testWidgets] and [benchmarkWidgets] functions to
788 /// run a test.
789 ///
790 /// The `invariantTester` argument is called after the `testBody`'s [Future]
791 /// completes. If it throws, then the test is marked as failed.
792 ///
793 /// The `description` is used by the [LiveTestWidgetsFlutterBinding] to
794 /// show a label on the screen during the test. The description comes from
795 /// the value passed to [testWidgets].
796 Future<void> runTest(
797 Future<void> Function() testBody,
798 VoidCallback invariantTester, {
799 String description = '',
800 });
801
802 /// This is called during test execution before and after the body has been
803 /// executed.
804 ///
805 /// It's used by [AutomatedTestWidgetsFlutterBinding] to drain the microtasks
806 /// before the final [pump] that happens during test cleanup.
807 void asyncBarrier() {
808 TestAsyncUtils.verifyAllScopesClosed();
809 }
810
811 Zone? _parentZone;
812
813 VoidCallback _createTestCompletionHandler(String testDescription, Completer<void> completer) {
814 return () {
815 // This can get called twice, in the case of a Future without listeners failing, and then
816 // our main future completing.
817 assert(Zone.current == _parentZone);
818 if (_pendingExceptionDetails != null) {
819 debugPrint =
820 debugPrintOverride; // just in case the test overrides it -- otherwise we won't see the error!
821 reportTestException(_pendingExceptionDetails!, testDescription);
822 _pendingExceptionDetails = null;
823 }
824 if (!completer.isCompleted) {
825 completer.complete();
826 }
827 };
828 }
829
830 /// Called when the framework catches an exception, even if that exception is
831 /// being handled by [takeException].
832 ///
833 /// This is called when there is no pending exception; if multiple exceptions
834 /// are thrown and [takeException] isn't used, then subsequent exceptions are
835 /// logged to the console regardless (and the test will fail).
836 @protected
837 void reportExceptionNoticed(FlutterErrorDetails exception) {
838 // By default we do nothing.
839 // The LiveTestWidgetsFlutterBinding overrides this to report the exception to the console.
840 }
841
842 Future<void> _handleAnnouncementMessage(Object? mockMessage) async {
843 if (mockMessage! case {
844 'type': 'announce',
845 'data': final Map<Object?, Object?> data as Map<Object?, Object?>,
846 }) {
847 _announcements.add(
848 CapturedAccessibilityAnnouncement._(
849 data['message'].toString(),
850 TextDirection.values[data['textDirection']! as int],
851 Assertiveness.values[(data['assertiveness'] ?? 0) as int],
852 ),
853 );
854 }
855 }
856
857 Future<void> _runTest(
858 Future<void> Function() testBody,
859 VoidCallback invariantTester,
860 String description,
861 ) {
862 assert(inTest);
863
864 // Set the handler only if there is currently none.
865 if (TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.checkMockMessageHandler(
866 SystemChannels.accessibility.name,
867 null,
868 )) {
869 _announcementHandler = _handleAnnouncementMessage;
870 TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
871 .setMockDecodedMessageHandler<dynamic>(
872 SystemChannels.accessibility,
873 _announcementHandler,
874 );
875 }
876
877 _oldExceptionHandler = FlutterError.onError;
878 _oldStackTraceDemangler = FlutterError.demangleStackTrace;
879 int exceptionCount = 0; // number of un-taken exceptions
880 FlutterError.onError = (FlutterErrorDetails details) {
881 if (_pendingExceptionDetails != null) {
882 debugPrint =
883 debugPrintOverride; // just in case the test overrides it -- otherwise we won't see the errors!
884 if (exceptionCount == 0) {
885 exceptionCount = 2;
886 FlutterError.dumpErrorToConsole(_pendingExceptionDetails!, forceReport: true);
887 } else {
888 exceptionCount += 1;
889 }
890 FlutterError.dumpErrorToConsole(details, forceReport: true);
891 _pendingExceptionDetails = FlutterErrorDetails(
892 exception:
893 'Multiple exceptions ($exceptionCount) were detected during the running of the current test, and at least one was unexpected.',
894 library: 'Flutter test framework',
895 );
896 } else {
897 reportExceptionNoticed(
898 details,
899 ); // mostly this is just a hook for the LiveTestWidgetsFlutterBinding
900 _pendingExceptionDetails = details;
901 }
902 };
903 FlutterError.demangleStackTrace = (StackTrace stack) {
904 // package:stack_trace uses ZoneSpecification.errorCallback to add useful
905 // information to stack traces, meaning Trace and Chain classes can be
906 // present. Because these StackTrace implementations do not follow the
907 // format the framework expects, we convert them to a vm trace here.
908 if (stack is stack_trace.Trace) {
909 return stack.vmTrace;
910 }
911 if (stack is stack_trace.Chain) {
912 return stack.toTrace().vmTrace;
913 }
914 return stack;
915 };
916 final Completer<void> testCompleter = Completer<void>();
917 final VoidCallback testCompletionHandler = _createTestCompletionHandler(
918 description,
919 testCompleter,
920 );
921 void handleUncaughtError(Object exception, StackTrace stack) {
922 if (testCompleter.isCompleted) {
923 // Well this is not a good sign.
924 // Ideally, once the test has failed we would stop getting errors from the test.
925 // However, if someone tries hard enough they could get in a state where this happens.
926 // If we silently dropped these errors on the ground, nobody would ever know. So instead
927 // we raise them and fail the test after it has already completed.
928 debugPrint =
929 debugPrintOverride; // just in case the test overrides it -- otherwise we won't see the error!
930 reportTestException(
931 FlutterErrorDetails(
932 exception: exception,
933 stack: stack,
934 context: ErrorDescription('running a test (but after the test had completed)'),
935 library: 'Flutter test framework',
936 ),
937 description,
938 );
939 return;
940 }
941 // This is where test failures, e.g. those in expect(), will end up.
942 // Specifically, runUnaryGuarded() will call this synchronously and
943 // return our return value if _runTestBody fails synchronously (which it
944 // won't, so this never happens), and Future will call this when the
945 // Future completes with an error and it would otherwise call listeners
946 // if the listener is in a different zone (which it would be for the
947 // `whenComplete` handler below), or if the Future completes with an
948 // error and the future has no listeners at all.
949 //
950 // This handler further calls the onError handler above, which sets
951 // _pendingExceptionDetails. Nothing gets printed as a result of that
952 // call unless we already had an exception pending, because in general
953 // we want people to be able to cause the framework to report exceptions
954 // and then use takeException to verify that they were really caught.
955 // Now, if we actually get here, this isn't going to be one of those
956 // cases. We only get here if the test has actually failed. So, once
957 // we've carefully reported it, we then immediately end the test by
958 // calling the testCompletionHandler in the _parentZone.
959 //
960 // We have to manually call testCompletionHandler because if the Future
961 // library calls us, it is maybe _instead_ of calling a registered
962 // listener from a different zone. In our case, that would be instead of
963 // calling the whenComplete() listener below.
964 //
965 // We have to call it in the parent zone because if we called it in
966 // _this_ zone, the test framework would find this zone was the current
967 // zone and helpfully throw the error in this zone, causing us to be
968 // directly called again.
969 DiagnosticsNode treeDump;
970 try {
971 treeDump = rootElement?.toDiagnosticsNode() ?? DiagnosticsNode.message('<no tree>');
972 // We try to stringify the tree dump here (though we immediately discard the result) because
973 // we want to make sure that if it can't be serialized, we replace it with a message that
974 // says the tree could not be serialized. Otherwise, the real exception might get obscured
975 // by side-effects of the underlying issues causing the tree dumping code to flail.
976 treeDump.toStringDeep();
977 } catch (exception) {
978 treeDump = DiagnosticsNode.message(
979 '<additional error caught while dumping tree: $exception>',
980 level: DiagnosticLevel.error,
981 );
982 }
983 final List<DiagnosticsNode> omittedFrames = <DiagnosticsNode>[];
984 final int stackLinesToOmit = reportExpectCall(stack, omittedFrames);
985 FlutterError.reportError(
986 FlutterErrorDetails(
987 exception: exception,
988 stack: stack,
989 context: ErrorDescription('running a test'),
990 library: 'Flutter test framework',
991 stackFilter: (Iterable<String> frames) {
992 return FlutterError.defaultStackFilter(frames.skip(stackLinesToOmit));
993 },
994 informationCollector: () sync* {
995 if (stackLinesToOmit > 0) {
996 yield* omittedFrames;
997 }
998 if (showAppDumpInErrors) {
999 yield DiagnosticsProperty<DiagnosticsNode>(
1000 'At the time of the failure, the widget tree looked as follows',
1001 treeDump,
1002 linePrefix: '# ',
1003 style: DiagnosticsTreeStyle.flat,
1004 );
1005 }
1006 if (description.isNotEmpty) {
1007 yield DiagnosticsProperty<String>(
1008 'The test description was',
1009 description,
1010 style: DiagnosticsTreeStyle.errorProperty,
1011 );
1012 }
1013 },
1014 ),
1015 );
1016 assert(_parentZone != null);
1017 assert(
1018 _pendingExceptionDetails != null,
1019 'A test overrode FlutterError.onError but either failed to return it to its original state, or had unexpected additional errors that it could not handle. Typically, this is caused by using expect() before restoring FlutterError.onError.',
1020 );
1021 _parentZone!.run<void>(testCompletionHandler);
1022 }
1023
1024 final ZoneSpecification errorHandlingZoneSpecification = ZoneSpecification(
1025 handleUncaughtError: (
1026 Zone self,
1027 ZoneDelegate parent,
1028 Zone zone,
1029 Object exception,
1030 StackTrace stack,
1031 ) {
1032 handleUncaughtError(exception, stack);
1033 },
1034 );
1035 _parentZone = Zone.current;
1036 final Zone testZone = _parentZone!.fork(specification: errorHandlingZoneSpecification);
1037 testZone
1038 .runBinary<Future<void>, Future<void> Function(), VoidCallback>(
1039 _runTestBody,
1040 testBody,
1041 invariantTester,
1042 )
1043 .whenComplete(testCompletionHandler);
1044 return testCompleter.future;
1045 }
1046
1047 Future<void> _runTestBody(Future<void> Function() testBody, VoidCallback invariantTester) async {
1048 assert(inTest);
1049 // So that we can assert that it remains the same after the test finishes.
1050 _beforeTestCheckIntrinsicSizes = debugCheckIntrinsicSizes;
1051
1052 runApp(Container(key: UniqueKey(), child: _preTestMessage)); // Reset the tree to a known state.
1053 await pump();
1054 // Pretend that the first frame produced in the test body is the first frame
1055 // sent to the engine.
1056 resetFirstFrameSent();
1057
1058 final bool autoUpdateGoldensBeforeTest = autoUpdateGoldenFiles && !isBrowser;
1059 final TestExceptionReporter reportTestExceptionBeforeTest = reportTestException;
1060 final ErrorWidgetBuilder errorWidgetBuilderBeforeTest = ErrorWidget.builder;
1061 final bool shouldPropagateDevicePointerEventsBeforeTest = shouldPropagateDevicePointerEvents;
1062
1063 // run the test
1064 await testBody();
1065 asyncBarrier(); // drains the microtasks in `flutter test` mode (when using AutomatedTestWidgetsFlutterBinding)
1066
1067 if (_pendingExceptionDetails == null) {
1068 // We only try to clean up and verify invariants if we didn't already
1069 // fail. If we got an exception already, then we instead leave everything
1070 // alone so that we don't cause more spurious errors.
1071 runApp(
1072 Container(key: UniqueKey(), child: _postTestMessage),
1073 ); // Unmount any remaining widgets.
1074 await pump();
1075 if (registerTestTextInput) {
1076 _testTextInput.unregister();
1077 }
1078 invariantTester();
1079 _verifyAutoUpdateGoldensUnset(autoUpdateGoldensBeforeTest && !isBrowser);
1080 _verifyReportTestExceptionUnset(reportTestExceptionBeforeTest);
1081 _verifyErrorWidgetBuilderUnset(errorWidgetBuilderBeforeTest);
1082 _verifyShouldPropagateDevicePointerEventsUnset(shouldPropagateDevicePointerEventsBeforeTest);
1083 _verifyInvariants();
1084 }
1085
1086 assert(inTest);
1087 asyncBarrier(); // When using AutomatedTestWidgetsFlutterBinding, this flushes the microtasks.
1088 }
1089
1090 late bool _beforeTestCheckIntrinsicSizes;
1091
1092 void _verifyInvariants() {
1093 assert(
1094 debugAssertNoTransientCallbacks(
1095 'An animation is still running even after the widget tree was disposed.',
1096 ),
1097 );
1098 assert(
1099 debugAssertNoPendingPerformanceModeRequests(
1100 'A performance mode was requested and not disposed by a test.',
1101 ),
1102 );
1103 assert(debugAssertNoTimeDilation('The timeDilation was changed and not reset by the test.'));
1104 assert(
1105 debugAssertAllFoundationVarsUnset(
1106 'The value of a foundation debug variable was changed by the test.',
1107 debugPrintOverride: debugPrintOverride,
1108 ),
1109 );
1110 assert(
1111 debugAssertAllGesturesVarsUnset(
1112 'The value of a gestures debug variable was changed by the test.',
1113 ),
1114 );
1115 assert(
1116 debugAssertAllPaintingVarsUnset(
1117 'The value of a painting debug variable was changed by the test.',
1118 debugDisableShadowsOverride: disableShadows,
1119 ),
1120 );
1121 assert(
1122 debugAssertAllRenderVarsUnset(
1123 'The value of a rendering debug variable was changed by the test.',
1124 debugCheckIntrinsicSizesOverride: _beforeTestCheckIntrinsicSizes,
1125 ),
1126 );
1127 assert(
1128 debugAssertAllWidgetVarsUnset(
1129 'The value of a widget debug variable was changed by the test.',
1130 ),
1131 );
1132 assert(
1133 debugAssertAllSchedulerVarsUnset(
1134 'The value of a scheduler debug variable was changed by the test.',
1135 ),
1136 );
1137 assert(
1138 debugAssertAllServicesVarsUnset(
1139 'The value of a services debug variable was changed by the test.',
1140 ),
1141 );
1142 }
1143
1144 void _verifyAutoUpdateGoldensUnset(bool valueBeforeTest) {
1145 assert(() {
1146 if (autoUpdateGoldenFiles != valueBeforeTest) {
1147 FlutterError.reportError(
1148 FlutterErrorDetails(
1149 exception: FlutterError('The value of autoUpdateGoldenFiles was changed by the test.'),
1150 stack: StackTrace.current,
1151 library: 'Flutter test framework',
1152 ),
1153 );
1154 }
1155 return true;
1156 }());
1157 }
1158
1159 void _verifyReportTestExceptionUnset(TestExceptionReporter valueBeforeTest) {
1160 assert(() {
1161 if (reportTestException != valueBeforeTest) {
1162 // We can't report this error to their modified reporter because we
1163 // can't be guaranteed that their reporter will cause the test to fail.
1164 // So we reset the error reporter to its initial value and then report
1165 // this error.
1166 reportTestException = valueBeforeTest;
1167 FlutterError.reportError(
1168 FlutterErrorDetails(
1169 exception: FlutterError('The value of reportTestException was changed by the test.'),
1170 stack: StackTrace.current,
1171 library: 'Flutter test framework',
1172 ),
1173 );
1174 }
1175 return true;
1176 }());
1177 }
1178
1179 void _verifyErrorWidgetBuilderUnset(ErrorWidgetBuilder valueBeforeTest) {
1180 assert(() {
1181 if (ErrorWidget.builder != valueBeforeTest) {
1182 FlutterError.reportError(
1183 FlutterErrorDetails(
1184 exception: FlutterError('The value of ErrorWidget.builder was changed by the test.'),
1185 stack: StackTrace.current,
1186 library: 'Flutter test framework',
1187 ),
1188 );
1189 }
1190 return true;
1191 }());
1192 }
1193
1194 void _verifyShouldPropagateDevicePointerEventsUnset(bool valueBeforeTest) {
1195 assert(() {
1196 if (shouldPropagateDevicePointerEvents != valueBeforeTest) {
1197 FlutterError.reportError(
1198 FlutterErrorDetails(
1199 exception: FlutterError(
1200 'The value of shouldPropagateDevicePointerEvents was changed by the test.',
1201 ),
1202 stack: StackTrace.current,
1203 library: 'Flutter test framework',
1204 ),
1205 );
1206 }
1207 return true;
1208 }());
1209 }
1210
1211 /// Called by the [testWidgets] function after a test is executed.
1212 void postTest() {
1213 assert(inTest);
1214 FlutterError.onError = _oldExceptionHandler;
1215 FlutterError.demangleStackTrace = _oldStackTraceDemangler;
1216 _pendingExceptionDetails = null;
1217 _parentZone = null;
1218 buildOwner!.focusManager.dispose();
1219
1220 if (TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.checkMockMessageHandler(
1221 SystemChannels.accessibility.name,
1222 _announcementHandler,
1223 )) {
1224 TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
1225 .setMockDecodedMessageHandler(SystemChannels.accessibility, null);
1226 _announcementHandler = null;
1227 }
1228 _announcements = <CapturedAccessibilityAnnouncement>[];
1229
1230 ServicesBinding.instance.keyEventManager.keyMessageHandler = null;
1231 buildOwner!.focusManager = FocusManager()..registerGlobalHandlers();
1232
1233 // Disabling the warning because @visibleForTesting doesn't take the testing
1234 // framework itself into account, but we don't want it visible outside of
1235 // tests.
1236 // ignore: invalid_use_of_visible_for_testing_member
1237 RawKeyboard.instance.clearKeysPressed();
1238 // ignore: invalid_use_of_visible_for_testing_member
1239 HardwareKeyboard.instance.clearState();
1240 // ignore: invalid_use_of_visible_for_testing_member
1241 keyEventManager.clearState();
1242 // ignore: invalid_use_of_visible_for_testing_member
1243 RendererBinding.instance.initMouseTracker();
1244
1245 assert(ServicesBinding.instance == WidgetsBinding.instance);
1246 // ignore: invalid_use_of_visible_for_testing_member
1247 ServicesBinding.instance.resetInternalState();
1248 }
1249}
1250
1251/// A variant of [TestWidgetsFlutterBinding] for executing tests typically
1252/// the `flutter test` environment, unless it is an integration test.
1253///
1254/// When doing integration test, [LiveTestWidgetsFlutterBinding] is utilized
1255/// instead.
1256///
1257/// This binding controls time, allowing tests to verify long
1258/// animation sequences without having to execute them in real time.
1259///
1260/// This class assumes it is always run in debug mode (since tests are always
1261/// run in debug mode).
1262///
1263/// See [TestWidgetsFlutterBinding] for a list of mixins that must be
1264/// provided by the binding active while the test framework is
1265/// running.
1266class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
1267 @override
1268 void initInstances() {
1269 super.initInstances();
1270 _instance = this;
1271 binding.mockFlutterAssets();
1272 }
1273
1274 /// The current [AutomatedTestWidgetsFlutterBinding], if one has been created.
1275 ///
1276 /// The binding must be initialized before using this getter. If you
1277 /// need the binding to be constructed before calling [testWidgets],
1278 /// you can ensure a binding has been constructed by calling the
1279 /// [TestWidgetsFlutterBinding.ensureInitialized] function.
1280 static AutomatedTestWidgetsFlutterBinding get instance => BindingBase.checkInstance(_instance);
1281 static AutomatedTestWidgetsFlutterBinding? _instance;
1282
1283 /// Returns an instance of the binding that implements
1284 /// [AutomatedTestWidgetsFlutterBinding]. If no binding has yet been
1285 /// initialized, the a new instance is created.
1286 ///
1287 /// Generally, there is no need to call this method. Use
1288 /// [TestWidgetsFlutterBinding.ensureInitialized] instead, as it
1289 /// will select the correct test binding implementation
1290 /// automatically.
1291 static AutomatedTestWidgetsFlutterBinding ensureInitialized() {
1292 if (AutomatedTestWidgetsFlutterBinding._instance == null) {
1293 AutomatedTestWidgetsFlutterBinding();
1294 }
1295 return AutomatedTestWidgetsFlutterBinding.instance;
1296 }
1297
1298 FakeAsync? _currentFakeAsync; // set in runTest; cleared in postTest
1299 Completer<void>? _pendingAsyncTasks;
1300
1301 @override
1302 Clock get clock {
1303 assert(inTest);
1304 return _clock!;
1305 }
1306
1307 Clock? _clock;
1308
1309 @override
1310 DebugPrintCallback get debugPrintOverride => debugPrintSynchronously;
1311
1312 @override
1313 bool get disableShadows => true;
1314
1315 /// The value of [defaultTestTimeout] can be set to `None` to enable debugging
1316 /// flutter tests where we would not want to timeout the test. This is
1317 /// expected to be used by test tooling which can detect debug mode.
1318 @override
1319 test_package.Timeout defaultTestTimeout = const test_package.Timeout(Duration(minutes: 10));
1320
1321 @override
1322 bool get inTest => _currentFakeAsync != null;
1323
1324 @override
1325 int get microtaskCount => _currentFakeAsync!.microtaskCount;
1326
1327 @override
1328 Future<void> pump([Duration? duration, EnginePhase newPhase = EnginePhase.sendSemanticsUpdate]) {
1329 return TestAsyncUtils.guard<void>(() {
1330 assert(inTest);
1331 assert(_clock != null);
1332 if (duration != null) {
1333 _currentFakeAsync!.elapse(duration);
1334 }
1335 _phase = newPhase;
1336 if (hasScheduledFrame) {
1337 _currentFakeAsync!.flushMicrotasks();
1338 handleBeginFrame(Duration(microseconds: _clock!.now().microsecondsSinceEpoch));
1339 _currentFakeAsync!.flushMicrotasks();
1340 handleDrawFrame();
1341 }
1342 _currentFakeAsync!.flushMicrotasks();
1343 return Future<void>.value();
1344 });
1345 }
1346
1347 @override
1348 Future<T?> runAsync<T>(Future<T> Function() callback) {
1349 assert(() {
1350 if (_pendingAsyncTasks == null) {
1351 return true;
1352 }
1353 fail(
1354 'Reentrant call to runAsync() denied.\n'
1355 'runAsync() was called, then before its future completed, it '
1356 'was called again. You must wait for the first returned future '
1357 'to complete before calling runAsync() again.',
1358 );
1359 }());
1360
1361 final Zone realAsyncZone = Zone.current.fork(
1362 specification: ZoneSpecification(
1363 scheduleMicrotask: (Zone self, ZoneDelegate parent, Zone zone, void Function() f) {
1364 _rootDelegate.scheduleMicrotask(zone, f);
1365 },
1366 createTimer: (
1367 Zone self,
1368 ZoneDelegate parent,
1369 Zone zone,
1370 Duration duration,
1371 void Function() f,
1372 ) {
1373 return _rootDelegate.createTimer(zone, duration, f);
1374 },
1375 createPeriodicTimer: (
1376 Zone self,
1377 ZoneDelegate parent,
1378 Zone zone,
1379 Duration period,
1380 void Function(Timer timer) f,
1381 ) {
1382 return _rootDelegate.createPeriodicTimer(zone, period, f);
1383 },
1384 ),
1385 );
1386
1387 return realAsyncZone.run<Future<T?>>(() {
1388 final Completer<T?> result = Completer<T?>();
1389 _pendingAsyncTasks = Completer<void>();
1390 try {
1391 callback().then(result.complete).catchError((Object exception, StackTrace stack) {
1392 FlutterError.reportError(
1393 FlutterErrorDetails(
1394 exception: exception,
1395 stack: stack,
1396 library: 'Flutter test framework',
1397 context: ErrorDescription('while running async test code'),
1398 informationCollector: () {
1399 return <DiagnosticsNode>[ErrorHint('The exception was caught asynchronously.')];
1400 },
1401 ),
1402 );
1403 result.complete(null);
1404 });
1405 } catch (exception, stack) {
1406 FlutterError.reportError(
1407 FlutterErrorDetails(
1408 exception: exception,
1409 stack: stack,
1410 library: 'Flutter test framework',
1411 context: ErrorDescription('while running async test code'),
1412 informationCollector: () {
1413 return <DiagnosticsNode>[ErrorHint('The exception was caught synchronously.')];
1414 },
1415 ),
1416 );
1417 result.complete(null);
1418 }
1419 result.future.whenComplete(() {
1420 _pendingAsyncTasks!.complete();
1421 _pendingAsyncTasks = null;
1422 });
1423 return result.future;
1424 });
1425 }
1426
1427 @override
1428 void ensureFrameCallbacksRegistered() {
1429 // Leave PlatformDispatcher alone, do nothing.
1430 assert(platformDispatcher.onDrawFrame == null);
1431 assert(platformDispatcher.onBeginFrame == null);
1432 }
1433
1434 @override
1435 void scheduleWarmUpFrame() {
1436 // We override the default version of this so that the application-startup warm-up frame
1437 // does not schedule timers which we might never get around to running.
1438 assert(inTest);
1439 handleBeginFrame(null);
1440 _currentFakeAsync!.flushMicrotasks();
1441 handleDrawFrame();
1442 _currentFakeAsync!.flushMicrotasks();
1443 }
1444
1445 /// The [ZoneDelegate] for [Zone.root].
1446 ///
1447 /// Used to schedule (real) microtasks and timers in the root zone,
1448 /// to be run in the correct zone.
1449 static final ZoneDelegate _rootDelegate = _captureRootZoneDelegate();
1450
1451 /// Hack to extract the [ZoneDelegate] for [Zone.root].
1452 static ZoneDelegate _captureRootZoneDelegate() {
1453 final Zone captureZone = Zone.root.fork(
1454 specification: ZoneSpecification(
1455 run: <R>(Zone self, ZoneDelegate parent, Zone zone, R Function() f) {
1456 return parent as R;
1457 },
1458 ),
1459 );
1460 // The `_captureRootZoneDelegate` argument just happens to be a constant
1461 // function with the necessary type. It's not called recursively.
1462 return captureZone.run<ZoneDelegate>(_captureRootZoneDelegate);
1463 }
1464
1465 @override
1466 void scheduleAttachRootWidget(Widget rootWidget) {
1467 // We override the default version of this so that the application-startup widget tree
1468 // build does not schedule timers which we might never get around to running.
1469 assert(inTest);
1470 attachRootWidget(rootWidget);
1471 _currentFakeAsync!.flushMicrotasks();
1472 }
1473
1474 @override
1475 Future<void> idle() {
1476 assert(inTest);
1477 final Future<void> result = super.idle();
1478 _currentFakeAsync!.elapse(Duration.zero);
1479 return result;
1480 }
1481
1482 int _firstFrameDeferredCount = 0;
1483 bool _firstFrameSent = false;
1484
1485 @override
1486 bool get sendFramesToEngine => _firstFrameSent || _firstFrameDeferredCount == 0;
1487
1488 @override
1489 void deferFirstFrame() {
1490 assert(_firstFrameDeferredCount >= 0);
1491 _firstFrameDeferredCount += 1;
1492 }
1493
1494 @override
1495 void allowFirstFrame() {
1496 assert(_firstFrameDeferredCount > 0);
1497 _firstFrameDeferredCount -= 1;
1498 // Unlike in RendererBinding.allowFirstFrame we do not force a frame here
1499 // to give the test full control over frame scheduling.
1500 }
1501
1502 @override
1503 void resetFirstFrameSent() {
1504 _firstFrameSent = false;
1505 }
1506
1507 EnginePhase _phase = EnginePhase.sendSemanticsUpdate;
1508
1509 // Cloned from RendererBinding.drawFrame() but with early-exit semantics.
1510 @override
1511 void drawFrame() {
1512 assert(inTest);
1513 try {
1514 debugBuildingDirtyElements = true;
1515 buildOwner!.buildScope(rootElement!);
1516 if (_phase != EnginePhase.build) {
1517 rootPipelineOwner.flushLayout();
1518 if (_phase != EnginePhase.layout) {
1519 rootPipelineOwner.flushCompositingBits();
1520 if (_phase != EnginePhase.compositingBits) {
1521 rootPipelineOwner.flushPaint();
1522 if (_phase != EnginePhase.paint && sendFramesToEngine) {
1523 _firstFrameSent = true;
1524 for (final RenderView renderView in renderViews) {
1525 renderView.compositeFrame(); // this sends the bits to the GPU
1526 }
1527 if (_phase != EnginePhase.composite) {
1528 rootPipelineOwner.flushSemantics(); // this sends the semantics to the OS.
1529 assert(
1530 _phase == EnginePhase.flushSemantics || _phase == EnginePhase.sendSemanticsUpdate,
1531 );
1532 }
1533 }
1534 }
1535 }
1536 }
1537 buildOwner!.finalizeTree();
1538 } finally {
1539 debugBuildingDirtyElements = false;
1540 }
1541 }
1542
1543 @override
1544 Future<void> delayed(Duration duration) {
1545 assert(_currentFakeAsync != null);
1546 _currentFakeAsync!.elapse(duration);
1547 return Future<void>.value();
1548 }
1549
1550 /// Simulates the synchronous passage of time, resulting from blocking or
1551 /// expensive calls.
1552 void elapseBlocking(Duration duration) {
1553 _currentFakeAsync!.elapseBlocking(duration);
1554 }
1555
1556 @override
1557 Future<void> runTest(
1558 Future<void> Function() testBody,
1559 VoidCallback invariantTester, {
1560 String description = '',
1561 }) {
1562 assert(!inTest);
1563 assert(_currentFakeAsync == null);
1564 assert(_clock == null);
1565
1566 final FakeAsync fakeAsync = FakeAsync();
1567 _currentFakeAsync = fakeAsync; // reset in postTest
1568 _clock = fakeAsync.getClock(DateTime.utc(2015));
1569 late Future<void> testBodyResult;
1570 fakeAsync.run((FakeAsync localFakeAsync) {
1571 assert(fakeAsync == _currentFakeAsync);
1572 assert(fakeAsync == localFakeAsync);
1573 testBodyResult = _runTest(testBody, invariantTester, description);
1574 assert(inTest);
1575 });
1576
1577 return Future<void>.microtask(() async {
1578 // testBodyResult is a Future that was created in the Zone of the
1579 // fakeAsync. This means that if we await it here, it will register a
1580 // microtask to handle the future _in the fake async zone_. We avoid this
1581 // by calling '.then' in the current zone. While flushing the microtasks
1582 // of the fake-zone below, the new future will be completed and can then
1583 // be used without fakeAsync.
1584
1585 final Future<void> resultFuture = testBodyResult.then<void>((_) {
1586 // Do nothing.
1587 });
1588
1589 // Resolve interplay between fake async and real async calls.
1590 fakeAsync.flushMicrotasks();
1591 while (_pendingAsyncTasks != null) {
1592 await _pendingAsyncTasks!.future;
1593 fakeAsync.flushMicrotasks();
1594 }
1595 return resultFuture;
1596 });
1597 }
1598
1599 @override
1600 void asyncBarrier() {
1601 assert(_currentFakeAsync != null);
1602 _currentFakeAsync!.flushMicrotasks();
1603 super.asyncBarrier();
1604 }
1605
1606 @override
1607 void _verifyInvariants() {
1608 super._verifyInvariants();
1609
1610 assert(inTest);
1611
1612 bool timersPending = false;
1613 if (_currentFakeAsync!.periodicTimerCount != 0 ||
1614 _currentFakeAsync!.nonPeriodicTimerCount != 0) {
1615 debugPrint('Pending timers:');
1616 for (final FakeTimer timer in _currentFakeAsync!.pendingTimers) {
1617 debugPrint(
1618 'Timer (duration: ${timer.duration}, '
1619 'periodic: ${timer.isPeriodic}), created:',
1620 );
1621 debugPrintStack(stackTrace: timer.creationStackTrace);
1622 debugPrint('');
1623 }
1624 timersPending = true;
1625 }
1626 assert(!timersPending, 'A Timer is still pending even after the widget tree was disposed.');
1627 assert(_currentFakeAsync!.microtaskCount == 0); // Shouldn't be possible.
1628 }
1629
1630 @override
1631 void postTest() {
1632 super.postTest();
1633 assert(_currentFakeAsync != null);
1634 assert(_clock != null);
1635 _clock = null;
1636 _currentFakeAsync = null;
1637 }
1638}
1639
1640/// Available policies for how a [LiveTestWidgetsFlutterBinding] should paint
1641/// frames.
1642///
1643/// These values are set on the binding's
1644/// [LiveTestWidgetsFlutterBinding.framePolicy] property.
1645///
1646/// {@template flutter.flutter_test.LiveTestWidgetsFlutterBindingFramePolicy}
1647/// The default is [LiveTestWidgetsFlutterBindingFramePolicy.fadePointers].
1648/// Setting this to anything other than
1649/// [LiveTestWidgetsFlutterBindingFramePolicy.onlyPumps] results in pumping
1650/// extra frames, which might involve calling builders more, or calling paint
1651/// callbacks more, etc, and might interfere with the test. If you know that
1652/// your test won't be affected by this, you can set the policy to
1653/// [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive] or
1654/// [LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive] in that particular
1655/// file.
1656///
1657/// To set a value while still allowing the test file to work as a normal test,
1658/// add the following code to your test file at the top of your
1659/// `void main() { }` function, before calls to [testWidgets]:
1660///
1661/// ```dart
1662/// TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
1663/// if (binding is LiveTestWidgetsFlutterBinding) {
1664/// binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.onlyPumps;
1665/// }
1666/// ```
1667/// {@endtemplate}
1668enum LiveTestWidgetsFlutterBindingFramePolicy {
1669 /// Strictly show only frames that are explicitly pumped.
1670 ///
1671 /// This most closely matches the [AutomatedTestWidgetsFlutterBinding]
1672 /// (the default binding for `flutter test`) behavior.
1673 onlyPumps,
1674
1675 /// Show pumped frames, and additionally schedule and run frames to fade
1676 /// out the pointer crosshairs and other debugging information shown by
1677 /// the binding.
1678 ///
1679 /// This will schedule frames when pumped or when there has been some
1680 /// activity with [TestPointer]s.
1681 ///
1682 /// This can result in additional frames being pumped beyond those that
1683 /// the test itself requests, which can cause differences in behavior.
1684 fadePointers,
1685
1686 /// Show every frame that the framework requests, even if the frames are not
1687 /// explicitly pumped.
1688 ///
1689 /// The major difference between [fullyLive] and [benchmarkLive] is the latter
1690 /// ignores frame requests by [WidgetTester.pump].
1691 ///
1692 /// This can help with orienting the developer when looking at
1693 /// heavily-animated situations, and will almost certainly result in
1694 /// additional frames being pumped beyond those that the test itself requests,
1695 /// which can cause differences in behavior.
1696 fullyLive,
1697
1698 /// Ignore any request to schedule a frame.
1699 ///
1700 /// This is intended to be used by benchmarks (hence the name) that drive the
1701 /// pipeline directly. It tells the binding to entirely ignore requests for a
1702 /// frame to be scheduled, while still allowing frames that are pumped
1703 /// directly to run (either by using [WidgetTester.pumpBenchmark] or invoking
1704 /// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame]).
1705 ///
1706 /// This allows all frame requests from the engine to be serviced, and allows
1707 /// all frame requests that are artificially triggered to be serviced, but
1708 /// ignores [SchedulerBinding.scheduleFrame] requests from the framework.
1709 /// Therefore animation won't run for this mode because the framework
1710 /// generates an animation by requesting new frames.
1711 ///
1712 /// The [SchedulerBinding.hasScheduledFrame] property will never be true in
1713 /// this mode. This can cause unexpected effects. For instance,
1714 /// [WidgetTester.pumpAndSettle] does not function in this mode, as it relies
1715 /// on the [SchedulerBinding.hasScheduledFrame] property to determine when the
1716 /// application has "settled".
1717 benchmark,
1718
1719 /// Ignore any request from pump but respect other requests to schedule a
1720 /// frame.
1721 ///
1722 /// This is used for running the test on a device, where scheduling of new
1723 /// frames respects what the engine and the device needed.
1724 ///
1725 /// Compared to [fullyLive] this policy ignores the frame requests from
1726 /// [WidgetTester.pump] so that frame scheduling mimics that of the real
1727 /// environment, and avoids waiting for an artificially pumped frame. (For
1728 /// example, when driving the test in methods like
1729 /// [WidgetTester.handlePointerEventRecord] or [WidgetTester.fling].)
1730 ///
1731 /// This policy differs from [benchmark] in that it can be used for capturing
1732 /// animation frames requested by the framework.
1733 benchmarkLive,
1734}
1735
1736enum _HandleDrawFrame { reset, drawFrame, skipFrame }
1737
1738/// A variant of [TestWidgetsFlutterBinding] for executing tests
1739/// on a device, typically via `flutter run`, or via integration tests.
1740/// This is intended to allow interactive test development.
1741///
1742/// This is not the way to run a remote-control test. To run a test on
1743/// a device from a development computer, see the [flutter_driver]
1744/// package and the `flutter drive` command.
1745///
1746/// When running tests using `flutter run`, consider adding the
1747/// `--use-test-fonts` argument so that the fonts used match those used under
1748/// `flutter test`. (This forces all text to use the "Ahem" font, which is a
1749/// font that covers ASCII characters and gives them all the appearance of a
1750/// square whose size equals the font size.)
1751///
1752/// This binding overrides the default [SchedulerBinding] behavior to ensure
1753/// that tests work in the same way in this environment as they would under the
1754/// [AutomatedTestWidgetsFlutterBinding]. To override this (and see intermediate
1755/// frames that the test does not explicitly trigger), set [framePolicy] to
1756/// [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive]. (This is likely to
1757/// make tests fail, though, especially if e.g. they test how many times a
1758/// particular widget was built.) The default behavior is to show pumped frames
1759/// and a few additional frames when pointers are triggered (to animate the
1760/// pointer crosshairs).
1761///
1762/// This binding does not support the [EnginePhase] argument to
1763/// [pump]. (There would be no point setting it to a value that
1764/// doesn't trigger a paint, since then you could not see anything
1765/// anyway.)
1766///
1767/// See [TestWidgetsFlutterBinding] for a list of mixins that must be
1768/// provided by the binding active while the test framework is
1769/// running.
1770class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
1771 @override
1772 void initInstances() {
1773 super.initInstances();
1774 _instance = this;
1775
1776 RenderView.debugAddPaintCallback(_handleRenderViewPaint);
1777 }
1778
1779 /// The current [LiveTestWidgetsFlutterBinding], if one has been created.
1780 ///
1781 /// The binding must be initialized before using this getter. If you
1782 /// need the binding to be constructed before calling [testWidgets],
1783 /// you can ensure a binding has been constructed by calling the
1784 /// [TestWidgetsFlutterBinding.ensureInitialized] function.
1785 static LiveTestWidgetsFlutterBinding get instance => BindingBase.checkInstance(_instance);
1786 static LiveTestWidgetsFlutterBinding? _instance;
1787
1788 /// Returns an instance of the binding that implements
1789 /// [LiveTestWidgetsFlutterBinding]. If no binding has yet been
1790 /// initialized, the a new instance is created.
1791 ///
1792 /// Generally, there is no need to call this method. Use
1793 /// [TestWidgetsFlutterBinding.ensureInitialized] instead, as it
1794 /// will select the correct test binding implementation
1795 /// automatically.
1796 static LiveTestWidgetsFlutterBinding ensureInitialized() {
1797 if (LiveTestWidgetsFlutterBinding._instance == null) {
1798 LiveTestWidgetsFlutterBinding();
1799 }
1800 return LiveTestWidgetsFlutterBinding.instance;
1801 }
1802
1803 @override
1804 bool get inTest => _inTest;
1805 bool _inTest = false;
1806
1807 @override
1808 Clock get clock => const Clock();
1809
1810 @override
1811 int get microtaskCount {
1812 // The Dart SDK doesn't report this number.
1813 assert(false, 'microtaskCount cannot be reported when running in real time');
1814 return -1;
1815 }
1816
1817 @override
1818 test_package.Timeout get defaultTestTimeout => test_package.Timeout.none;
1819
1820 Completer<void>? _pendingFrame;
1821 bool _expectingFrame = false;
1822 bool _expectingFrameToReassemble = false;
1823 bool _viewNeedsPaint = false;
1824 bool _runningAsyncTasks = false;
1825
1826 /// The strategy for [pump]ing and requesting new frames.
1827 ///
1828 /// The policy decides whether [pump] (with a duration) pumps a single frame
1829 /// (as would happen in a normal test environment using
1830 /// [AutomatedTestWidgetsFlutterBinding]), or pumps every frame that the
1831 /// system requests during an asynchronous pause (as would normally happen
1832 /// when running an application with [WidgetsFlutterBinding]).
1833 ///
1834 /// {@macro flutter.flutter_test.LiveTestWidgetsFlutterBindingFramePolicy}
1835 ///
1836 /// See [LiveTestWidgetsFlutterBindingFramePolicy].
1837 LiveTestWidgetsFlutterBindingFramePolicy framePolicy =
1838 LiveTestWidgetsFlutterBindingFramePolicy.fadePointers;
1839
1840 @override
1841 Future<void> delayed(Duration duration) {
1842 return Future<void>.delayed(duration);
1843 }
1844
1845 @override
1846 void scheduleFrame() {
1847 if (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.benchmark) {
1848 // In benchmark mode, don't actually schedule any engine frames.
1849 return;
1850 }
1851 super.scheduleFrame();
1852 }
1853
1854 @override
1855 void scheduleForcedFrame() {
1856 if (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.benchmark) {
1857 // In benchmark mode, don't actually schedule any engine frames.
1858 return;
1859 }
1860 super.scheduleForcedFrame();
1861 }
1862
1863 @override
1864 Future<void> reassembleApplication() {
1865 _expectingFrameToReassemble = true;
1866 return super.reassembleApplication();
1867 }
1868
1869 _HandleDrawFrame _drawFrame = _HandleDrawFrame.reset;
1870
1871 @override
1872 void handleBeginFrame(Duration? rawTimeStamp) {
1873 if (_drawFrame != _HandleDrawFrame.reset) {
1874 throw StateError('handleBeginFrame() called before previous handleDrawFrame()');
1875 }
1876 if (_expectingFrame ||
1877 _expectingFrameToReassemble ||
1878 (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.fullyLive) ||
1879 (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive) ||
1880 (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.benchmark) ||
1881 (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.fadePointers && _viewNeedsPaint)) {
1882 _drawFrame = _HandleDrawFrame.drawFrame;
1883 super.handleBeginFrame(rawTimeStamp);
1884 } else {
1885 _drawFrame = _HandleDrawFrame.skipFrame;
1886 }
1887 }
1888
1889 @override
1890 void handleDrawFrame() {
1891 if (_drawFrame == _HandleDrawFrame.reset) {
1892 throw StateError('handleDrawFrame() called without paired handleBeginFrame()');
1893 }
1894 if (_drawFrame == _HandleDrawFrame.drawFrame) {
1895 super.handleDrawFrame();
1896 }
1897 _drawFrame = _HandleDrawFrame.reset;
1898 _viewNeedsPaint = false;
1899 _expectingFrameToReassemble = false;
1900 if (_expectingFrame) {
1901 // set during pump
1902 assert(_pendingFrame != null);
1903 _pendingFrame!.complete(); // unlocks the test API
1904 _pendingFrame = null;
1905 _expectingFrame = false;
1906 } else if (framePolicy != LiveTestWidgetsFlutterBindingFramePolicy.benchmark) {
1907 platformDispatcher.scheduleFrame();
1908 }
1909 }
1910
1911 void _markViewsNeedPaint([int? viewId]) {
1912 _viewNeedsPaint = true;
1913 final Iterable<RenderView> toMark =
1914 viewId == null
1915 ? renderViews
1916 : renderViews.where((RenderView renderView) => renderView.flutterView.viewId == viewId);
1917 for (final RenderView renderView in toMark) {
1918 renderView.markNeedsPaint();
1919 }
1920 }
1921
1922 TextPainter? _label;
1923 static const TextStyle _labelStyle = TextStyle(fontFamily: 'sans-serif', fontSize: 10.0);
1924
1925 /// Label describing the test.
1926 @visibleForTesting
1927 TextPainter? get label => _label;
1928
1929 /// Set a description label that is drawn into the test output.
1930 @protected
1931 void setLabel(String value) {
1932 if (value.isEmpty) {
1933 _label = null;
1934 return;
1935 }
1936 // TODO(ianh): Figure out if the test name is actually RTL.
1937 _label ??= TextPainter(textAlign: TextAlign.left, textDirection: TextDirection.ltr);
1938 _label!.text = TextSpan(text: value, style: _labelStyle);
1939 _label!.layout();
1940 _markViewsNeedPaint();
1941 }
1942
1943 final Expando<Map<int, _LiveTestPointerRecord>> _renderViewToPointerIdToPointerRecord =
1944 Expando<Map<int, _LiveTestPointerRecord>>();
1945
1946 void _handleRenderViewPaint(PaintingContext context, Offset offset, RenderView renderView) {
1947 assert(offset == Offset.zero);
1948
1949 final Map<int, _LiveTestPointerRecord>? pointerIdToRecord =
1950 _renderViewToPointerIdToPointerRecord[renderView];
1951 if (pointerIdToRecord != null && pointerIdToRecord.isNotEmpty) {
1952 final double radius = renderView.size.shortestSide * 0.05;
1953 final Path path =
1954 Path()
1955 ..addOval(Rect.fromCircle(center: Offset.zero, radius: radius))
1956 ..moveTo(0.0, -radius * 2.0)
1957 ..lineTo(0.0, radius * 2.0)
1958 ..moveTo(-radius * 2.0, 0.0)
1959 ..lineTo(radius * 2.0, 0.0);
1960 final Canvas canvas = context.canvas;
1961 final Paint paint =
1962 Paint()
1963 ..strokeWidth = radius / 10.0
1964 ..style = PaintingStyle.stroke;
1965 bool dirty = false;
1966 for (final _LiveTestPointerRecord record in pointerIdToRecord.values) {
1967 paint.color = record.color.withOpacity(
1968 record.decay < 0 ? (record.decay / (_kPointerDecay - 1)) : 1.0,
1969 );
1970 canvas.drawPath(path.shift(record.position), paint);
1971 if (record.decay < 0) {
1972 dirty = true;
1973 }
1974 record.decay += 1;
1975 }
1976 pointerIdToRecord.keys
1977 .where((int pointer) => pointerIdToRecord[pointer]!.decay == 0)
1978 .toList()
1979 .forEach(pointerIdToRecord.remove);
1980 if (dirty) {
1981 scheduleMicrotask(() {
1982 _markViewsNeedPaint(renderView.flutterView.viewId);
1983 });
1984 }
1985 }
1986
1987 _label?.paint(context.canvas, offset - const Offset(0.0, 10.0));
1988 }
1989
1990 /// An object to which real device events should be routed.
1991 ///
1992 /// Normally, device events are silently dropped. However, if this property is
1993 /// set to a non-null value, then the events will be routed to its
1994 /// [HitTestDispatcher.dispatchEvent] method instead, unless
1995 /// [shouldPropagateDevicePointerEvents] is true.
1996 ///
1997 /// Events dispatched by [TestGesture] are not affected by this.
1998 HitTestDispatcher? deviceEventDispatcher;
1999
2000 /// Dispatch an event to the targets found by a hit test on its position.
2001 ///
2002 /// If the [pointerEventSource] is [TestBindingEventSource.test], then
2003 /// the event is forwarded to [GestureBinding.dispatchEvent] as usual;
2004 /// additionally, down pointers are painted on the screen.
2005 ///
2006 /// If the [pointerEventSource] is [TestBindingEventSource.device], then
2007 /// the event, after being transformed to the local coordinate system, is
2008 /// forwarded to [deviceEventDispatcher].
2009 @override
2010 void handlePointerEvent(PointerEvent event) {
2011 switch (pointerEventSource) {
2012 case TestBindingEventSource.test:
2013 RenderView? target;
2014 for (final RenderView renderView in renderViews) {
2015 if (renderView.flutterView.viewId == event.viewId) {
2016 target = renderView;
2017 break;
2018 }
2019 }
2020 if (target != null) {
2021 final _LiveTestPointerRecord? record =
2022 _renderViewToPointerIdToPointerRecord[target]?[event.pointer];
2023 if (record != null) {
2024 record.position = event.position;
2025 if (!event.down) {
2026 record.decay = _kPointerDecay;
2027 }
2028 _markViewsNeedPaint(event.viewId);
2029 } else if (event.down) {
2030 _renderViewToPointerIdToPointerRecord[target] ??= <int, _LiveTestPointerRecord>{};
2031 _renderViewToPointerIdToPointerRecord[target]![event.pointer] = _LiveTestPointerRecord(
2032 event.pointer,
2033 event.position,
2034 );
2035 _markViewsNeedPaint(event.viewId);
2036 }
2037 }
2038 super.handlePointerEvent(event);
2039 case TestBindingEventSource.device:
2040 if (shouldPropagateDevicePointerEvents) {
2041 super.handlePointerEvent(event);
2042 break;
2043 }
2044 if (deviceEventDispatcher != null) {
2045 // The pointer events received with this source has a global position
2046 // (see [handlePointerEventForSource]). Transform it to the local
2047 // coordinate space used by the testing widgets.
2048 final RenderView renderView = renderViews.firstWhere(
2049 (RenderView r) => r.flutterView.viewId == event.viewId,
2050 );
2051 final PointerEvent localEvent = event.copyWith(
2052 position: globalToLocal(event.position, renderView),
2053 );
2054 withPointerEventSource(
2055 TestBindingEventSource.device,
2056 () => super.handlePointerEvent(localEvent),
2057 );
2058 }
2059 }
2060 }
2061
2062 @override
2063 void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
2064 switch (pointerEventSource) {
2065 case TestBindingEventSource.test:
2066 super.dispatchEvent(event, hitTestResult);
2067 case TestBindingEventSource.device:
2068 assert(hitTestResult != null || event is PointerAddedEvent || event is PointerRemovedEvent);
2069 if (shouldPropagateDevicePointerEvents) {
2070 super.dispatchEvent(event, hitTestResult);
2071 break;
2072 }
2073 assert(deviceEventDispatcher != null);
2074 if (hitTestResult != null) {
2075 deviceEventDispatcher!.dispatchEvent(event, hitTestResult);
2076 }
2077 }
2078 }
2079
2080 @override
2081 Future<void> pump([Duration? duration, EnginePhase newPhase = EnginePhase.sendSemanticsUpdate]) {
2082 assert(newPhase == EnginePhase.sendSemanticsUpdate);
2083 assert(inTest);
2084 assert(!_expectingFrame);
2085 assert(_pendingFrame == null);
2086 if (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive) {
2087 // Ignore all pumps and just wait.
2088 return delayed(duration ?? Duration.zero);
2089 }
2090 return TestAsyncUtils.guard<void>(() {
2091 if (duration != null) {
2092 Timer(duration, () {
2093 _expectingFrame = true;
2094 scheduleFrame();
2095 });
2096 } else {
2097 _expectingFrame = true;
2098 scheduleFrame();
2099 }
2100 _pendingFrame = Completer<void>();
2101 return _pendingFrame!.future;
2102 });
2103 }
2104
2105 @override
2106 Future<T?> runAsync<T>(Future<T> Function() callback) async {
2107 assert(() {
2108 if (!_runningAsyncTasks) {
2109 return true;
2110 }
2111 fail(
2112 'Reentrant call to runAsync() denied.\n'
2113 'runAsync() was called, then before its future completed, it '
2114 'was called again. You must wait for the first returned future '
2115 'to complete before calling runAsync() again.',
2116 );
2117 }());
2118
2119 _runningAsyncTasks = true;
2120 try {
2121 return await callback();
2122 } catch (error, stack) {
2123 FlutterError.reportError(
2124 FlutterErrorDetails(
2125 exception: error,
2126 stack: stack,
2127 library: 'Flutter test framework',
2128 context: ErrorSummary('while running async test code'),
2129 ),
2130 );
2131 return null;
2132 } finally {
2133 _runningAsyncTasks = false;
2134 }
2135 }
2136
2137 @override
2138 Future<void> runTest(
2139 Future<void> Function() testBody,
2140 VoidCallback invariantTester, {
2141 String description = '',
2142 }) {
2143 assert(!inTest);
2144 _inTest = true;
2145 setLabel(description);
2146 return _runTest(testBody, invariantTester, description);
2147 }
2148
2149 @override
2150 void reportExceptionNoticed(FlutterErrorDetails exception) {
2151 final DebugPrintCallback testPrint = debugPrint;
2152 debugPrint = debugPrintOverride;
2153 debugPrint('(The following exception is now available via WidgetTester.takeException:)');
2154 FlutterError.dumpErrorToConsole(exception, forceReport: true);
2155 debugPrint(
2156 '(If WidgetTester.takeException is called, the above exception will be ignored. '
2157 'If it is not, then the above exception will be dumped when another exception is '
2158 'caught by the framework or when the test ends, whichever happens first, and then '
2159 'the test will fail due to having not caught or expected the exception.)',
2160 );
2161 debugPrint = testPrint;
2162 }
2163
2164 @override
2165 void postTest() {
2166 super.postTest();
2167 assert(!_expectingFrame);
2168 assert(_pendingFrame == null);
2169 _inTest = false;
2170 }
2171
2172 @override
2173 ViewConfiguration createViewConfigurationFor(RenderView renderView) {
2174 final FlutterView view = renderView.flutterView;
2175 if (view == platformDispatcher.implicitView) {
2176 return TestViewConfiguration.fromView(
2177 size: _surfaceSize ?? _kDefaultTestViewportSize,
2178 view: view,
2179 );
2180 }
2181 final double devicePixelRatio = view.devicePixelRatio;
2182 return TestViewConfiguration.fromView(size: view.physicalSize / devicePixelRatio, view: view);
2183 }
2184
2185 @override
2186 Offset globalToLocal(Offset point, RenderView view) {
2187 // The method is expected to translate the given point expressed in logical
2188 // pixels in the global coordinate space to the local coordinate space (also
2189 // expressed in logical pixels).
2190 // The inverted transform translates from the global coordinate space in
2191 // physical pixels to the local coordinate space in logical pixels.
2192 final Matrix4 transform = view.configuration.toMatrix();
2193 final double det = transform.invert();
2194 assert(det != 0.0);
2195 // In order to use the transform, we need to translate the point first into
2196 // the physical coordinate space by applying the device pixel ratio.
2197 return MatrixUtils.transformPoint(transform, point * view.configuration.devicePixelRatio);
2198 }
2199
2200 @override
2201 Offset localToGlobal(Offset point, RenderView view) {
2202 // The method is expected to translate the given point expressed in logical
2203 // pixels in the local coordinate space to the global coordinate space (also
2204 // expressed in logical pixels).
2205 // The transform translates from the local coordinate space in logical
2206 // pixels to the global coordinate space in physical pixels.
2207 final Matrix4 transform = view.configuration.toMatrix();
2208 final Offset pointInPhysicalPixels = MatrixUtils.transformPoint(transform, point);
2209 // We need to apply the device pixel ratio to get back to logical pixels.
2210 return pointInPhysicalPixels / view.configuration.devicePixelRatio;
2211 }
2212}
2213
2214/// A [ViewConfiguration] that pretends the display is of a particular size (in
2215/// logical pixels).
2216///
2217/// The resulting ViewConfiguration maps the given size onto the actual display
2218/// using the [BoxFit.contain] algorithm.
2219///
2220/// If the underlying [FlutterView] changes, a new [TestViewConfiguration] should
2221/// be created. See [RendererBinding.handleMetricsChanged] and
2222/// [RendererBinding.createViewConfigurationFor].
2223class TestViewConfiguration implements ViewConfiguration {
2224 /// Deprecated. Will be removed in a future version of Flutter.
2225 ///
2226 /// This property has been deprecated to prepare for Flutter's upcoming
2227 /// support for multiple views and multiple windows.
2228 ///
2229 /// Use [TestViewConfiguration.fromView] instead.
2230 @Deprecated(
2231 'Use TestViewConfiguration.fromView instead. '
2232 'Deprecated to prepare for the upcoming multi-window support. '
2233 'This feature was deprecated after v3.7.0-32.0.pre.',
2234 )
2235 factory TestViewConfiguration({Size size = _kDefaultTestViewportSize, ui.FlutterView? window}) {
2236 return TestViewConfiguration.fromView(size: size, view: window ?? ui.window);
2237 }
2238
2239 /// Creates a [TestViewConfiguration] with the given size and view.
2240 ///
2241 /// The [size] defaults to 800x600.
2242 ///
2243 /// The settings of the given [FlutterView] are captured when the constructor
2244 /// is called, and subsequent changes are ignored. A new
2245 /// [TestViewConfiguration] should be created if the underlying [FlutterView]
2246 /// changes. See [RendererBinding.handleMetricsChanged] and
2247 /// [RendererBinding.createViewConfigurationFor].
2248 TestViewConfiguration.fromView({
2249 required ui.FlutterView view,
2250 Size size = _kDefaultTestViewportSize,
2251 }) : devicePixelRatio = view.devicePixelRatio,
2252 logicalConstraints = BoxConstraints.tight(size),
2253 physicalConstraints = BoxConstraints.tight(size) * view.devicePixelRatio,
2254 _paintMatrix = _getMatrix(size, view.devicePixelRatio, view),
2255 _physicalSize = view.physicalSize;
2256
2257 @override
2258 final double devicePixelRatio;
2259
2260 @override
2261 final BoxConstraints logicalConstraints;
2262
2263 @override
2264 final BoxConstraints physicalConstraints;
2265
2266 static Matrix4 _getMatrix(Size size, double devicePixelRatio, ui.FlutterView window) {
2267 final double inverseRatio = devicePixelRatio / window.devicePixelRatio;
2268 final double actualWidth = window.physicalSize.width * inverseRatio;
2269 final double actualHeight = window.physicalSize.height * inverseRatio;
2270 final double desiredWidth = size.width;
2271 final double desiredHeight = size.height;
2272 double scale, shiftX, shiftY;
2273 if (desiredWidth == 0.0 || desiredHeight == 0.0) {
2274 scale = 1.0;
2275 shiftX = 0.0;
2276 shiftY = 0.0;
2277 } else if ((actualWidth / actualHeight) > (desiredWidth / desiredHeight)) {
2278 scale = actualHeight / desiredHeight;
2279 shiftX = (actualWidth - desiredWidth * scale) / 2.0;
2280 shiftY = 0.0;
2281 } else {
2282 scale = actualWidth / desiredWidth;
2283 shiftX = 0.0;
2284 shiftY = (actualHeight - desiredHeight * scale) / 2.0;
2285 }
2286 final Matrix4 matrix = Matrix4.compose(
2287 Vector3(shiftX, shiftY, 0.0), // translation
2288 Quaternion.identity(), // rotation
2289 Vector3(scale, scale, 1.0), // scale
2290 );
2291 return matrix;
2292 }
2293
2294 final Matrix4 _paintMatrix;
2295
2296 @override
2297 Matrix4 toMatrix() => _paintMatrix.clone();
2298
2299 @override
2300 bool shouldUpdateMatrix(ViewConfiguration oldConfiguration) {
2301 if (oldConfiguration.runtimeType != runtimeType) {
2302 // New configuration could have different logic, so we don't know
2303 // whether it will need a new transform. Return a conservative result.
2304 return true;
2305 }
2306 oldConfiguration as TestViewConfiguration;
2307 // Compare the matrices directly since they are cached.
2308 return oldConfiguration._paintMatrix != _paintMatrix;
2309 }
2310
2311 final Size _physicalSize;
2312
2313 @override
2314 Size toPhysicalSize(Size logicalSize) => _physicalSize;
2315
2316 @override
2317 String toString() => 'TestViewConfiguration';
2318}
2319
2320class _TestSamplingClock implements SamplingClock {
2321 _TestSamplingClock(this._clock);
2322
2323 @override
2324 DateTime now() => _clock.now();
2325
2326 @override
2327 Stopwatch stopwatch() => _clock.stopwatch();
2328
2329 final Clock _clock;
2330}
2331
2332const int _kPointerDecay = -2;
2333
2334class _LiveTestPointerRecord {
2335 _LiveTestPointerRecord(this.pointer, this.position)
2336 : color = HSVColor.fromAHSV(0.8, (35.0 * pointer) % 360.0, 1.0, 1.0).toColor(),
2337 decay = 1;
2338 final int pointer;
2339 final Color color;
2340 Offset position;
2341 int decay; // >0 means down, <0 means up, increases by one each time, removed at 0
2342}
2343

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com