Commit f61a2c39 authored by Ian Hickson's avatar Ian Hickson Committed by Tong Mu

Clean up flutter_test/test/controller_test.dart (#31333)

Instead of using a custom WidgetController, which is very brittle, we just use the usual infrastructure.

Also, use structured data instead of an array.

This adds offsetMoreOrLessEquals to handle small floating point errors in offsets.
parent b770cdf2
...@@ -206,12 +206,14 @@ Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>(); ...@@ -206,12 +206,14 @@ Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>();
/// Asserts that two [double]s are equal, within some tolerated error. /// Asserts that two [double]s are equal, within some tolerated error.
/// ///
/// {@template flutter.flutter_test.moreOrLessEquals.epsilon}
/// Two values are considered equal if the difference between them is within /// Two values are considered equal if the difference between them is within
/// [precisionErrorTolerance] of the larger one. This is an arbitrary value /// [precisionErrorTolerance] of the larger one. This is an arbitrary value
/// which can be adjusted using the `epsilon` argument. This matcher is intended /// which can be adjusted using the `epsilon` argument. This matcher is intended
/// to compare floating point numbers that are the result of different sequences /// to compare floating point numbers that are the result of different sequences
/// of operations, such that they may have accumulated slightly different /// of operations, such that they may have accumulated slightly different
/// errors. /// errors.
/// {@endtemplate}
/// ///
/// See also: /// See also:
/// ///
...@@ -219,28 +221,40 @@ Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>(); ...@@ -219,28 +221,40 @@ Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>();
/// required and not named. /// required and not named.
/// * [inInclusiveRange], which matches if the argument is in a specified /// * [inInclusiveRange], which matches if the argument is in a specified
/// range. /// range.
/// * [rectMoreOrLessEquals] and [offsetMoreOrLessEquals], which do something
/// similar but for [Rect]s and [Offset]s respectively.
Matcher moreOrLessEquals(double value, { double epsilon = precisionErrorTolerance }) { Matcher moreOrLessEquals(double value, { double epsilon = precisionErrorTolerance }) {
return _MoreOrLessEquals(value, epsilon); return _MoreOrLessEquals(value, epsilon);
} }
/// Asserts that two [Rect]s are equal, within some tolerated error. /// Asserts that two [Rect]s are equal, within some tolerated error.
/// ///
/// Two values are considered equal if the difference between them is within /// {@macro flutter.flutter_test.moreOrLessEquals.epsilon}
/// [precisionErrorTolerance] of the larger one. This is an arbitrary value
/// which can be adjusted using the `epsilon` argument. This matcher is intended
/// to compare floating point numbers that are the result of different sequences
/// of operations, such that they may have accumulated slightly different
/// errors.
/// ///
/// See also: /// See also:
/// ///
/// * [moreOrLessEquals], which is for [double]s. /// * [moreOrLessEquals], which is for [double]s.
/// * [offsetMoreOrLessEquals], which is for [Offset]s.
/// * [within], which offers a generic version of this functionality that can /// * [within], which offers a generic version of this functionality that can
/// be used to match [Rect]s as well as other types. /// be used to match [Rect]s as well as other types.
Matcher rectMoreOrLessEquals(Rect value, { double epsilon = precisionErrorTolerance }) { Matcher rectMoreOrLessEquals(Rect value, { double epsilon = precisionErrorTolerance }) {
return _IsWithinDistance<Rect>(_rectDistance, value, epsilon); return _IsWithinDistance<Rect>(_rectDistance, value, epsilon);
} }
/// Asserts that two [Offset]s are equal, within some tolerated error.
///
/// {@macro flutter.flutter_test.moreOrLessEquals.epsilon}
///
/// See also:
///
/// * [moreOrLessEquals], which is for [double]s.
/// * [rectMoreOrLessEquals], which is for [Rect]s.
/// * [within], which offers a generic version of this functionality that can
/// be used to match [Offset]s as well as other types.
Matcher offsetMoreOrLessEquals(Offset value, { double epsilon = precisionErrorTolerance }) {
return _IsWithinDistance<Offset>(_offsetDistance, value, epsilon);
}
/// Asserts that two [String]s are equal after normalizing likely hash codes. /// Asserts that two [String]s are equal after normalizing likely hash codes.
/// ///
/// A `#` followed by 5 hexadecimal digits is assumed to be a short hash code /// A `#` followed by 5 hexadecimal digits is assumed to be a short hash code
......
...@@ -9,274 +9,259 @@ import 'package:flutter/widgets.dart'; ...@@ -9,274 +9,259 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
class TestDragData {
const TestDragData(
this.slop,
this.dragDistance,
this.expectedOffsets,
);
final Offset slop;
final Offset dragDistance;
final List<Offset> expectedOffsets;
}
void main() { void main() {
testWidgets( testWidgets(
'WidgetTester.drag must break the offset into multiple parallel components if' 'WidgetTester.drag must break the offset into multiple parallel components if'
'the drag goes outside the touch slop values', 'the drag goes outside the touch slop values',
(WidgetTester tester) async { (WidgetTester tester) async {
// The first Offset in every sub array (ie. offsetResults[i][0]) is (touchSlopX, touchSlopY).
// The second Offset in every sub array (ie. offsetResults[i][1]) will be the total move offset.
// The remaining values in every sub array are the expected separated drag offsets.
// This test checks to make sure that the total drag will be correctly split into // This test checks to make sure that the total drag will be correctly split into
// pieces such that the first (and potentially second) moveBy function call(s) in // pieces such that the first (and potentially second) moveBy function call(s) in
// controller.drag() will never have a component greater than the touch // controller.drag() will never have a component greater than the touch
// slop in that component's respective axis. // slop in that component's respective axis.
final List<List<Offset>> offsetResults = <List<Offset>>[ const List<TestDragData> offsetResults = <TestDragData>[
TestDragData(
Offset(10.0, 10.0),
Offset(-150.0, 200.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(-7.5, 10.0),
const Offset(-150.0, 200.0), Offset(-2.5, 3.333333333333333),
const Offset(-7.5, 10.0), Offset(-140.0, 186.66666666666666),
const Offset(-2.5, 3.333333333333333),
const Offset(-140.0, 186.66666666666666),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(150, -200),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(7.5, -10),
const Offset(150, -200), Offset(2.5, -3.333333333333333),
const Offset(7.5, -10), Offset(140.0, -186.66666666666666),
const Offset(2.5, -3.333333333333333),
const Offset(140.0, -186.66666666666666),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(-200, 150),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(-10, 7.5),
const Offset(-200, 150), Offset(-3.333333333333333, 2.5),
const Offset(-10, 7.5), Offset(-186.66666666666666, 140.0),
const Offset(-3.333333333333333, 2.5),
const Offset(-186.66666666666666, 140.0),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(200.0, -150.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(10, -7.5),
const Offset(200.0, -150.0), Offset(3.333333333333333, -2.5),
const Offset(10, -7.5), Offset(186.66666666666666, -140.0),
const Offset(3.333333333333333, -2.5),
const Offset(186.66666666666666, -140.0),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(-150.0, -200.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(-7.5, -10.0),
const Offset(-150.0, -200.0), Offset(-2.5, -3.333333333333333),
const Offset(-7.5, -10.0), Offset(-140.0, -186.66666666666666),
const Offset(-2.5, -3.333333333333333),
const Offset(-140.0, -186.66666666666666),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(8.0, 3.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(8.0, 3.0),
const Offset(8.0, 3.0),
const Offset(8.0, 3.0),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(3.0, 8.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(3.0, 8.0),
const Offset(3.0, 8.0),
const Offset(3.0, 8.0),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(20.0, 5.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(10.0, 2.5),
const Offset(20.0, 5.0), Offset(10.0, 2.5),
const Offset(10.0, 2.5),
const Offset(10.0, 2.5),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(5.0, 20.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(2.5, 10.0),
const Offset(5.0, 20.0), Offset(2.5, 10.0),
const Offset(2.5, 10.0),
const Offset(2.5, 10.0),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(20.0, 15.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(10.0, 7.5),
const Offset(20.0, 15.0), Offset(3.333333333333333, 2.5),
const Offset(10.0, 7.5), Offset(6.666666666666668, 5.0),
const Offset(3.333333333333333, 2.5),
const Offset(6.666666666666668, 5.0),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(15.0, 20.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(7.5, 10.0),
const Offset(15.0, 20.0), Offset(2.5, 3.333333333333333),
const Offset(7.5, 10.0), Offset(5.0, 6.666666666666668),
const Offset(2.5, 3.333333333333333),
const Offset(5.0, 6.666666666666668),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(20.0, 20.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(10.0, 10.0),
const Offset(20.0, 20.0), Offset(10.0, 10.0),
const Offset(10.0, 10.0),
const Offset(10.0, 10.0),
], ],
),
TestDragData(
Offset(10.0, 10.0),
Offset(0.0, 5.0),
<Offset>[ <Offset>[
const Offset(10.0, 10.0), Offset(0.0, 5.0),
const Offset(0.0, 5.0),
const Offset(0.0, 5.0),
], ],
),
//// [VARYING TOUCH SLOP] //// //// Varying touch slops
TestDragData(
Offset(12.0, 5.0),
Offset(0.0, 5.0),
<Offset>[ <Offset>[
const Offset(12.0, 5.0), Offset(0.0, 5.0),
const Offset(0.0, 5.0),
const Offset(0.0, 5.0),
], ],
),
TestDragData(
Offset(12.0, 5.0),
Offset(20.0, 5.0),
<Offset>[ <Offset>[
const Offset(12.0, 5.0), Offset(12.0, 3.0),
const Offset(20.0, 5.0), Offset(8.0, 2.0),
const Offset(12.0, 3.0),
const Offset(8.0, 2.0),
], ],
),
TestDragData(
Offset(12.0, 5.0),
Offset(5.0, 20.0),
<Offset>[ <Offset>[
const Offset(12.0, 5.0), Offset(1.25, 5.0),
const Offset(5.0, 20.0), Offset(3.75, 15.0),
const Offset(1.25, 5.0),
const Offset(3.75, 15.0),
], ],
),
TestDragData(
Offset(5.0, 12.0),
Offset(5.0, 20.0),
<Offset>[ <Offset>[
const Offset(5.0, 12.0), Offset(3.0, 12.0),
const Offset(5.0, 20.0), Offset(2.0, 8.0),
const Offset(3.0, 12.0),
const Offset(2.0, 8.0),
], ],
),
TestDragData(
Offset(5.0, 12.0),
Offset(20.0, 5.0),
<Offset>[ <Offset>[
const Offset(5.0, 12.0), Offset(5.0, 1.25),
const Offset(20.0, 5.0), Offset(15.0, 3.75),
const Offset(5.0, 1.25),
const Offset(15.0, 3.75),
], ],
),
TestDragData(
Offset(18.0, 18.0),
Offset(0.0, 150.0),
<Offset>[ <Offset>[
const Offset(18.0, 18.0), Offset(0.0, 18.0),
const Offset(0.0, 150.0), Offset(0.0, 132.0),
const Offset(0.0, 18.0),
const Offset(0.0, 132.0),
], ],
),
TestDragData(
Offset(18.0, 18.0),
Offset(0.0, -150.0),
<Offset>[ <Offset>[
const Offset(18.0, 18.0), Offset(0.0, -18.0),
const Offset(0.0, -150.0), Offset(0.0, -132.0),
const Offset(0.0, -18.0),
const Offset(0.0, -132.0),
], ],
),
TestDragData(
Offset(18.0, 18.0),
Offset(-150.0, 0.0),
<Offset>[ <Offset>[
const Offset(18.0, 18.0), Offset(-18.0, 0.0),
const Offset(-150.0, 0.0), Offset(-132.0, 0.0),
const Offset(-18.0, 0.0),
const Offset(-132.0, 0.0),
], ],
),
TestDragData(
Offset(0.0, 0.0),
Offset(-150.0, 0.0),
<Offset>[ <Offset>[
const Offset(0.0, 0.0), Offset(-150.0, 0.0),
const Offset(-150.0, 0.0),
const Offset(-150.0, 0.0),
], ],
),
TestDragData(
Offset(18.0, 18.0),
Offset(-32.0, 0.0),
<Offset>[ <Offset>[
const Offset(18.0, 18.0), Offset(-18.0, 0.0),
const Offset(-32.0, 0.0), Offset(-14.0, 0.0),
const Offset(-18.0, 0.0),
const Offset(-14.0, 0.0),
], ],
),
]; ];
final List<Offset> dragOffsets = <Offset>[];
await tester.pumpWidget( await tester.pumpWidget(
const Directionality( Listener(
textDirection: TextDirection.ltr, onPointerMove: (PointerMoveEvent event) {
child: Text('test'), dragOffsets.add(event.delta);
},
child: const Text('test', textDirection: TextDirection.ltr),
), ),
); );
final WidgetControllerSpy spyController = WidgetControllerSpy(tester.binding);
for (int resultIndex = 0; resultIndex < offsetResults.length; resultIndex += 1) { for (int resultIndex = 0; resultIndex < offsetResults.length; resultIndex += 1) {
final List<Offset> testResult = offsetResults[resultIndex]; final TestDragData testResult = offsetResults[resultIndex];
await spyController.drag( await tester.drag(
find.text('test'), find.text('test'),
testResult[1], testResult.dragDistance,
touchSlopX: testResult[0].dx, touchSlopX: testResult.slop.dx,
touchSlopY: testResult[0].dy, touchSlopY: testResult.slop.dy,
); );
final List<Offset> dragOffsets = spyController.testGestureSpy.offsets;
expect( expect(
offsetResults[resultIndex].length - 2, testResult.expectedOffsets.length,
dragOffsets.length, dragOffsets.length,
reason: reason:
'There is a difference in the number of expected and actual split offsets for the drag with:\n' 'There is a difference in the number of expected and actual split offsets for the drag with:\n'
'Touch Slop: ' + testResult[0].toString() + '\n' 'Touch Slop: ${testResult.slop}\n'
'Delta: ' + testResult[1].toString() + '\n', 'Delta: ${testResult.dragDistance}\n',
); );
for (int valueIndex = 2; valueIndex < offsetResults[resultIndex].length; valueIndex += 1) { for (int valueIndex = 0; valueIndex < offsetResults[resultIndex].expectedOffsets.length; valueIndex += 1) {
expect( expect(
offsetResults[resultIndex][valueIndex], testResult.expectedOffsets[valueIndex],
dragOffsets[valueIndex - 2], offsetMoreOrLessEquals(dragOffsets[valueIndex]),
reason: reason:
'There is a difference in the expected and actual value of the ' + 'There is a difference in the expected and actual value of the ' +
(valueIndex == 2 ? 'first' : valueIndex == 3 ? 'second' : 'third') + (valueIndex == 2 ? 'first' : valueIndex == 3 ? 'second' : 'third') +
' split offset for the drag with:\n' ' split offset for the drag with:\n'
'Touch slop: ' + testResult[0].toString() + '\n' 'Touch slop: ${testResult.slop}\n'
'Delta: ' + testResult[1].toString() + '\n', 'Delta: ${testResult.dragDistance}\n'
); );
} }
spyController.testGestureSpy.clearOffsets(); dragOffsets.clear();
} }
}, },
); );
} }
class WidgetControllerSpy extends WidgetController {
WidgetControllerSpy(
TestWidgetsFlutterBinding binding,
) : super(binding) {
_binding = binding;
}
TestWidgetsFlutterBinding _binding;
@override
Future<void> pump(Duration duration) async {
if (duration != null)
await Future<void>.delayed(duration);
_binding.scheduleFrame();
await _binding.endOfFrame;
}
int _getNextPointer() {
final int result = nextPointer;
nextPointer += 1;
return result;
}
@override
Future<void> sendEventToBinding(PointerEvent event, HitTestResult result) {
return TestAsyncUtils.guard<void>(() async {
_binding.dispatchEvent(event, result, source: TestBindingEventSource.test);
});
}
TestGestureSpy testGestureSpy;
@override
Future<TestGesture> createGesture({int pointer, PointerDeviceKind kind = PointerDeviceKind.touch}) async {
return testGestureSpy = TestGestureSpy(
pointer: pointer ?? _getNextPointer(),
kind: kind,
dispatcher: sendEventToBinding,
hitTester: hitTestOnBinding,
);
}
}
class TestGestureSpy extends TestGesture {
TestGestureSpy({
int pointer,
PointerDeviceKind kind,
EventDispatcher dispatcher,
HitTester hitTester,
}) : super(
pointer: pointer,
kind: kind,
dispatcher: dispatcher,
hitTester: hitTester
);
List<Offset> offsets = <Offset>[];
void clearOffsets() {
offsets = <Offset>[];
}
@override
Future<void> moveBy(Offset offset, {Duration timeStamp = Duration.zero}) {
offsets.add(offset);
return super.moveBy(offset, timeStamp: timeStamp);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment