diff --git a/packages/flutter_test/lib/src/controller.dart b/packages/flutter_test/lib/src/controller.dart index 9969fae6cc..2de90c367b 100644 --- a/packages/flutter_test/lib/src/controller.dart +++ b/packages/flutter_test/lib/src/controller.dart @@ -440,6 +440,34 @@ abstract class WidgetController { /// be appropriate to return in the implementation of this method. Future pump([Duration duration]); + /// Repeatedly calls [pump] with the given `duration` until there are no + /// longer any frames scheduled. This will call [pump] at least once, even if + /// no frames are scheduled when the function is called, to flush any pending + /// microtasks which may themselves schedule a frame. + /// + /// This essentially waits for all animations to have completed. + /// + /// If it takes longer that the given `timeout` to settle, then the test will + /// fail (this method will throw an exception). In particular, this means that + /// if there is an infinite animation in progress (for example, if there is an + /// indeterminate progress indicator spinning), this method will throw. + /// + /// The default timeout is ten minutes, which is longer than most reasonable + /// finite animations would last. + /// + /// If the function returns, it returns the number of pumps that it performed. + /// + /// In general, it is better practice to figure out exactly why each frame is + /// needed, and then to [pump] exactly as many frames as necessary. This will + /// help catch regressions where, for instance, an animation is being started + /// one frame later than it should. + /// + /// Alternatively, one can check that the return value from this function + /// matches the expected number of pumps. + Future pumpAndSettle([ + Duration duration = const Duration(milliseconds: 100), + ]); + /// Attempts to drag the given widget by the given offset, by /// starting a drag in the middle of the widget. /// @@ -908,6 +936,22 @@ class LiveWidgetController extends WidgetController { await binding.endOfFrame; } + @override + Future pumpAndSettle([ + Duration duration = const Duration(milliseconds: 100), + ]) { + assert(duration != null); + assert(duration > Duration.zero); + return TestAsyncUtils.guard(() async { + int count = 0; + do { + await pump(duration); + count += 1; + } while (binding.hasScheduledFrame); + return count; + }); + } + @override Future> handlePointerEventRecord(List records) { assert(records != null); diff --git a/packages/flutter_test/lib/src/widget_tester.dart b/packages/flutter_test/lib/src/widget_tester.dart index 59dca04253..6dbd4a23f1 100644 --- a/packages/flutter_test/lib/src/widget_tester.dart +++ b/packages/flutter_test/lib/src/widget_tester.dart @@ -604,39 +604,13 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker } } - /// Repeatedly calls [pump] with the given `duration` until there are no - /// longer any frames scheduled. This will call [pump] at least once, even if - /// no frames are scheduled when the function is called, to flush any pending - /// microtasks which may themselves schedule a frame. - /// - /// This essentially waits for all animations to have completed. - /// - /// If it takes longer that the given `timeout` to settle, then the test will - /// fail (this method will throw an exception). In particular, this means that - /// if there is an infinite animation in progress (for example, if there is an - /// indeterminate progress indicator spinning), this method will throw. - /// - /// The default timeout is ten minutes, which is longer than most reasonable - /// finite animations would last. - /// - /// If the function returns, it returns the number of pumps that it performed. - /// - /// In general, it is better practice to figure out exactly why each frame is - /// needed, and then to [pump] exactly as many frames as necessary. This will - /// help catch regressions where, for instance, an animation is being started - /// one frame later than it should. - /// - /// Alternatively, one can check that the return value from this function - /// matches the expected number of pumps. + @override Future pumpAndSettle([ Duration duration = const Duration(milliseconds: 100), EnginePhase phase = EnginePhase.sendSemanticsUpdate, - Duration timeout = const Duration(minutes: 10), ]) { assert(duration != null); assert(duration > Duration.zero); - assert(timeout != null); - assert(timeout > Duration.zero); assert(() { final WidgetsBinding binding = this.binding; if (binding is LiveTestWidgetsFlutterBinding && @@ -648,16 +622,14 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker } return true; }()); - int count = 0; - return TestAsyncUtils.guard(() async { - final DateTime endTime = binding.clock.fromNowBy(timeout); + return TestAsyncUtils.guard(() async { + int count = 0; do { - if (binding.clock.now().isAfter(endTime)) - throw FlutterError('pumpAndSettle timed out'); await binding.pump(duration, phase); count += 1; } while (binding.hasScheduledFrame); - }).then((_) => count); + return count; + }); } /// Repeatedly pump frames that render the `target` widget with a fixed time diff --git a/packages/flutter_test/test/live_widget_controller_test.dart b/packages/flutter_test/test/live_widget_controller_test.dart index 68ce1b70eb..1b9ea2362e 100644 --- a/packages/flutter_test/test/live_widget_controller_test.dart +++ b/packages/flutter_test/test/live_widget_controller_test.dart @@ -29,6 +29,39 @@ class _CountButtonState extends State { } } +class AnimateSample extends StatefulWidget { + @override + _AnimateSampleState createState() => _AnimateSampleState(); +} + +class _AnimateSampleState extends State + with SingleTickerProviderStateMixin { + AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(seconds: 1), + )..forward(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _controller, + builder: (BuildContext context, _) => Text('Value: ${_controller.value}'), + ); + } +} + void main() { test('Test pump on LiveWidgetController', () async { runApp(MaterialApp(home: Center(child: CountButton()))); @@ -44,6 +77,16 @@ void main() { expect(find.text('Counter 1'), findsOneWidget); }); + test('Test pumpAndSettle on LiveWidgetController', () async { + runApp(MaterialApp(home: Center(child: AnimateSample()))); + await SchedulerBinding.instance.endOfFrame; + final WidgetController controller = + LiveWidgetController(WidgetsBinding.instance); + expect(find.text('Value: 1.0'), findsNothing); + await controller.pumpAndSettle(); + expect(find.text('Value: 1.0'), findsOneWidget); + }); + test('Input event array on LiveWidgetController', () async { final List logs = []; runApp( diff --git a/packages/flutter_test/test/widget_tester_test.dart b/packages/flutter_test/test/widget_tester_test.dart index bd91a48d6b..1e92b16f76 100644 --- a/packages/flutter_test/test/widget_tester_test.dart +++ b/packages/flutter_test/test/widget_tester_test.dart @@ -465,21 +465,11 @@ void main() { testWidgets('pumpAndSettle control test', (WidgetTester tester) async { final AnimationController controller = AnimationController( - duration: const Duration(minutes: 525600), + duration: const Duration(seconds: 1), vsync: const TestVSync(), ); expect(await tester.pumpAndSettle(), 1); controller.forward(); - try { - await tester.pumpAndSettle(); - expect(true, isFalse); - } catch (e) { - expect(e, isFlutterError); - } - controller.stop(); - expect(await tester.pumpAndSettle(), 1); - controller.duration = const Duration(seconds: 1); - controller.forward(); expect(await tester.pumpAndSettle(const Duration(milliseconds: 300)), 5); // 0, 300, 600, 900, 1200ms });