| // Copyright 2014 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| import 'package:flutter/foundation.dart'; | |
| import 'package:flutter/gestures.dart'; | |
| import 'package:flutter/scheduler.dart'; | |
| import 'package:flutter/widgets.dart'; | |
| import 'package:flutter_test/flutter_test.dart'; | |
| // This test is very fragile and bypasses some zone-related checks. | |
| // It is written this way to verify some invariants that would otherwise | |
| // be difficult to check. | |
| // Do not use this test as a guide for writing good Flutter code. | |
| class TestBinding extends WidgetsFlutterBinding { | |
| @override | |
| void initInstances() { | |
| super.initInstances(); | |
| _instance = this; | |
| } | |
| @override | |
| bool debugCheckZone(String entryPoint) { | |
| return true; | |
| } | |
| static TestBinding get instance => BindingBase.checkInstance(_instance); | |
| static TestBinding? _instance; | |
| static TestBinding ensureInitialized() { | |
| if (TestBinding._instance == null) { | |
| TestBinding(); | |
| } | |
| return TestBinding.instance; | |
| } | |
| } | |
| class CountButton extends StatefulWidget { | |
| const CountButton({super.key}); | |
| @override | |
| State<CountButton> createState() => _CountButtonState(); | |
| } | |
| class _CountButtonState extends State<CountButton> { | |
| int counter = 0; | |
| @override | |
| Widget build(BuildContext context) { | |
| return GestureDetector( | |
| child: Text('Counter $counter'), | |
| onTap: () { | |
| setState(() { | |
| counter += 1; | |
| }); | |
| }, | |
| ); | |
| } | |
| } | |
| class AnimateSample extends StatefulWidget { | |
| const AnimateSample({super.key}); | |
| @override | |
| State<AnimateSample> createState() => _AnimateSampleState(); | |
| } | |
| class _AnimateSampleState extends State<AnimateSample> with SingleTickerProviderStateMixin { | |
| late 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() { | |
| TestBinding.ensureInitialized(); | |
| test('Test pump on LiveWidgetController', () async { | |
| runApp(const TestWidgetsApp(home: CountButton())); | |
| await SchedulerBinding.instance.endOfFrame; | |
| final WidgetController controller = LiveWidgetController(WidgetsBinding.instance); | |
| await controller.tap(find.text('Counter 0')); | |
| expect(find.text('Counter 0'), findsOneWidget); | |
| expect(find.text('Counter 1'), findsNothing); | |
| await controller.pump(); | |
| expect(find.text('Counter 0'), findsNothing); | |
| expect(find.text('Counter 1'), findsOneWidget); | |
| }); | |
| test('Test pumpAndSettle on LiveWidgetController', () async { | |
| runApp(const TestWidgetsApp(home: 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 logs = <String>[]; | |
| runApp( | |
| TestWidgetsApp( | |
| home: Listener( | |
| onPointerDown: (PointerDownEvent event) => logs.add('down ${event.buttons}'), | |
| onPointerMove: (PointerMoveEvent event) => logs.add('move ${event.buttons}'), | |
| onPointerUp: (PointerUpEvent event) => logs.add('up ${event.buttons}'), | |
| child: const Text('test'), | |
| ), | |
| ), | |
| ); | |
| await SchedulerBinding.instance.endOfFrame; | |
| final WidgetController controller = LiveWidgetController(WidgetsBinding.instance); | |
| final Offset location = controller.getCenter(find.text('test')); | |
| final records = <PointerEventRecord>[ | |
| PointerEventRecord(Duration.zero, <PointerEvent>[ | |
| // Typically PointerAddedEvent is not used in testers, but for records | |
| // captured on a device it is usually what starts a gesture. | |
| PointerAddedEvent(position: location), | |
| PointerDownEvent(position: location, buttons: kSecondaryMouseButton, pointer: 1), | |
| ]), | |
| ...<PointerEventRecord>[ | |
| for ( | |
| Duration t = const Duration(milliseconds: 5); | |
| t < const Duration(milliseconds: 80); | |
| t += const Duration(milliseconds: 16) | |
| ) | |
| PointerEventRecord(t, <PointerEvent>[ | |
| PointerMoveEvent( | |
| timeStamp: t - const Duration(milliseconds: 1), | |
| position: location, | |
| buttons: kSecondaryMouseButton, | |
| pointer: 1, | |
| ), | |
| ]), | |
| ], | |
| PointerEventRecord(const Duration(milliseconds: 80), <PointerEvent>[ | |
| PointerUpEvent( | |
| timeStamp: const Duration(milliseconds: 79), | |
| position: location, | |
| buttons: kSecondaryMouseButton, | |
| pointer: 1, | |
| ), | |
| ]), | |
| ]; | |
| final List<Duration> timeDiffs = await controller.handlePointerEventRecord(records); | |
| expect(timeDiffs.length, records.length); | |
| for (final diff in timeDiffs) { | |
| // Allow some freedom of time delay in real world. | |
| // TODO(pdblasi-google): The expected wiggle room should be -1, but occasional | |
| // results were reaching -6. This assert has been adjusted to reduce flakiness, | |
| // but the root cause is still unknown. (https://github.com/flutter/flutter/issues/109638) | |
| assert( | |
| diff.inMilliseconds > -7, | |
| 'timeDiffs were: $timeDiffs (offending time was ${diff.inMilliseconds}ms)', | |
| ); | |
| } | |
| const b = '$kSecondaryMouseButton'; | |
| expect(logs.first, 'down $b'); | |
| for (var i = 1; i < logs.length - 1; i++) { | |
| expect(logs[i], 'move $b'); | |
| } | |
| expect(logs.last, 'up $b'); | |
| }); | |
| } |