binding.dart 61.4 KB
Newer Older
1 2 3 4
// Copyright 2016 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.

5
import 'dart:async';
6
import 'dart:typed_data';
7
import 'dart:ui' as ui;
8

9
import 'package:flutter/foundation.dart';
10
import 'package:flutter/gestures.dart';
11 12
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
13
import 'package:flutter/services.dart';
14
import 'package:flutter/widgets.dart';
15
import 'package:flutter_test/flutter_test.dart' show TestWindow;
16 17
import 'package:quiver/testing/async.dart';
import 'package:quiver/time.dart';
18
import 'package:test_api/test_api.dart' as test_package;
19
import 'package:stack_trace/stack_trace.dart' as stack_trace;
20 21
import 'package:vector_math/vector_math_64.dart';

22
import '_binding_io.dart' if (dart.library.html) '_binding_web.dart' as binding;
23
import 'goldens.dart';
24
import 'platform.dart';
25
import 'stack_manipulation.dart';
26
import 'test_async_utils.dart';
27
import 'test_exception_reporter.dart';
28
import 'test_text_input.dart';
29

30 31
/// Phases that can be reached by [WidgetTester.pumpWidget] and
/// [TestWidgetsFlutterBinding.pump].
32
///
33
/// See [WidgetsBinding.drawFrame] for a more detailed description of some of
34
/// these phases.
35
enum EnginePhase {
36
  /// The build phase in the widgets library. See [BuildOwner.buildScope].
37 38 39
  build,

  /// The layout phase in the rendering library. See [PipelineOwner.flushLayout].
40
  layout,
41 42 43

  /// The compositing bits update phase in the rendering library. See
  /// [PipelineOwner.flushCompositingBits].
44
  compositingBits,
45 46

  /// The paint phase in the rendering library. See [PipelineOwner.flushPaint].
47
  paint,
48 49 50 51

  /// The compositing phase in the rendering library. See
  /// [RenderView.compositeFrame]. This is the phase in which data is sent to
  /// the GPU. If semantics are not enabled, then this is the last phase.
52
  composite,
53 54 55

  /// The semantics building phase in the rendering library. See
  /// [PipelineOwner.flushSemantics].
56
  flushSemantics,
57 58

  /// The final phase in the rendering library, wherein semantics information is
Ian Hickson's avatar
Ian Hickson committed
59
  /// sent to the embedder. See [SemanticsOwner.sendSemanticsUpdate].
60
  sendSemanticsUpdate,
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
}

/// Parts of the system that can generate pointer events that reach the test
/// binding.
///
/// This is used to identify how to handle events in the
/// [LiveTestWidgetsFlutterBinding]. See
/// [TestWidgetsFlutterBinding.dispatchEvent].
enum TestBindingEventSource {
  /// The pointer event came from the test framework itself, e.g. from a
  /// [TestGesture] created by [WidgetTester.startGesture].
  test,

  /// The pointer event came from the system, presumably as a result of the user
  /// interactive directly with the device while the test was running.
  device,
77 78
}

79
const Size _kDefaultTestViewportSize = Size(800.0, 600.0);
80

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
/// A [BinaryMessenger] subclass that is used as the default binary messenger
/// under testing environment.
///
/// It tracks status of data sent across the Flutter platform barrier, which is
/// useful for testing frameworks to monitor and synchronize against the
/// platform messages.
class TestDefaultBinaryMessenger extends BinaryMessenger {
  /// Creates a [TestDefaultBinaryMessenger] instance.
  ///
  /// The [delegate] instance must not be null.
  TestDefaultBinaryMessenger(this.delegate): assert(delegate != null);

  /// The delegate [BinaryMessenger].
  final BinaryMessenger delegate;

  final List<Future<ByteData>> _pendingMessages = <Future<ByteData>>[];

  /// The number of incomplete/pending calls sent to the platform channels.
  int get pendingMessageCount => _pendingMessages.length;

  @override
  Future<ByteData> send(String channel, ByteData message) {
    final Future<ByteData> resultFuture = delegate.send(channel, message);
    // Removes the future itself from the [_pendingMessages] list when it
    // completes.
    if (resultFuture != null) {
      _pendingMessages.add(resultFuture);
      resultFuture.whenComplete(() => _pendingMessages.remove(resultFuture));
    }
    return resultFuture;
  }

  /// Returns a Future that completes after all the platform calls are finished.
  ///
  /// If a new platform message is sent after this method is called, this new
  /// message is not tracked. Use with [pendingMessageCount] to guarantee no
  /// pending message calls.
  Future<void> get platformMessagesFinished {
    return Future.wait<void>(_pendingMessages);
  }

  @override
  Future<void> handlePlatformMessage(
      String channel,
      ByteData data,
      ui.PlatformMessageResponseCallback callback,
  ) {
    return delegate.handlePlatformMessage(channel, data, callback);
  }

  @override
  void setMessageHandler(String channel, MessageHandler handler) {
    delegate.setMessageHandler(channel, handler);
  }

  @override
  void setMockMessageHandler(String channel, MessageHandler handler) {
    delegate.setMockMessageHandler(channel, handler);
  }
}

142 143 144 145 146 147 148 149
/// Base class for bindings used by widgets library tests.
///
/// The [ensureInitialized] method creates (if necessary) and returns
/// an instance of the appropriate subclass.
///
/// When using these bindings, certain features are disabled. For
/// example, [timeDilation] is reset to 1.0 on initialization.
abstract class TestWidgetsFlutterBinding extends BindingBase
150 151
  with ServicesBinding,
       SchedulerBinding,
152
       GestureBinding,
153
       SemanticsBinding,
154
       RendererBinding,
155
       PaintingBinding,
156
       WidgetsBinding {
157

158 159 160 161
  /// Constructor for [TestWidgetsFlutterBinding].
  ///
  /// This constructor overrides the [debugPrint] global hook to point to
  /// [debugPrintOverride], which can be overridden by subclasses.
162
  TestWidgetsFlutterBinding() : _window = TestWindow(window: ui.window) {
163
    debugPrint = debugPrintOverride;
164
    debugDisableShadows = disableShadows;
165
    debugCheckIntrinsicSizes = checkIntrinsicSizes;
166 167
  }

168 169 170 171
  @override
  TestWindow get window => _window;
  final TestWindow _window;

172 173 174 175 176
  /// The value to set [debugPrint] to while tests are running.
  ///
  /// This can be used to redirect console output from the framework, or to
  /// change the behavior of [debugPrint]. For example,
  /// [AutomatedTestWidgetsFlutterBinding] uses it to make [debugPrint]
177
  /// synchronous, disabling its normal throttling behavior.
178 179 180
  @protected
  DebugPrintCallback get debugPrintOverride => debugPrint;

181 182 183 184 185 186 187 188 189
  /// The value to set [debugDisableShadows] to while tests are running.
  ///
  /// This can be used to reduce the likelihood of golden file tests being
  /// flaky, because shadow rendering is not always deterministic. The
  /// [AutomatedTestWidgetsFlutterBinding] sets this to true, so that all tests
  /// always run with shadows disabled.
  @protected
  bool get disableShadows => false;

190
  /// Increase the timeout for the current test by the given duration.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
  ///
  /// This only matters if the test has an `initialTimeout` set on
  /// [testWidgets], and the test is running via `flutter test`. By default,
  /// tests do not have such a timeout. Tests run using `flutter run` never time
  /// out even if one is specified.
  ///
  /// This method has no effect on the timeout specified via `timeout` on
  /// [testWidgets]. That timeout is implemented by the `test` package.
  ///
  /// By default, each [pump] and [pumpWidget] call increases the timeout by a
  /// hundred milliseconds, and each [matchesGoldenFile] expectation increases
  /// it by a minute. If there is no timeout in the first place, this has no
  /// effect.
  ///
  /// The granularity of timeouts is coarse: the time is checked once per
  /// second, and only when the test is not executing. It is therefore possible
  /// for a timeout to be exceeded by hundreds of milliseconds and for the test
  /// to still succeed. If precise timing is required, it should be implemented
  /// as a part of the test rather than relying on this mechanism.
  ///
  /// See also:
  ///
  ///  * [testWidgets], on which a timeout can be set using the `timeout`
  ///    argument.
  ///  * [defaultTestTimeout], the maximum that the timeout can reach.
  ///    (That timeout is implemented by the `test` package.)
  // See AutomatedTestWidgetsFlutterBinding.addTime for an actual implementation.
  void addTime(Duration duration);
219

220 221 222 223 224
  /// The value to set [debugCheckIntrinsicSizes] to while tests are running.
  ///
  /// This can be used to enable additional checks. For example,
  /// [AutomatedTestWidgetsFlutterBinding] sets this to true, so that all tests
  /// always run with aggressive intrinsic sizing tests enabled.
225 226 227
  @protected
  bool get checkIntrinsicSizes => false;

228
  /// Creates and initializes the binding. This function is
229 230
  /// idempotent; calling it a second time will just return the
  /// previously-created instance.
231 232 233 234
  ///
  /// This function will use [AutomatedTestWidgetsFlutterBinding] if
  /// the test was run using `flutter test`, and
  /// [LiveTestWidgetsFlutterBinding] otherwise (e.g. if it was run
235 236 237 238 239 240 241 242 243 244 245 246 247
  /// using `flutter run`). This is determined by looking at the
  /// environment variables for a variable called `FLUTTER_TEST`.
  ///
  /// If `FLUTTER_TEST` is set with a value of 'true', then this test was
  /// invoked by `flutter test`. If `FLUTTER_TEST` is not set, or if it is set
  /// to 'false', then this test was invoked by `flutter run`.
  ///
  /// Browser environments do not currently support the
  /// [LiveTestWidgetsFlutterBinding], so this function will always set up an
  /// [AutomatedTestWidgetsFlutterBinding] when run in a web browser.
  ///
  /// The parameter `environment` is exposed to test different environment
  /// variable values, and should not be used.
248
  static WidgetsBinding ensureInitialized([@visibleForTesting Map<String, String> environment]) => binding.ensureInitialized(environment);
249

250 251
  @override
  void initInstances() {
252
    super.initInstances();
253
    timeDilation = 1.0; // just in case the developer has artificially changed it for development
254
    binding.setupHttpOverrides();
255
    _testTextInput = TestTextInput(onCleared: _resetFocusedEditable)..register();
256 257
  }

258
  @override
259
  // ignore: MUST_CALL_SUPER
260 261 262 263 264
  void initLicenses() {
    // Do not include any licenses, because we're a test, and the LICENSE file
    // doesn't get generated for tests.
  }

265 266 267 268 269
  @override
  BinaryMessenger createBinaryMessenger() {
    return TestDefaultBinaryMessenger(super.createBinaryMessenger());
  }

270
  /// Whether there is currently a test executing.
271
  bool get inTest;
272

273 274 275
  /// The number of outstanding microtasks in the queue.
  int get microtaskCount;

276 277 278 279 280 281
  /// The default maximum test timeout for tests when using this binding.
  ///
  /// This controls the default for the `timeout` argument on `testWidgets`. It
  /// is 10 minutes for [AutomatedTestWidgetsFlutterBinding] (tests running
  /// using `flutter test`), and unlimited for tests using
  /// [LiveTestWidgetsFlutterBinding] (tests running using `flutter run`).
282
  ///
283 284
  /// This is the maximum that the timeout controlled by `initialTimeout` on
  /// [testWidgets] can reach when augmented using [addTime].
285
  test_package.Timeout get defaultTestTimeout;
286

287 288 289 290 291 292 293 294 295 296
  /// The current time.
  ///
  /// In the automated test environment (`flutter test`), this is a fake clock
  /// that begins in January 2015 at the start of the test and advances each
  /// time [pump] is called with a non-zero duration.
  ///
  /// In the live testing environment (`flutter run`), this object shows the
  /// actual current wall-clock time.
  Clock get clock;

297 298 299 300 301 302 303 304
  /// Triggers a frame sequence (build/layout/paint/etc),
  /// then flushes microtasks.
  ///
  /// If duration is set, then advances the clock by that much first.
  /// Doing this flushes microtasks.
  ///
  /// The supplied EnginePhase is the final phase reached during the pump pass;
  /// if not supplied, the whole pass is executed.
305 306 307
  ///
  /// See also [LiveTestWidgetsFlutterBindingFramePolicy], which affects how
  /// this method works when the test is run with `flutter run`.
308
  Future<void> pump([ Duration duration, EnginePhase newPhase = EnginePhase.sendSemanticsUpdate ]);
309

310
  /// Runs a `callback` that performs real asynchronous work.
311 312 313 314 315
  ///
  /// This is intended for callers who need to call asynchronous methods where
  /// the methods spawn isolates or OS threads and thus cannot be executed
  /// synchronously by calling [pump].
  ///
316 317
  /// If `callback` completes successfully, this will return the future
  /// returned by `callback`.
318
  ///
319
  /// If `callback` completes with an error, the error will be caught by the
320 321 322 323 324 325 326
  /// Flutter framework and made available via [takeException], and this method
  /// will return a future that completes will `null`.
  ///
  /// Re-entrant calls to this method are not allowed; callers of this method
  /// are required to wait for the returned future to complete before calling
  /// this method again. Attempts to do otherwise will result in a
  /// [TestFailure] error being thrown.
327 328 329
  ///
  /// The `additionalTime` argument is used by the
  /// [AutomatedTestWidgetsFlutterBinding] implementation to increase the
330 331
  /// current timeout, if any. See [AutomatedTestWidgetsFlutterBinding.addTime]
  /// for details.
332 333
  Future<T> runAsync<T>(
    Future<T> callback(), {
334
    Duration additionalTime = const Duration(milliseconds: 1000),
335
  });
336

337
  /// Artificially calls dispatchLocalesChanged on the Widget binding,
338
  /// then flushes microtasks.
339 340 341
  ///
  /// Passes only one single Locale. Use [setLocales] to pass a full preferred
  /// locales list.
342 343
  Future<void> setLocale(String languageCode, String countryCode) {
    return TestAsyncUtils.guard<void>(() async {
344
      assert(inTest);
345
      final Locale locale = Locale(languageCode, countryCode == '' ? null : countryCode);
346 347 348
      if (isBrowser) {
        return;
      }
349 350 351 352 353 354 355 356 357 358
      dispatchLocalesChanged(<Locale>[locale]);
    });
  }

  /// Artificially calls dispatchLocalesChanged on the Widget binding,
  /// then flushes microtasks.
  Future<void> setLocales(List<Locale> locales) {
    return TestAsyncUtils.guard<void>(() async {
      assert(inTest);
      dispatchLocalesChanged(locales);
359 360 361
    });
  }

362 363 364 365 366 367
  /// Re-attempts the initialization of the lifecycle state after providing
  /// test values in [TestWindow.initialLifecycleStateTestValue].
  void readTestInitialLifecycleStateFromNativeWindow() {
    readInitialLifecycleStateFromNativeWindow();
  }

368 369 370 371 372 373
  Size _surfaceSize;

  /// Artificially changes the surface size to `size` on the Widget binding,
  /// then flushes microtasks.
  ///
  /// Set to null to use the default surface size.
374 375
  Future<void> setSurfaceSize(Size size) {
    return TestAsyncUtils.guard<void>(() async {
376 377
      assert(inTest);
      if (_surfaceSize == size)
378
        return;
379 380 381 382 383 384 385
      _surfaceSize = size;
      handleMetricsChanged();
    });
  }

  @override
  ViewConfiguration createViewConfiguration() {
386 387
    final double devicePixelRatio = window.devicePixelRatio;
    final Size size = _surfaceSize ?? window.physicalSize / devicePixelRatio;
388
    return ViewConfiguration(
389 390 391 392 393
      size: size,
      devicePixelRatio: devicePixelRatio,
    );
  }

394 395 396
  /// Acts as if the application went idle.
  ///
  /// Runs all remaining microtasks, including those scheduled as a result of
397 398
  /// running them, until there are no more microtasks scheduled. Then, runs any
  /// previously scheduled timers with zero time, and completes the returned future.
399
  ///
400 401 402
  /// May result in an infinite loop or run out of memory if microtasks continue
  /// to recursively schedule new microtasks. Will not run any timers scheduled
  /// after this method was invoked, even if they are zero-time timers.
403 404 405
  Future<void> idle() {
    return TestAsyncUtils.guard<void>(() {
      final Completer<void> completer = Completer<void>();
406
      Timer.run(() {
407
        completer.complete();
408 409 410
      });
      return completer.future;
    });
411 412
  }

413
  /// Convert the given point from the global coordinate system (as used by
414 415
  /// pointer events from the device) to the coordinate system used by the
  /// tests (an 800 by 600 window).
416
  Offset globalToLocal(Offset point) => point;
417 418

  /// Convert the given point from the coordinate system used by the tests (an
419
  /// 800 by 600 window) to the global coordinate system (as used by pointer
420
  /// events from the device).
421
  Offset localToGlobal(Offset point) => point;
422

423
  @override
424 425 426
  void dispatchEvent(
    PointerEvent event,
    HitTestResult hitTestResult, {
427
    TestBindingEventSource source = TestBindingEventSource.device,
428 429
  }) {
    assert(source == TestBindingEventSource.test);
430
    super.dispatchEvent(event, hitTestResult);
431 432
  }

433 434 435 436 437 438 439 440
  /// A stub for the system's onscreen keyboard. Callers must set the
  /// [focusedEditable] before using this value.
  TestTextInput get testTextInput => _testTextInput;
  TestTextInput _testTextInput;

  /// The current client of the onscreen keyboard. Callers must pump
  /// an additional frame after setting this property to complete the
  /// the focus change.
441 442 443
  ///
  /// Instead of setting this directly, consider using
  /// [WidgetTester.showKeyboard].
444 445
  EditableTextState get focusedEditable => _focusedEditable;
  EditableTextState _focusedEditable;
446
  set focusedEditable(EditableTextState value) {
447 448 449 450 451 452 453 454
    if (_focusedEditable != value) {
      _focusedEditable = value;
      value?.requestKeyboard();
    }
  }

  void _resetFocusedEditable() {
    _focusedEditable = null;
455 456
  }

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
  /// Returns the exception most recently caught by the Flutter framework.
  ///
  /// Call this if you expect an exception during a test. If an exception is
  /// thrown and this is not called, then the exception is rethrown when
  /// the [testWidgets] call completes.
  ///
  /// If two exceptions are thrown in a row without the first one being
  /// acknowledged with a call to this method, then when the second exception is
  /// thrown, they are both dumped to the console and then the second is
  /// rethrown from the exception handler. This will likely result in the
  /// framework entering a highly unstable state and everything collapsing.
  ///
  /// It's safe to call this when there's no pending exception; it will return
  /// null in that case.
  dynamic takeException() {
472
    assert(inTest);
473
    final dynamic result = _pendingExceptionDetails?.exception;
474
    _pendingExceptionDetails = null;
475 476
    return result;
  }
477 478
  FlutterExceptionHandler _oldExceptionHandler;
  FlutterErrorDetails _pendingExceptionDetails;
479

480 481
  static const TextStyle _messageStyle = TextStyle(
    color: Color(0xFF917FFF),
Ian Hickson's avatar
Ian Hickson committed
482
    fontSize: 40.0,
483 484
  );

485 486
  static const Widget _preTestMessage = Center(
    child: Text(
487
      'Test starting...',
488
      style: _messageStyle,
Ian Hickson's avatar
Ian Hickson committed
489
      textDirection: TextDirection.ltr,
490
    ),
491 492
  );

493 494
  static const Widget _postTestMessage = Center(
    child: Text(
495
      'Test finished.',
496
      style: _messageStyle,
Ian Hickson's avatar
Ian Hickson committed
497
      textDirection: TextDirection.ltr,
498
    ),
499 500 501 502 503 504
  );

  /// Whether to include the output of debugDumpApp() when reporting
  /// test failures.
  bool showAppDumpInErrors = false;

505
  /// Call the testBody inside a [FakeAsync] scope on which [pump] can
506 507 508 509 510 511
  /// advance time.
  ///
  /// Returns a future which completes when the test has run.
  ///
  /// Called by the [testWidgets] and [benchmarkWidgets] functions to
  /// run a test.
512 513 514
  ///
  /// The `invariantTester` argument is called after the `testBody`'s [Future]
  /// completes. If it throws, then the test is marked as failed.
515 516 517 518
  ///
  /// The `description` is used by the [LiveTestWidgetsFlutterBinding] to
  /// show a label on the screen during the test. The description comes from
  /// the value passed to [testWidgets]. It must not be null.
519 520 521 522
  ///
  /// The `timeout` argument sets the initial timeout, if any. It can
  /// be increased with [addTime]. By default there is no timeout.
  Future<void> runTest(Future<void> testBody(), VoidCallback invariantTester, { String description = '', Duration timeout });
523 524 525 526 527 528 529 530 531 532 533

  /// This is called during test execution before and after the body has been
  /// executed.
  ///
  /// It's used by [AutomatedTestWidgetsFlutterBinding] to drain the microtasks
  /// before the final [pump] that happens during test cleanup.
  void asyncBarrier() {
    TestAsyncUtils.verifyAllScopesClosed();
  }

  Zone _parentZone;
534

535
  VoidCallback _createTestCompletionHandler(String testDescription, Completer<void> completer) {
536 537 538 539 540 541 542 543 544 545
    return () {
      // This can get called twice, in the case of a Future without listeners failing, and then
      // our main future completing.
      assert(Zone.current == _parentZone);
      if (_pendingExceptionDetails != null) {
        debugPrint = debugPrintOverride; // just in case the test overrides it -- otherwise we won't see the error!
        reportTestException(_pendingExceptionDetails, testDescription);
        _pendingExceptionDetails = null;
      }
      if (!completer.isCompleted)
546
        completer.complete();
547
    };
548 549
  }

550 551 552 553 554 555 556 557 558 559 560 561
  /// Called when the framework catches an exception, even if that exception is
  /// being handled by [takeException].
  ///
  /// This is called when there is no pending exception; if multiple exceptions
  /// are thrown and [takeException] isn't used, then subsequent exceptions are
  /// logged to the console regardless (and the test will fail).
  @protected
  void reportExceptionNoticed(FlutterErrorDetails exception) {
    // By default we do nothing.
    // The LiveTestWidgetsFlutterBinding overrides this to report the exception to the console.
  }

562 563 564 565
  Future<void> _runTest(
    Future<void> testBody(),
    VoidCallback invariantTester,
    String description, {
566
    Future<void> timeout,
567
  }) {
568
    assert(description != null);
569 570 571
    assert(inTest);
    _oldExceptionHandler = FlutterError.onError;
    int _exceptionCount = 0; // number of un-taken exceptions
572
    FlutterError.onError = (FlutterErrorDetails details) {
573
      if (_pendingExceptionDetails != null) {
574
        debugPrint = debugPrintOverride; // just in case the test overrides it -- otherwise we won't see the errors!
575 576
        if (_exceptionCount == 0) {
          _exceptionCount = 2;
577
          FlutterError.dumpErrorToConsole(_pendingExceptionDetails, forceReport: true);
578 579
        } else {
          _exceptionCount += 1;
580
        }
581
        FlutterError.dumpErrorToConsole(details, forceReport: true);
582
        _pendingExceptionDetails = FlutterErrorDetails(
583
          exception: 'Multiple exceptions ($_exceptionCount) were detected during the running of the current test, and at least one was unexpected.',
584
          library: 'Flutter test framework',
585 586
        );
      } else {
587
        reportExceptionNoticed(details); // mostly this is just a hook for the LiveTestWidgetsFlutterBinding
588
        _pendingExceptionDetails = details;
589
      }
590
    };
591
    final Completer<void> testCompleter = Completer<void>();
592
    final VoidCallback testCompletionHandler = _createTestCompletionHandler(description, testCompleter);
593 594 595 596 597 598 599 600 601
    void handleUncaughtError(dynamic exception, StackTrace stack) {
      if (testCompleter.isCompleted) {
        // Well this is not a good sign.
        // Ideally, once the test has failed we would stop getting errors from the test.
        // However, if someone tries hard enough they could get in a state where this happens.
        // If we silently dropped these errors on the ground, nobody would ever know. So instead
        // we report them to the console. They don't cause test failures, but hopefully someone
        // will see them in the logs at some point.
        debugPrint = debugPrintOverride; // just in case the test overrides it -- otherwise we won't see the error!
602
        FlutterError.dumpErrorToConsole(FlutterErrorDetails(
603
          exception: exception,
604
          stack: _unmangle(stack),
605
          context: ErrorDescription('running a test (but after the test had completed)'),
606
          library: 'Flutter test framework',
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
        ), forceReport: true);
        return;
      }
      // This is where test failures, e.g. those in expect(), will end up.
      // Specifically, runUnaryGuarded() will call this synchronously and
      // return our return value if _runTestBody fails synchronously (which it
      // won't, so this never happens), and Future will call this when the
      // Future completes with an error and it would otherwise call listeners
      // if the listener is in a different zone (which it would be for the
      // `whenComplete` handler below), or if the Future completes with an
      // error and the future has no listeners at all.
      //
      // This handler further calls the onError handler above, which sets
      // _pendingExceptionDetails. Nothing gets printed as a result of that
      // call unless we already had an exception pending, because in general
      // we want people to be able to cause the framework to report exceptions
      // and then use takeException to verify that they were really caught.
      // Now, if we actually get here, this isn't going to be one of those
      // cases. We only get here if the test has actually failed. So, once
      // we've carefully reported it, we then immediately end the test by
      // calling the testCompletionHandler in the _parentZone.
      //
      // We have to manually call testCompletionHandler because if the Future
      // library calls us, it is maybe _instead_ of calling a registered
      // listener from a different zone. In our case, that would be instead of
      // calling the whenComplete() listener below.
      //
      // We have to call it in the parent zone because if we called it in
      // _this_ zone, the test framework would find this zone was the current
      // zone and helpfully throw the error in this zone, causing us to be
      // directly called again.
638
      DiagnosticsNode treeDump;
639
      try {
640 641 642 643
        treeDump = renderViewElement?.toDiagnosticsNode() ?? DiagnosticsNode.message('<no tree>');
        // TODO(jacobr): this is a hack to make sure the tree can safely be fully dumped.
        // Potentially everything is good enough without this case.
        treeDump.toStringDeep();
644
      } catch (exception) {
645
        treeDump = DiagnosticsNode.message('<additional error caught while dumping tree: $exception>', level: DiagnosticLevel.error);
646
      }
647 648
      final List<DiagnosticsNode> omittedFrames = <DiagnosticsNode>[];
      final int stackLinesToOmit = reportExpectCall(stack, omittedFrames);
649
      FlutterError.reportError(FlutterErrorDetails(
650 651
        exception: exception,
        stack: _unmangle(stack),
652
        context: ErrorDescription('running a test'),
653 654 655 656
        library: 'Flutter test framework',
        stackFilter: (Iterable<String> frames) {
          return FlutterError.defaultStackFilter(frames.skip(stackLinesToOmit));
        },
657
        informationCollector: () sync* {
658
          if (stackLinesToOmit > 0)
659
            yield* omittedFrames;
660
          if (showAppDumpInErrors) {
661
            yield DiagnosticsProperty<DiagnosticsNode>('At the time of the failure, the widget tree looked as follows', treeDump, linePrefix: '# ', style: DiagnosticsTreeStyle.flat);
662
          }
663
          if (description.isNotEmpty)
664
            yield DiagnosticsProperty<String>('The test description was', description, style: DiagnosticsTreeStyle.errorProperty);
665
        },
666 667 668 669 670
      ));
      assert(_parentZone != null);
      assert(_pendingExceptionDetails != null, '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.');
      _parentZone.run<void>(testCompletionHandler);
    }
671
    final ZoneSpecification errorHandlingZoneSpecification = ZoneSpecification(
672 673
      handleUncaughtError: (Zone self, ZoneDelegate parent, Zone zone, dynamic exception, StackTrace stack) {
        handleUncaughtError(exception, stack);
674 675 676
      }
    );
    _parentZone = Zone.current;
677
    final Zone testZone = _parentZone.fork(specification: errorHandlingZoneSpecification);
678
    testZone.runBinary<Future<void>, Future<void> Function(), VoidCallback>(_runTestBody, testBody, invariantTester)
679
      .whenComplete(testCompletionHandler);
680
    timeout?.catchError(handleUncaughtError);
681
    return testCompleter.future;
682 683
  }

684
  Future<void> _runTestBody(Future<void> testBody(), VoidCallback invariantTester) async {
685 686
    assert(inTest);

687
    runApp(Container(key: UniqueKey(), child: _preTestMessage)); // Reset the tree to a known state.
688 689
    await pump();

690
    final bool autoUpdateGoldensBeforeTest = autoUpdateGoldenFiles && !isBrowser;
691
    final TestExceptionReporter reportTestExceptionBeforeTest = reportTestException;
692
    final ErrorWidgetBuilder errorWidgetBuilderBeforeTest = ErrorWidget.builder;
693

694
    // run the test
695
    await testBody();
696 697 698 699 700 701
    asyncBarrier(); // drains the microtasks in `flutter test` mode (when using AutomatedTestWidgetsFlutterBinding)

    if (_pendingExceptionDetails == null) {
      // We only try to clean up and verify invariants if we didn't already
      // fail. If we got an exception already, then we instead leave everything
      // alone so that we don't cause more spurious errors.
702
      runApp(Container(key: UniqueKey(), child: _postTestMessage)); // Unmount any remaining widgets.
703
      await pump();
704
      invariantTester();
705
      _verifyAutoUpdateGoldensUnset(autoUpdateGoldensBeforeTest && !isBrowser);
706
      _verifyReportTestExceptionUnset(reportTestExceptionBeforeTest);
707
      _verifyErrorWidgetBuilderUnset(errorWidgetBuilderBeforeTest);
708 709 710 711
      _verifyInvariants();
    }

    assert(inTest);
712
    asyncBarrier(); // When using AutomatedTestWidgetsFlutterBinding, this flushes the microtasks.
713 714 715 716 717 718
  }

  void _verifyInvariants() {
    assert(debugAssertNoTransientCallbacks(
      'An animation is still running even after the widget tree was disposed.'
    ));
719 720 721 722
    assert(debugAssertAllFoundationVarsUnset(
      'The value of a foundation debug variable was changed by the test.',
      debugPrintOverride: debugPrintOverride,
    ));
723 724 725
    assert(debugAssertAllGesturesVarsUnset(
      'The value of a gestures debug variable was changed by the test.',
    ));
726 727 728 729
    assert(debugAssertAllPaintingVarsUnset(
      'The value of a painting debug variable was changed by the test.',
      debugDisableShadowsOverride: disableShadows,
    ));
730
    assert(debugAssertAllRenderVarsUnset(
731 732
      'The value of a rendering debug variable was changed by the test.',
      debugCheckIntrinsicSizesOverride: checkIntrinsicSizes,
733 734
    ));
    assert(debugAssertAllWidgetVarsUnset(
735
      'The value of a widget debug variable was changed by the test.',
736 737
    ));
    assert(debugAssertAllSchedulerVarsUnset(
738
      'The value of a scheduler debug variable was changed by the test.',
739
    ));
740 741
  }

742 743 744
  void _verifyAutoUpdateGoldensUnset(bool valueBeforeTest) {
    assert(() {
      if (autoUpdateGoldenFiles != valueBeforeTest) {
745 746
        FlutterError.reportError(FlutterErrorDetails(
          exception: FlutterError(
747 748 749 750 751 752 753 754 755 756
              'The value of autoUpdateGoldenFiles was changed by the test.',
          ),
          stack: StackTrace.current,
          library: 'Flutter test framework',
        ));
      }
      return true;
    }());
  }

757 758 759 760 761 762 763 764
  void _verifyReportTestExceptionUnset(TestExceptionReporter valueBeforeTest) {
    assert(() {
      if (reportTestException != valueBeforeTest) {
        // We can't report this error to their modified reporter because we
        // can't be guaranteed that their reporter will cause the test to fail.
        // So we reset the error reporter to its initial value and then report
        // this error.
        reportTestException = valueBeforeTest;
765 766
        FlutterError.reportError(FlutterErrorDetails(
          exception: FlutterError(
767 768 769 770 771 772 773 774 775 776
            'The value of reportTestException was changed by the test.',
          ),
          stack: StackTrace.current,
          library: 'Flutter test framework',
        ));
      }
      return true;
    }());
  }

777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
  void _verifyErrorWidgetBuilderUnset(ErrorWidgetBuilder valueBeforeTest) {
    assert(() {
      if (ErrorWidget.builder != valueBeforeTest) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: FlutterError(
              'The value of ErrorWidget.builder was changed by the test.',
          ),
          stack: StackTrace.current,
          library: 'Flutter test framework',
        ));
      }
      return true;
    }());
  }

792 793 794 795 796 797
  /// Called by the [testWidgets] function after a test is executed.
  void postTest() {
    assert(inTest);
    FlutterError.onError = _oldExceptionHandler;
    _pendingExceptionDetails = null;
    _parentZone = null;
798
    buildOwner.focusManager = FocusManager();
799 800 801 802
    assert(!RendererBinding.instance.mouseTracker.mouseIsConnected,
        'The MouseTracker thinks that there is still a mouse connected, which indicates that a '
        'test has not removed the mouse pointer which it added. Call removePointer on the '
        'active mouse gesture to remove the mouse pointer.');
803 804 805 806 807 808 809 810
  }
}

/// A variant of [TestWidgetsFlutterBinding] for executing tests in
/// the `flutter test` environment.
///
/// This binding controls time, allowing tests to verify long
/// animation sequences without having to execute them in real time.
811 812 813
///
/// This class assumes it is always run in checked mode (since tests are always
/// run in checked mode).
814 815 816 817
class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
  @override
  void initInstances() {
    super.initInstances();
818
    binding.mockFlutterAssets();
819 820
  }

821
  FakeAsync _currentFakeAsync; // set in runTest; cleared in postTest
822
  Completer<void> _pendingAsyncTasks;
823 824 825

  @override
  Clock get clock => _clock;
826 827
  Clock _clock;

828 829 830
  @override
  DebugPrintCallback get debugPrintOverride => debugPrintSynchronously;

831 832 833
  @override
  bool get disableShadows => true;

834 835 836
  @override
  bool get checkIntrinsicSizes => true;

837
  @override
838
  test_package.Timeout get defaultTestTimeout => const test_package.Timeout(Duration(minutes: 10));
839 840

  @override
841
  bool get inTest => _currentFakeAsync != null;
842

843
  @override
844
  int get microtaskCount => _currentFakeAsync.microtaskCount;
845

846
  @override
847 848
  Future<void> pump([ Duration duration, EnginePhase newPhase = EnginePhase.sendSemanticsUpdate ]) {
    return TestAsyncUtils.guard<void>(() {
849 850 851
      assert(inTest);
      assert(_clock != null);
      if (duration != null)
852
        _currentFakeAsync.elapse(duration);
853 854
      _phase = newPhase;
      if (hasScheduledFrame) {
855
        addTime(const Duration(milliseconds: 500));
856
        _currentFakeAsync.flushMicrotasks();
857
        handleBeginFrame(Duration(
858
          milliseconds: _clock.now().millisecondsSinceEpoch,
859
        ));
860
        _currentFakeAsync.flushMicrotasks();
861
        handleDrawFrame();
862
      }
863
      _currentFakeAsync.flushMicrotasks();
864
      return Future<void>.value();
865 866 867
    });
  }

868
  @override
869 870
  Future<T> runAsync<T>(
    Future<T> callback(), {
871
    Duration additionalTime = const Duration(milliseconds: 1000),
872 873
  }) {
    assert(additionalTime != null);
874 875 876
    assert(() {
      if (_pendingAsyncTasks == null)
        return true;
877
      throw test_package.TestFailure(
878 879 880 881 882 883 884
          'Reentrant call to runAsync() denied.\n'
          'runAsync() was called, then before its future completed, it '
          'was called again. You must wait for the first returned future '
          'to complete before calling runAsync() again.'
      );
    }());

885
    final Zone realAsyncZone = Zone.current.fork(
886
      specification: ZoneSpecification(
887 888 889 890 891 892 893 894 895 896 897 898
        scheduleMicrotask: (Zone self, ZoneDelegate parent, Zone zone, void f()) {
          Zone.root.scheduleMicrotask(f);
        },
        createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f()) {
          return Zone.root.createTimer(duration, f);
        },
        createPeriodicTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration period, void f(Timer timer)) {
          return Zone.root.createPeriodicTimer(period, f);
        },
      ),
    );

899 900
    addTime(additionalTime);

901
    return realAsyncZone.run<Future<T>>(() {
902
      _pendingAsyncTasks = Completer<void>();
903
      return callback().catchError((dynamic exception, StackTrace stack) {
904
        FlutterError.reportError(FlutterErrorDetails(
905 906 907
          exception: exception,
          stack: stack,
          library: 'Flutter test framework',
908
          context: ErrorDescription('while running async test code'),
909 910 911 912 913 914 915 916 917 918 919 920 921 922
        ));
        return null;
      }).whenComplete(() {
        // We complete the _pendingAsyncTasks future successfully regardless of
        // whether an exception occurred because in the case of an exception,
        // we already reported the exception to FlutterError. Moreover,
        // completing the future with an error would trigger an unhandled
        // exception due to zone error boundaries.
        _pendingAsyncTasks.complete();
        _pendingAsyncTasks = null;
      });
    });
  }

923 924 925 926 927 928 929
  @override
  void ensureFrameCallbacksRegistered() {
    // Leave Window alone, do nothing.
    assert(window.onDrawFrame == null);
    assert(window.onBeginFrame == null);
  }

930 931 932 933 934
  @override
  void scheduleWarmUpFrame() {
    // We override the default version of this so that the application-startup warm-up frame
    // does not schedule timers which we might never get around to running.
    handleBeginFrame(null);
935
    _currentFakeAsync.flushMicrotasks();
936
    handleDrawFrame();
937
    _currentFakeAsync.flushMicrotasks();
938 939
  }

940 941 942 943 944 945 946 947
  @override
  void scheduleAttachRootWidget(Widget rootWidget) {
    // We override the default version of this so that the application-startup widget tree
    // build does not schedule timers which we might never get around to running.
    attachRootWidget(rootWidget);
    _currentFakeAsync.flushMicrotasks();
  }

948
  @override
949 950
  Future<void> idle() {
    final Future<void> result = super.idle();
951
    _currentFakeAsync.elapse(Duration.zero);
952 953 954
    return result;
  }

955
  EnginePhase _phase = EnginePhase.sendSemanticsUpdate;
956

957
  // Cloned from RendererBinding.drawFrame() but with early-exit semantics.
958
  @override
959
  void drawFrame() {
960
    assert(inTest);
961 962 963
    try {
      debugBuildingDirtyElements = true;
      buildOwner.buildScope(renderViewElement);
964 965 966 967 968 969 970 971 972 973 974
      if (_phase != EnginePhase.build) {
        assert(renderView != null);
        pipelineOwner.flushLayout();
        if (_phase != EnginePhase.layout) {
          pipelineOwner.flushCompositingBits();
          if (_phase != EnginePhase.compositingBits) {
            pipelineOwner.flushPaint();
            if (_phase != EnginePhase.paint) {
              renderView.compositeFrame(); // this sends the bits to the GPU
              if (_phase != EnginePhase.composite) {
                pipelineOwner.flushSemantics();
975 976
                assert(_phase == EnginePhase.flushSemantics ||
                       _phase == EnginePhase.sendSemanticsUpdate);
977 978 979 980 981
              }
            }
          }
        }
      }
982
      buildOwner.finalizeTree();
983
    } finally {
984 985
      debugBuildingDirtyElements = false;
    }
986 987
  }

988 989 990
  Duration _timeout;
  Stopwatch _timeoutStopwatch;
  Timer _timeoutTimer;
991
  Completer<void> _timeoutCompleter;
992 993 994

  void _checkTimeout(Timer timer) {
    assert(_timeoutTimer == timer);
995
    assert(_timeout != null);
996 997
    if (_timeoutStopwatch.elapsed > _timeout) {
      _timeoutCompleter.completeError(
998
        TimeoutException(
999
          'The test exceeded the timeout. It may have hung.\n'
1000
          'Consider using "tester.binding.addTime" to increase the timeout before expensive operations.',
1001 1002 1003 1004 1005 1006
          _timeout,
        ),
      );
    }
  }

1007
  @override
1008
  void addTime(Duration duration) {
1009 1010
    if (_timeout != null)
      _timeout += duration;
1011 1012
  }

1013
  @override
1014 1015 1016
  Future<void> runTest(
    Future<void> testBody(),
    VoidCallback invariantTester, {
1017
    String description = '',
1018
    Duration timeout,
1019
  }) {
1020
    assert(description != null);
1021
    assert(!inTest);
1022
    assert(_currentFakeAsync == null);
1023
    assert(_clock == null);
1024 1025

    _timeout = timeout;
1026 1027 1028 1029 1030
    if (_timeout != null) {
      _timeoutStopwatch = Stopwatch()..start();
      _timeoutTimer = Timer.periodic(const Duration(seconds: 1), _checkTimeout);
      _timeoutCompleter = Completer<void>();
    }
1031

1032
    final FakeAsync fakeAsync = FakeAsync();
1033
    _currentFakeAsync = fakeAsync; // reset in postTest
1034
    _clock = fakeAsync.getClock(DateTime.utc(2015, 1, 1));
1035
    Future<void> testBodyResult;
1036 1037 1038
    fakeAsync.run((FakeAsync localFakeAsync) {
      assert(fakeAsync == _currentFakeAsync);
      assert(fakeAsync == localFakeAsync);
1039
      testBodyResult = _runTest(testBody, invariantTester, description, timeout: _timeoutCompleter?.future);
1040 1041
      assert(inTest);
    });
1042

1043
    return Future<void>.microtask(() async {
1044 1045 1046 1047 1048 1049
      // testBodyResult is a Future that was created in the Zone of the
      // fakeAsync. This means that if we await it here, it will register a
      // microtask to handle the future _in the fake async zone_. We avoid this
      // by calling '.then' in the current zone. While flushing the microtasks
      // of the fake-zone below, the new future will be completed and can then
      // be used without fakeAsync.
1050
      final Future<void> resultFuture = testBodyResult.then<void>((_) {
1051 1052 1053
        // Do nothing.
      });

1054
      // Resolve interplay between fake async and real async calls.
1055
      fakeAsync.flushMicrotasks();
1056 1057
      while (_pendingAsyncTasks != null) {
        await _pendingAsyncTasks.future;
1058
        fakeAsync.flushMicrotasks();
1059
      }
1060
      return resultFuture;
1061
    });
1062 1063
  }

1064 1065
  @override
  void asyncBarrier() {
1066 1067
    assert(_currentFakeAsync != null);
    _currentFakeAsync.flushMicrotasks();
1068 1069
    super.asyncBarrier();
  }
1070

1071 1072 1073
  @override
  void _verifyInvariants() {
    super._verifyInvariants();
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096

    assert(() {
      if (   _currentFakeAsync.periodicTimerCount == 0
          && _currentFakeAsync.nonPeriodicTimerCount == 0) {
        return true;
      }

      debugPrint('Pending timers:');
      for (String timerInfo in _currentFakeAsync.pendingTimersDebugInfo) {
        final int firstLineEnd = timerInfo.indexOf('\n');
        assert(firstLineEnd != -1);

        // No need to include the newline.
        final String firstLine = timerInfo.substring(0, firstLineEnd);
        final String stackTrace = timerInfo.substring(firstLineEnd + 1);

        debugPrint(firstLine);
        debugPrintStack(stackTrace: StackTrace.fromString(stackTrace));
        debugPrint('');
      }
      return false;
    }(), 'A Timer is still pending even after the widget tree was disposed.');

1097
    assert(_currentFakeAsync.microtaskCount == 0); // Shouldn't be possible.
1098 1099
  }

1100
  @override
1101
  void postTest() {
1102
    super.postTest();
1103
    assert(_currentFakeAsync != null);
1104 1105
    assert(_clock != null);
    _clock = null;
1106
    _currentFakeAsync = null;
1107
    _timeoutCompleter = null;
1108
    _timeoutTimer?.cancel();
1109 1110 1111
    _timeoutTimer = null;
    _timeoutStopwatch = null;
    _timeout = null;
1112 1113
  }

1114
}
1115

1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
/// Available policies for how a [LiveTestWidgetsFlutterBinding] should paint
/// frames.
///
/// These values are set on the binding's
/// [LiveTestWidgetsFlutterBinding.framePolicy] property. The default is
/// [fadePointers].
enum LiveTestWidgetsFlutterBindingFramePolicy {
  /// Strictly show only frames that are explicitly pumped. This most closely
  /// matches the behavior of tests when run under `flutter test`.
  onlyPumps,

  /// Show pumped frames, and additionally schedule and run frames to fade
  /// out the pointer crosshairs and other debugging information shown by
  /// the binding.
  ///
  /// This can result in additional frames being pumped beyond those that
  /// the test itself requests, which can cause differences in behavior.
  fadePointers,

  /// Show every frame that the framework requests, even if the frames are not
  /// explicitly pumped.
  ///
  /// This can help with orienting the developer when looking at
  /// heavily-animated situations, and will almost certainly result in
  /// additional frames being pumped beyond those that the test itself requests,
  /// which can cause differences in behavior.
  fullyLive,
1143 1144 1145 1146 1147 1148

  /// Ignore any request to schedule a frame.
  ///
  /// This is intended to be used by benchmarks (hence the name) that drive the
  /// pipeline directly. It tells the binding to entirely ignore requests for a
  /// frame to be scheduled, while still allowing frames that are pumped
1149 1150
  /// directly to run (either by using [WidgetTester.pumpBenchmark] or invoking
  /// [Window.onBeginFrame] and [Window.onDrawFrame]).
1151 1152 1153 1154 1155 1156 1157
  ///
  /// The [SchedulerBinding.hasScheduledFrame] property will never be true in
  /// this mode. This can cause unexpected effects. For instance,
  /// [WidgetTester.pumpAndSettle] does not function in this mode, as it relies
  /// on the [SchedulerBinding.hasScheduledFrame] property to determine when the
  /// application has "settled".
  benchmark,
1158 1159
}

1160 1161 1162 1163 1164 1165 1166 1167
/// A variant of [TestWidgetsFlutterBinding] for executing tests in
/// the `flutter run` environment, on a device. This is intended to
/// allow interactive test development.
///
/// This is not the way to run a remote-control test. To run a test on
/// a device from a development computer, see the [flutter_driver]
/// package and the `flutter drive` command.
///
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
/// When running tests using `flutter run`, consider adding the
/// `--use-test-fonts` argument so that the fonts used match those used under
/// `flutter test`. (This forces all text to use the "Ahem" font, which is a
/// font that covers ASCII characters and gives them all the appearance of a
/// square whose size equals the font size.)
///
/// This binding overrides the default [SchedulerBinding] behavior to ensure
/// that tests work in the same way in this environment as they would under the
/// [AutomatedTestWidgetsFlutterBinding]. To override this (and see intermediate
/// frames that the test does not explicitly trigger), set [framePolicy] to
/// [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive]. (This is likely to
/// make tests fail, though, especially if e.g. they test how many times a
/// particular widget was built.) The default behavior is to show pumped frames
/// and a few additional frames when pointers are triggered (to animate the
/// pointer crosshairs).
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192
///
/// This binding does not support the [EnginePhase] argument to
/// [pump]. (There would be no point setting it to a value that
/// doesn't trigger a paint, since then you could not see anything
/// anyway.)
class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
  @override
  bool get inTest => _inTest;
  bool _inTest = false;

1193 1194 1195
  @override
  Clock get clock => const Clock();

1196 1197
  @override
  int get microtaskCount {
1198 1199
    // The Dart SDK doesn't report this number.
    assert(false, 'microtaskCount cannot be reported when running in real time');
1200 1201 1202
    return -1;
  }

1203 1204 1205
  @override
  test_package.Timeout get defaultTestTimeout => test_package.Timeout.none;

1206
  Completer<void> _pendingFrame;
1207
  bool _expectingFrame = false;
1208
  bool _viewNeedsPaint = false;
1209
  bool _runningAsyncTasks = false;
1210 1211 1212 1213 1214 1215 1216 1217

  /// Whether to have [pump] with a duration only pump a single frame
  /// (as would happen in a normal test environment using
  /// [AutomatedTestWidgetsFlutterBinding]), or whether to instead
  /// pump every frame that the system requests during any
  /// asynchronous pause in the test (as would normally happen when
  /// running an application with [WidgetsFlutterBinding]).
  ///
1218 1219 1220 1221 1222 1223 1224 1225
  /// * [LiveTestWidgetsFlutterBindingFramePolicy.fadePointers] is the default
  ///   behavior, which is to only pump once, except when there has been some
  ///   activity with [TestPointer]s, in which case those are shown and may pump
  ///   additional frames.
  ///
  /// * [LiveTestWidgetsFlutterBindingFramePolicy.onlyPumps] is the strictest
  ///   behavior, which is to only pump once. This most closely matches the
  ///   [AutomatedTestWidgetsFlutterBinding] (`flutter test`) behavior.
1226
  ///
1227 1228 1229
  /// * [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive] allows all frame
  ///   requests from the engine to be serviced, even those the test did not
  ///   explicitly pump.
1230
  ///
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
  /// * [LiveTestWidgetsFlutterBindingFramePolicy.benchmark] allows all frame
  ///   requests from the engine to be serviced, and allows all frame requests
  ///   that are artificially triggered to be serviced, but prevents the
  ///   framework from requesting any frames from the engine itself. The
  ///   [SchedulerBinding.hasScheduledFrame] property will never be true in this
  ///   mode. This can cause unexpected effects. For instance,
  ///   [WidgetTester.pumpAndSettle] does not function in this mode, as it
  ///   relies on the [SchedulerBinding.hasScheduledFrame] property to determine
  ///   when the application has "settled".
  ///
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
  /// Setting this to anything other than
  /// [LiveTestWidgetsFlutterBindingFramePolicy.onlyPumps] means pumping extra
  /// frames, which might involve calling builders more, or calling paint
  /// callbacks more, etc, which might interfere with the test. If you know your
  /// test file wouldn't be affected by this, you can set it to
  /// [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive] persistently in that
  /// particular test file. To set this to
  /// [LiveTestWidgetsFlutterBindingFramePolicy.fullyLive] while still allowing
  /// the test file to work as a normal test, add the following code to your
  /// test file at the top of your `void main() { }` function, before calls to
  /// [testWidgets]:
1252 1253 1254 1255
  ///
  /// ```dart
  /// TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
  /// if (binding is LiveTestWidgetsFlutterBinding)
1256
  ///   binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
1257
  /// ```
1258
  LiveTestWidgetsFlutterBindingFramePolicy framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fadePointers;
1259

1260 1261 1262 1263 1264 1265
  @override
  void addTime(Duration duration) {
    // We don't support timeouts on the LiveTestWidgetsFlutterBinding.
    // See runTest().
  }

1266 1267 1268 1269 1270 1271 1272
  @override
  void scheduleFrame() {
    if (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.benchmark)
      return; // In benchmark mode, don't actually schedule any engine frames.
    super.scheduleFrame();
  }

1273 1274 1275 1276 1277 1278 1279
  @override
  void scheduleForcedFrame() {
    if (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.benchmark)
      return; // In benchmark mode, don't actually schedule any engine frames.
    super.scheduleForcedFrame();
  }

1280 1281
  bool _doDrawThisFrame;

1282 1283
  @override
  void handleBeginFrame(Duration rawTimeStamp) {
1284
    assert(_doDrawThisFrame == null);
1285 1286
    if (_expectingFrame ||
        (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.fullyLive) ||
1287
        (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.benchmark) ||
1288 1289
        (framePolicy == LiveTestWidgetsFlutterBindingFramePolicy.fadePointers && _viewNeedsPaint)) {
      _doDrawThisFrame = true;
1290
      super.handleBeginFrame(rawTimeStamp);
1291 1292 1293
    } else {
      _doDrawThisFrame = false;
    }
1294 1295 1296 1297 1298 1299 1300 1301
  }

  @override
  void handleDrawFrame() {
    assert(_doDrawThisFrame != null);
    if (_doDrawThisFrame)
      super.handleDrawFrame();
    _doDrawThisFrame = null;
1302 1303
    _viewNeedsPaint = false;
    if (_expectingFrame) { // set during pump
1304 1305 1306 1307
      assert(_pendingFrame != null);
      _pendingFrame.complete(); // unlocks the test API
      _pendingFrame = null;
      _expectingFrame = false;
1308
    } else if (framePolicy != LiveTestWidgetsFlutterBindingFramePolicy.benchmark) {
1309
      window.scheduleFrame();
1310 1311 1312
    }
  }

1313 1314 1315
  @override
  void initRenderView() {
    assert(renderView == null);
1316
    renderView = _LiveTestRenderView(
1317 1318
      configuration: createViewConfiguration(),
      onNeedPaint: _handleViewNeedsPaint,
1319
      window: window,
1320
    );
1321
    renderView.prepareInitialFrame();
1322 1323 1324 1325 1326
  }

  @override
  _LiveTestRenderView get renderView => super.renderView;

1327 1328 1329 1330 1331
  void _handleViewNeedsPaint() {
    _viewNeedsPaint = true;
    renderView.markNeedsPaint();
  }

1332 1333 1334 1335 1336 1337 1338 1339 1340
  /// An object to which real device events should be routed.
  ///
  /// Normally, device events are silently dropped. However, if this property is
  /// set to a non-null value, then the events will be routed to its
  /// [HitTestDispatcher.dispatchEvent] method instead.
  ///
  /// Events dispatched by [TestGesture] are not affected by this.
  HitTestDispatcher deviceEventDispatcher;

1341
  @override
1342 1343 1344
  void dispatchEvent(
    PointerEvent event,
    HitTestResult hitTestResult, {
1345
    TestBindingEventSource source = TestBindingEventSource.device,
1346
  }) {
1347 1348 1349 1350
    switch (source) {
      case TestBindingEventSource.test:
        if (!renderView._pointers.containsKey(event.pointer)) {
          assert(event.down);
1351
          renderView._pointers[event.pointer] = _LiveTestPointerRecord(event.pointer, event.position);
1352 1353 1354 1355 1356
        } else {
          renderView._pointers[event.pointer].position = event.position;
          if (!event.down)
            renderView._pointers[event.pointer].decay = _kPointerDecay;
        }
1357
        _handleViewNeedsPaint();
1358
        super.dispatchEvent(event, hitTestResult, source: source);
1359 1360 1361
        break;
      case TestBindingEventSource.device:
        if (deviceEventDispatcher != null)
1362
          deviceEventDispatcher.dispatchEvent(event, hitTestResult);
1363
        break;
1364 1365 1366
    }
  }

1367
  @override
1368
  Future<void> pump([ Duration duration, EnginePhase newPhase = EnginePhase.sendSemanticsUpdate ]) {
1369
    assert(newPhase == EnginePhase.sendSemanticsUpdate);
1370 1371 1372
    assert(inTest);
    assert(!_expectingFrame);
    assert(_pendingFrame == null);
1373
    return TestAsyncUtils.guard<void>(() {
1374
      if (duration != null) {
1375
        Timer(duration, () {
1376 1377 1378 1379 1380 1381 1382
          _expectingFrame = true;
          scheduleFrame();
        });
      } else {
        _expectingFrame = true;
        scheduleFrame();
      }
1383
      _pendingFrame = Completer<void>();
1384 1385 1386 1387
      return _pendingFrame.future;
    });
  }

1388
  @override
1389 1390
  Future<T> runAsync<T>(
    Future<T> callback(), {
1391
    Duration additionalTime = const Duration(milliseconds: 1000),
1392
  }) async {
1393 1394 1395
    assert(() {
      if (!_runningAsyncTasks)
        return true;
1396
      throw test_package.TestFailure(
1397 1398 1399 1400 1401 1402 1403
          'Reentrant call to runAsync() denied.\n'
          'runAsync() was called, then before its future completed, it '
          'was called again. You must wait for the first returned future '
          'to complete before calling runAsync() again.'
      );
    }());

1404 1405
    addTime(additionalTime); // doesn't do anything since we don't actually track the timeout, but just for correctness...

1406 1407 1408 1409
    _runningAsyncTasks = true;
    try {
      return await callback();
    } catch (error, stack) {
1410
      FlutterError.reportError(FlutterErrorDetails(
1411 1412 1413
        exception: error,
        stack: stack,
        library: 'Flutter test framework',
1414
        context: ErrorSummary('while running async test code'),
1415 1416 1417 1418 1419 1420 1421
      ));
      return null;
    } finally {
      _runningAsyncTasks = false;
    }
  }

1422
  @override
1423
  Future<void> runTest(Future<void> testBody(), VoidCallback invariantTester, { String description = '', Duration timeout }) async {
1424
    assert(description != null);
1425 1426
    assert(!inTest);
    _inTest = true;
1427
    renderView._setDescription(description);
1428 1429 1430 1431
    // We drop the timeout on the floor in `flutter run` mode.
    // We could support it, but we'd have to automatically add the entire duration of pumps
    // and timers and so on, since those operate in real time when using this binding, but
    // the timeouts expect them to happen near-instantaneously.
1432
    return _runTest(testBody, invariantTester, description);
1433 1434
  }

1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449
  @override
  void reportExceptionNoticed(FlutterErrorDetails exception) {
    final DebugPrintCallback testPrint = debugPrint;
    debugPrint = debugPrintOverride;
    debugPrint('(The following exception is now available via WidgetTester.takeException:)');
    FlutterError.dumpErrorToConsole(exception, forceReport: true);
    debugPrint(
      '(If WidgetTester.takeException is called, the above exception will be ignored. '
      'If it is not, then the above exception will be dumped when another exception is '
      'caught by the framework or when the test ends, whichever happens first, and then '
      'the test will fail due to having not caught or expected the exception.)'
    );
    debugPrint = testPrint;
  }

1450 1451 1452 1453 1454 1455 1456 1457 1458 1459
  @override
  void postTest() {
    super.postTest();
    assert(!_expectingFrame);
    assert(_pendingFrame == null);
    _inTest = false;
  }

  @override
  ViewConfiguration createViewConfiguration() {
1460 1461 1462 1463
    return TestViewConfiguration(
      size: _surfaceSize ?? _kDefaultTestViewportSize,
      window: window,
    );
1464 1465 1466
  }

  @override
1467
  Offset globalToLocal(Offset point) {
1468 1469
    final Matrix4 transform = renderView.configuration.toHitTestMatrix();
    final double det = transform.invert();
1470
    assert(det != 0.0);
1471
    final Offset result = MatrixUtils.transformPoint(transform, point);
1472 1473 1474 1475
    return result;
  }

  @override
1476
  Offset localToGlobal(Offset point) {
1477
    final Matrix4 transform = renderView.configuration.toHitTestMatrix();
1478 1479
    return MatrixUtils.transformPoint(transform, point);
  }
1480
}
1481

1482 1483
/// A [ViewConfiguration] that pretends the display is of a particular size. The
/// size is in logical pixels. The resulting ViewConfiguration maps the given
1484
/// size onto the actual display using the [BoxFit.contain] algorithm.
1485 1486
class TestViewConfiguration extends ViewConfiguration {
  /// Creates a [TestViewConfiguration] with the given size. Defaults to 800x600.
1487 1488 1489
  ///
  /// If a [window] instance is not provided it defaults to [ui.window].
  factory TestViewConfiguration({
1490 1491
    Size size = _kDefaultTestViewportSize,
    ui.Window window,
1492 1493 1494 1495 1496
  }) {
    return TestViewConfiguration._(size, window ?? ui.window);
  }

  TestViewConfiguration._(Size size, ui.Window window)
1497 1498
    : _paintMatrix = _getMatrix(size, window.devicePixelRatio, window),
      _hitTestMatrix = _getMatrix(size, 1.0, window),
1499 1500
      super(size: size);

1501 1502 1503 1504
  static Matrix4 _getMatrix(Size size, double devicePixelRatio, ui.Window window) {
    final double inverseRatio = devicePixelRatio / window.devicePixelRatio;
    final double actualWidth = window.physicalSize.width * inverseRatio;
    final double actualHeight = window.physicalSize.height * inverseRatio;
1505 1506
    final double desiredWidth = size.width;
    final double desiredHeight = size.height;
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516
    double scale, shiftX, shiftY;
    if ((actualWidth / actualHeight) > (desiredWidth / desiredHeight)) {
      scale = actualHeight / desiredHeight;
      shiftX = (actualWidth - desiredWidth * scale) / 2.0;
      shiftY = 0.0;
    } else {
      scale = actualWidth / desiredWidth;
      shiftX = 0.0;
      shiftY = (actualHeight - desiredHeight * scale) / 2.0;
    }
1517 1518 1519
    final Matrix4 matrix = Matrix4.compose(
      Vector3(shiftX, shiftY, 0.0), // translation
      Quaternion.identity(), // rotation
1520
      Vector3(scale, scale, 1.0), // scale
1521
    );
1522
    return matrix;
1523 1524
  }

1525 1526
  final Matrix4 _paintMatrix;
  final Matrix4 _hitTestMatrix;
1527 1528

  @override
1529
  Matrix4 toMatrix() => _paintMatrix.clone();
1530

1531 1532 1533
  /// Provides the transformation matrix that converts coordinates in the test
  /// coordinate space to coordinates in logical pixels on the real display.
  ///
1534
  /// This is essentially the same as [toMatrix] but ignoring the device pixel
1535 1536 1537 1538 1539
  /// ratio.
  ///
  /// This is useful because pointers are described in logical pixels, as
  /// opposed to graphics which are expressed in physical pixels.
  Matrix4 toHitTestMatrix() => _hitTestMatrix.clone();
1540 1541 1542 1543 1544

  @override
  String toString() => 'TestViewConfiguration';
}

1545 1546 1547 1548
const int _kPointerDecay = -2;

class _LiveTestPointerRecord {
  _LiveTestPointerRecord(
1549
    this.pointer,
1550
    this.position,
1551
  ) : color = HSVColor.fromAHSV(0.8, (35.0 * pointer) % 360.0, 1.0, 1.0).toColor(),
1552 1553 1554
      decay = 1;
  final int pointer;
  final Color color;
1555
  Offset position;
1556 1557 1558 1559 1560
  int decay; // >0 means down, <0 means up, increases by one each time, removed at 0
}

class _LiveTestRenderView extends RenderView {
  _LiveTestRenderView({
1561 1562
    ViewConfiguration configuration,
    this.onNeedPaint,
1563 1564
    @required ui.Window window,
  }) : super(configuration: configuration, window: window);
1565

1566
  @override
1567 1568
  TestViewConfiguration get configuration => super.configuration;
  @override
1569
  set configuration(covariant TestViewConfiguration value) { super.configuration = value; }
1570

1571 1572
  final VoidCallback onNeedPaint;

1573 1574
  final Map<int, _LiveTestPointerRecord> _pointers = <int, _LiveTestPointerRecord>{};

1575
  TextPainter _label;
1576
  static const TextStyle _labelStyle = TextStyle(
1577 1578 1579 1580 1581 1582 1583 1584 1585
    fontFamily: 'sans-serif',
    fontSize: 10.0,
  );
  void _setDescription(String value) {
    assert(value != null);
    if (value.isEmpty) {
      _label = null;
      return;
    }
Ian Hickson's avatar
Ian Hickson committed
1586
    // TODO(ianh): Figure out if the test name is actually RTL.
1587 1588
    _label ??= TextPainter(textAlign: TextAlign.left, textDirection: TextDirection.ltr);
    _label.text = TextSpan(text: value, style: _labelStyle);
1589 1590 1591 1592 1593
    _label.layout();
    if (onNeedPaint != null)
      onNeedPaint();
  }

1594
  @override
1595
  bool hitTest(HitTestResult result, { Offset position }) {
1596 1597
    final Matrix4 transform = configuration.toHitTestMatrix();
    final double det = transform.invert();
1598 1599 1600 1601 1602
    assert(det != 0.0);
    position = MatrixUtils.transformPoint(transform, position);
    return super.hitTest(result, position: position);
  }

1603 1604 1605 1606 1607 1608
  @override
  void paint(PaintingContext context, Offset offset) {
    assert(offset == Offset.zero);
    super.paint(context, offset);
    if (_pointers.isNotEmpty) {
      final double radius = configuration.size.shortestSide * 0.05;
1609 1610
      final Path path = Path()
        ..addOval(Rect.fromCircle(center: Offset.zero, radius: radius))
1611 1612 1613 1614 1615
        ..moveTo(0.0, -radius * 2.0)
        ..lineTo(0.0, radius * 2.0)
        ..moveTo(-radius * 2.0, 0.0)
        ..lineTo(radius * 2.0, 0.0);
      final Canvas canvas = context.canvas;
1616
      final Paint paint = Paint()
1617 1618 1619 1620
        ..strokeWidth = radius / 10.0
        ..style = PaintingStyle.stroke;
      bool dirty = false;
      for (int pointer in _pointers.keys) {
1621
        final _LiveTestPointerRecord record = _pointers[pointer];
1622
        paint.color = record.color.withOpacity(record.decay < 0 ? (record.decay / (_kPointerDecay - 1)) : 1.0);
1623
        canvas.drawPath(path.shift(record.position), paint);
1624 1625 1626 1627 1628 1629 1630 1631
        if (record.decay < 0)
          dirty = true;
        record.decay += 1;
      }
      _pointers
        .keys
        .where((int pointer) => _pointers[pointer].decay == 0)
        .toList()
1632
        .forEach(_pointers.remove);
1633 1634
      if (dirty && onNeedPaint != null)
        scheduleMicrotask(onNeedPaint);
1635
    }
1636
    _label?.paint(context.canvas, offset - const Offset(0.0, 10.0));
1637 1638 1639
  }
}

1640 1641 1642 1643 1644 1645 1646
StackTrace _unmangle(StackTrace stack) {
  if (stack is stack_trace.Trace)
    return stack.vmTrace;
  if (stack is stack_trace.Chain)
    return stack.toTrace().vmTrace;
  return stack;
}