Commit bb0c41f2 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Improve test coverage for gestures.dart (#4773)

This patch adds tests for some code paths we weren't hitting before and removes
some dead code that couldn't be tested because it was unreachable.
parent 2898768d
...@@ -2,22 +2,18 @@ ...@@ -2,22 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import "dart:math" as math; import 'dart:math' as math;
import "dart:typed_data"; import 'dart:typed_data';
// TODO(abarth): Consider using vector_math.
class _Vector { class _Vector {
_Vector(int size) _Vector(int size) : _offset = 0, _length = size, _elements = new Float64List(size);
: _offset = 0, _length = size, _elements = new Float64List(size);
_Vector.fromValues(List<double> values)
: _offset = 0, _length = values.length, _elements = values;
_Vector.fromVOL(List<double> values, int offset, int length) _Vector.fromVOL(List<double> values, int offset, int length)
: _offset = offset, _length = length, _elements = values; : _offset = offset, _length = length, _elements = values;
final int _offset; final int _offset;
int get length => _length;
final int _length; final int _length;
final List<double> _elements; final List<double> _elements;
...@@ -35,26 +31,14 @@ class _Vector { ...@@ -35,26 +31,14 @@ class _Vector {
} }
double norm() => math.sqrt(this * this); double norm() => math.sqrt(this * this);
@override
String toString() {
String result = "";
for (int i = 0; i < _length; i++) {
if (i > 0)
result += ", ";
result += this[i].toString();
}
return result;
}
} }
// TODO(abarth): Consider using vector_math.
class _Matrix { class _Matrix {
_Matrix(int rows, int cols) _Matrix(int rows, int cols)
: _rows = rows, : _columns = cols,
_columns = cols,
_elements = new Float64List(rows * cols); _elements = new Float64List(rows * cols);
final int _rows;
final int _columns; final int _columns;
final List<double> _elements; final List<double> _elements;
...@@ -68,21 +52,6 @@ class _Matrix { ...@@ -68,21 +52,6 @@ class _Matrix {
row * _columns, row * _columns,
_columns _columns
); );
@override
String toString() {
String result = "";
for (int i = 0; i < _rows; i++) {
if (i > 0)
result += "; ";
for (int j = 0; j < _columns; j++) {
if (j > 0)
result += ", ";
result += get(i, j).toString();
}
}
return result;
}
} }
/// An nth degree polynomial fit to a dataset. /// An nth degree polynomial fit to a dataset.
......
...@@ -456,23 +456,18 @@ class DelayedMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Dela ...@@ -456,23 +456,18 @@ class DelayedMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Dela
/// defaults to [kLongPressTimeout] to match [LongPressGestureRecognizer] but /// defaults to [kLongPressTimeout] to match [LongPressGestureRecognizer] but
/// can be changed for specific behaviors. /// can be changed for specific behaviors.
DelayedMultiDragGestureRecognizer({ DelayedMultiDragGestureRecognizer({
Duration delay: kLongPressTimeout this.delay: kLongPressTimeout
}) : _delay = delay { }) {
assert(delay != null); assert(delay != null);
} }
/// The amount of time the pointer must remain in the same place for the drag /// The amount of time the pointer must remain in the same place for the drag
/// to be recognized. /// to be recognized.
Duration get delay => _delay; final Duration delay;
Duration _delay;
set delay(Duration value) {
assert(value != null);
_delay = value;
}
@override @override
_DelayedPointerState createNewPointerState(PointerDownEvent event) { _DelayedPointerState createNewPointerState(PointerDownEvent event) {
return new _DelayedPointerState(event.position, _delay); return new _DelayedPointerState(event.position, delay);
} }
@override @override
......
...@@ -16,15 +16,6 @@ class _Estimate { ...@@ -16,15 +16,6 @@ class _Estimate {
final Duration time; final Duration time;
final int degree; final int degree;
final double confidence; final double confidence;
@override
String toString() {
return 'Estimate(xCoefficients: $xCoefficients, '
'yCoefficients: $yCoefficients, '
'time: $time, '
'degree: $degree, '
'confidence: $confidence)';
}
} }
abstract class _VelocityTrackerStrategy { abstract class _VelocityTrackerStrategy {
...@@ -33,46 +24,20 @@ abstract class _VelocityTrackerStrategy { ...@@ -33,46 +24,20 @@ abstract class _VelocityTrackerStrategy {
void clear(); void clear();
} }
enum _Weighting {
weightingNone,
weightingDelta,
weightingCentral,
weightingRecent
}
class _Movement { class _Movement {
Duration eventTime = Duration.ZERO; Duration eventTime = Duration.ZERO;
Point position = Point.origin; Point position = Point.origin;
} }
typedef double _WeightChooser(int index);
class _LeastSquaresVelocityTrackerStrategy extends _VelocityTrackerStrategy { class _LeastSquaresVelocityTrackerStrategy extends _VelocityTrackerStrategy {
static const int kHistorySize = 20; static const int kHistorySize = 20;
static const int kHorizonMilliseconds = 100; static const int kHorizonMilliseconds = 100;
_LeastSquaresVelocityTrackerStrategy(this.degree, _Weighting weighting) _LeastSquaresVelocityTrackerStrategy(this.degree);
: _index = 0, _movements = new List<_Movement>(kHistorySize) {
switch (weighting) {
case _Weighting.weightingNone:
_chooseWeight = null;
break;
case _Weighting.weightingDelta:
_chooseWeight = _weightDelta;
break;
case _Weighting.weightingCentral:
_chooseWeight = _weightCentral;
break;
case _Weighting.weightingRecent:
_chooseWeight = _weightRecent;
break;
}
}
final int degree; final int degree;
final List<_Movement> _movements; final List<_Movement> _movements = new List<_Movement>(kHistorySize);
_WeightChooser _chooseWeight; int _index = 0;
int _index;
@override @override
void addMovement(Duration timeStamp, Point position) { void addMovement(Duration timeStamp, Point position) {
...@@ -104,7 +69,7 @@ class _LeastSquaresVelocityTrackerStrategy extends _VelocityTrackerStrategy { ...@@ -104,7 +69,7 @@ class _LeastSquaresVelocityTrackerStrategy extends _VelocityTrackerStrategy {
Point position = movement.position; Point position = movement.position;
x.add(position.x); x.add(position.x);
y.add(position.y); y.add(position.y);
w.add(_chooseWeight != null ? _chooseWeight(index) : 1.0); w.add(1.0);
time.add(-age); time.add(-age);
index = (index == 0 ? kHistorySize : index) - 1; index = (index == 0 ? kHistorySize : index) - 1;
...@@ -153,54 +118,6 @@ class _LeastSquaresVelocityTrackerStrategy extends _VelocityTrackerStrategy { ...@@ -153,54 +118,6 @@ class _LeastSquaresVelocityTrackerStrategy extends _VelocityTrackerStrategy {
_index = -1; _index = -1;
} }
double _weightDelta(int index) {
// Weight points based on how much time elapsed between them and the next
// point so that points that "cover" a shorter time span are weighed less.
// delta 0ms: 0.5
// delta 10ms: 1.0
if (index == _index)
return 1.0;
int nextIndex = (index + 1) % kHistorySize;
int deltaMilliseconds = (_movements[nextIndex].eventTime - _movements[index].eventTime).inMilliseconds;
if (deltaMilliseconds < 0)
return 0.5;
if (deltaMilliseconds < 10)
return 0.5 + deltaMilliseconds * 0.05;
return 1.0;
}
double _weightCentral(int index) {
// Weight points based on their age, weighing very recent and very old
// points less.
// age 0ms: 0.5
// age 10ms: 1.0
// age 50ms: 1.0
// age 60ms: 0.5
int ageMilliseconds = (_movements[_index].eventTime - _movements[index].eventTime).inMilliseconds;
if (ageMilliseconds < 0)
return 0.5;
if (ageMilliseconds < 10)
return 0.5 + ageMilliseconds * 0.05;
if (ageMilliseconds < 50)
return 1.0;
if (ageMilliseconds < 60)
return 0.5 + (60 - ageMilliseconds) * 0.05;
return 0.5;
}
double _weightRecent(int index) {
// Weight points based on their age, weighing older points less.
// age 0ms: 1.0
// age 50ms: 1.0
// age 100ms: 0.5
int ageMilliseconds = (_movements[_index].eventTime - _movements[index].eventTime).inMilliseconds;
if (ageMilliseconds < 50)
return 1.0;
if (ageMilliseconds < 100)
return 0.5 + (100 - ageMilliseconds) * 0.01;
return 0.5;
}
_Movement _getMovement(int i) { _Movement _getMovement(int i) {
_Movement result = _movements[i]; _Movement result = _movements[i];
if (result == null) { if (result == null) {
...@@ -310,6 +227,6 @@ class VelocityTracker { ...@@ -310,6 +227,6 @@ class VelocityTracker {
} }
static _VelocityTrackerStrategy _createStrategy() { static _VelocityTrackerStrategy _createStrategy() {
return new _LeastSquaresVelocityTrackerStrategy(2, _Weighting.weightingNone); return new _LeastSquaresVelocityTrackerStrategy(2);
} }
} }
...@@ -128,4 +128,34 @@ void main() { ...@@ -128,4 +128,34 @@ void main() {
drag.dispose(); drag.dispose();
}); });
testGesture('Clamp max velocity', (GestureTester tester) {
HorizontalDragGestureRecognizer drag = new HorizontalDragGestureRecognizer();
Velocity velocity;
drag.onEnd = (DragEndDetails details) {
velocity = details.velocity;
};
TestPointer pointer = new TestPointer(5);
PointerDownEvent down = pointer.down(const Point(10.0, 25.0), timeStamp: const Duration(milliseconds: 10));
drag.addPointer(down);
tester.closeArena(5);
tester.route(down);
tester.route(pointer.move(const Point(20.0, 25.0), timeStamp: const Duration(milliseconds: 10)));
tester.route(pointer.move(const Point(30.0, 25.0), timeStamp: const Duration(milliseconds: 11)));
tester.route(pointer.move(const Point(40.0, 25.0), timeStamp: const Duration(milliseconds: 12)));
tester.route(pointer.move(const Point(50.0, 25.0), timeStamp: const Duration(milliseconds: 13)));
tester.route(pointer.move(const Point(60.0, 25.0), timeStamp: const Duration(milliseconds: 14)));
tester.route(pointer.move(const Point(70.0, 25.0), timeStamp: const Duration(milliseconds: 15)));
tester.route(pointer.move(const Point(80.0, 25.0), timeStamp: const Duration(milliseconds: 16)));
tester.route(pointer.move(const Point(90.0, 25.0), timeStamp: const Duration(milliseconds: 17)));
tester.route(pointer.move(const Point(100.0, 25.0), timeStamp: const Duration(milliseconds: 18)));
tester.route(pointer.move(const Point(110.0, 25.0), timeStamp: const Duration(milliseconds: 19)));
tester.route(pointer.move(const Point(120.0, 25.0), timeStamp: const Duration(milliseconds: 20)));
tester.route(pointer.up(timeStamp: const Duration(milliseconds: 20)));
expect(velocity.pixelsPerSecond.dx, inInclusiveRange(0.99 * kMaxFlingVelocity, kMaxFlingVelocity));
drag.dispose();
});
} }
// Copyright 2015 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.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart';
import 'gesture_tester.dart';
void main() {
setUp(ensureGestureBinding);
testGesture('toString control tests', (GestureTester tester) {
expect(new PointerDownEvent(), hasOneLineDescription);
expect(new PointerDownEvent().toStringFull(), hasOneLineDescription);
});
testGesture('nthMouseButton control tests', (GestureTester tester) {
expect(nthMouseButton(2), kSecondaryMouseButton);
expect(nthStylusButton(2), kSecondaryStylusButton);
});
}
...@@ -77,6 +77,34 @@ void main() { ...@@ -77,6 +77,34 @@ void main() {
expect(events[2].runtimeType, equals(PointerUpEvent)); expect(events[2].runtimeType, equals(PointerUpEvent));
}); });
test('Synthetic move events', () {
mojo_bindings.Encoder encoder = new mojo_bindings.Encoder();
PointerPacket packet = new PointerPacket();
packet.pointers = <Pointer>[new Pointer(), new Pointer()];
packet.pointers[0]
..type = PointerType.down
..kind = PointerKind.touch
..x = 1.0
..y = 3.0;
packet.pointers[1]
..type = PointerType.up
..kind = PointerKind.touch
..x = 10.0
..y = 15.0;
packet.encode(encoder);
List<PointerEvent> events = <PointerEvent>[];
_binding.callback = (PointerEvent event) => events.add(event);
ui.window.onPointerPacket(encoder.message.buffer);
expect(events.length, 3);
expect(events[0].runtimeType, equals(PointerDownEvent));
expect(events[1].runtimeType, equals(PointerMoveEvent));
expect(events[1].delta, equals(const Offset(9.0, 12.0)));
expect(events[2].runtimeType, equals(PointerUpEvent));
});
test('Pointer cancel events', () { test('Pointer cancel events', () {
mojo_bindings.Encoder encoder = new mojo_bindings.Encoder(); mojo_bindings.Encoder encoder = new mojo_bindings.Encoder();
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:test/test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'velocity_tracker_data.dart'; import 'velocity_tracker_data.dart';
bool _withinTolerance(double actual, double expected) { bool _withinTolerance(double actual, double expected) {
...@@ -47,4 +47,14 @@ void main() { ...@@ -47,4 +47,14 @@ void main() {
} }
} }
}); });
test('Velocity control test', () {
Velocity velocity1 = new Velocity(pixelsPerSecond: const Offset(7.0, 0.0));
Velocity velocity2 = new Velocity(pixelsPerSecond: const Offset(12.0, 0.0));
expect(velocity1, equals(new Velocity(pixelsPerSecond: new Offset(7.0, 0.0))));
expect(velocity1, isNot(equals(velocity2)));
expect(velocity2 - velocity1, equals(new Velocity(pixelsPerSecond: new Offset(5.0, 0.0))));
expect(velocity1.hashCode, isNot(equals(velocity2.hashCode)));
expect(velocity1, hasOneLineDescription);
});
} }
// Copyright 2015 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.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
void main() {
testWidgets('toString control test', (WidgetTester tester) async {
await tester.pumpWidget(new Center(child: new Text('Hello')));
HitTestResult result = tester.hitTestOnBinding(Point.origin);
expect(result, hasOneLineDescription);
expect(result.path.first, hasOneLineDescription);
});
}
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