[web] Update the within() matcher to be more useful (flutter/engine#39288)

* [web] Add the moreOrLessEquals matcher

* address review comments

* use within() instead

* fix wasm tests
This commit is contained in:
Mouad Debbar 2023-02-02 16:56:21 -05:00 committed by GitHub
parent 0f117282e4
commit 1df3bf7ab1
4 changed files with 60 additions and 56 deletions

View File

@ -11,6 +11,8 @@ import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;
import '../matchers.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
}
@ -31,7 +33,7 @@ void runCanvasTests({required bool deviceClipRoundsOut}) {
expect(value.length, equals(16));
for (int r = 0; r < 4; r++) {
for (int c = 0; c < 4; c++) {
expect(value[r*4 + c], closeTo(expected[r*4 + c], 1e-10));
expect(value[r*4 + c], within(from: expected[r*4 + c]));
}
}
}

View File

@ -13,6 +13,8 @@ import 'package:test/test.dart';
import 'package:ui/ui.dart';
import 'matchers.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
}
@ -31,34 +33,32 @@ void testMain() {
});
test('Offset.fromDirection', () {
expect(Offset.fromDirection(0.0, 0.0), const Offset(0.0, 0.0));
expect(Offset.fromDirection(pi / 2.0).dx,
closeTo(0.0, 1e-12)); // aah, floating point math. i love you so.
expect(Offset.fromDirection(pi / 2.0).dy, 1.0);
expect(Offset.fromDirection(-pi / 2.0).dx, closeTo(0.0, 1e-12));
expect(Offset.fromDirection(-pi / 2.0).dy, -1.0);
// aah, floating point math. i love you so.
expect(Offset.fromDirection(pi / 2.0), within(from: const Offset(0.0, 1.0)));
expect(Offset.fromDirection(-pi / 2.0), within(from: const Offset(0.0, -1.0)));
expect(Offset.fromDirection(0.0), const Offset(1.0, 0.0));
expect(Offset.fromDirection(pi / 4.0).dx,
closeTo(1.0 / math.sqrt(2.0), 1e-12));
expect(Offset.fromDirection(pi / 4.0).dy,
closeTo(1.0 / math.sqrt(2.0), 1e-12));
expect(Offset.fromDirection(-pi / 4.0).dx,
closeTo(1.0 / math.sqrt(2.0), 1e-12));
expect(Offset.fromDirection(-pi / 4.0).dy,
closeTo(-1.0 / math.sqrt(2.0), 1e-12));
expect(Offset.fromDirection(pi).dx, -1.0);
expect(Offset.fromDirection(pi).dy, closeTo(0.0, 1e-12));
expect(Offset.fromDirection(pi * 3.0 / 4.0).dx,
closeTo(-1.0 / math.sqrt(2.0), 1e-12));
expect(Offset.fromDirection(pi * 3.0 / 4.0).dy,
closeTo(1.0 / math.sqrt(2.0), 1e-12));
expect(Offset.fromDirection(-pi * 3.0 / 4.0).dx,
closeTo(-1.0 / math.sqrt(2.0), 1e-12));
expect(Offset.fromDirection(-pi * 3.0 / 4.0).dy,
closeTo(-1.0 / math.sqrt(2.0), 1e-12));
expect(
Offset.fromDirection(pi / 4.0),
within(from: Offset(1.0 / math.sqrt(2.0), 1.0 / math.sqrt(2.0))),
);
expect(
Offset.fromDirection(-pi / 4.0),
within(from: Offset(1.0 / math.sqrt(2.0), -1.0 / math.sqrt(2.0))),
);
expect(Offset.fromDirection(pi), within(from: const Offset(-1.0, 0.0)));
expect(
Offset.fromDirection(pi * 3.0 / 4.0),
within(from: Offset(-1.0 / math.sqrt(2.0), 1.0 / math.sqrt(2.0))),
);
expect(
Offset.fromDirection(-pi * 3.0 / 4.0),
within(from: Offset(-1.0 / math.sqrt(2.0), -1.0 / math.sqrt(2.0))),
);
expect(Offset.fromDirection(0.0, 2.0), const Offset(2.0, 0.0));
expect(
Offset.fromDirection(pi / 6, 2.0).dx, closeTo(math.sqrt(3.0), 1e-12));
expect(Offset.fromDirection(pi / 6, 2.0).dy, closeTo(1.0, 1e-12));
Offset.fromDirection(pi / 6, 2.0),
within(from: Offset(math.sqrt(3.0), 1.0)),
);
});
test('Size.aspectRatio', () {
expect(const Size(0.0, 0.0).aspectRatio, 0.0);

View File

@ -7,12 +7,7 @@ import 'package:test/test.dart';
import 'package:ui/ui.dart';
/// The epsilon of tolerable double precision error.
///
/// This is used in various places in the framework to allow for floating point
/// precision loss in calculations. Differences below this threshold are safe
/// to disregard.
const double precisionErrorTolerance = 1e-10;
import 'matchers.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
@ -31,19 +26,19 @@ void testMain() {
});
test('lerpDouble should treat a null input as 0 if the other input is non-null', () {
expect(lerpDouble(null, 10.0, 0.25), closeTo(2.5, precisionErrorTolerance));
expect(lerpDouble(10.0, null, 0.25), closeTo(7.5, precisionErrorTolerance));
expect(lerpDouble(null, 10.0, 0.25), within(from: 2.5));
expect(lerpDouble(10.0, null, 0.25), within(from: 7.5));
expect(lerpDouble(null, 10, 0.25), closeTo(2.5, precisionErrorTolerance));
expect(lerpDouble(10, null, 0.25), closeTo(7.5, precisionErrorTolerance));
expect(lerpDouble(null, 10, 0.25), within(from: 2.5));
expect(lerpDouble(10, null, 0.25), within(from: 7.5));
});
test('lerpDouble should handle interpolation values < 0.0', () {
expect(lerpDouble(0.0, 10.0, -5.0), closeTo(-50.0, precisionErrorTolerance));
expect(lerpDouble(10.0, 0.0, -5.0), closeTo(60.0, precisionErrorTolerance));
expect(lerpDouble(0.0, 10.0, -5.0), within(from: -50.0));
expect(lerpDouble(10.0, 0.0, -5.0), within(from: 60.0));
expect(lerpDouble(0, 10, -5), closeTo(-50, precisionErrorTolerance));
expect(lerpDouble(10, 0, -5), closeTo(60, precisionErrorTolerance));
expect(lerpDouble(0, 10, -5), within(from: -50.0));
expect(lerpDouble(10, 0, -5), within(from: 60.0));
});
test('lerpDouble should return the start value at 0.0', () {
@ -55,17 +50,17 @@ void testMain() {
});
test('lerpDouble should interpolate between two values', () {
expect(lerpDouble(0.0, 10.0, 0.25), closeTo(2.5, precisionErrorTolerance));
expect(lerpDouble(10.0, 0.0, 0.25), closeTo(7.5, precisionErrorTolerance));
expect(lerpDouble(0.0, 10.0, 0.25), within(from: 2.5));
expect(lerpDouble(10.0, 0.0, 0.25), within(from: 7.5));
expect(lerpDouble(0, 10, 0.25), closeTo(2.5, precisionErrorTolerance));
expect(lerpDouble(10, 0, 0.25), closeTo(7.5, precisionErrorTolerance));
expect(lerpDouble(0, 10, 0.25), within(from: 2.5));
expect(lerpDouble(10, 0, 0.25), within(from: 7.5));
// Exact answer: 20.0 - 1.0e-29
expect(lerpDouble(10.0, 1.0e30, 1.0e-29), closeTo(20.0, precisionErrorTolerance));
expect(lerpDouble(10.0, 1.0e30, 1.0e-29), within(from: 20.0));
// Exact answer: 5.0 + 5.0e29
expect(lerpDouble(10.0, 1.0e30, 0.5), closeTo(5.0e29, precisionErrorTolerance));
expect(lerpDouble(10.0, 1.0e30, 0.5), within(from: 5.0e29));
});
test('lerpDouble should return the end value at 1.0', () {
@ -80,11 +75,11 @@ void testMain() {
});
test('lerpDouble should handle interpolation values > 1.0', () {
expect(lerpDouble(0.0, 10.0, 5.0), closeTo(50.0, precisionErrorTolerance));
expect(lerpDouble(10.0, 0.0, 5.0), closeTo(-40.0, precisionErrorTolerance));
expect(lerpDouble(0.0, 10.0, 5.0), within(from: 50.0));
expect(lerpDouble(10.0, 0.0, 5.0), within(from: -40.0));
expect(lerpDouble(0, 10, 5), closeTo(50, precisionErrorTolerance));
expect(lerpDouble(10, 0, 5), closeTo(-40, precisionErrorTolerance));
expect(lerpDouble(0, 10, 5), within(from: 50.0));
expect(lerpDouble(10, 0, 5), within(from: -40.0));
});
test('lerpDouble should return input value in all cases if begin/end are equal', () {

View File

@ -15,6 +15,13 @@ import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart';
/// The epsilon of tolerable double precision error.
///
/// This is used in various places in the framework to allow for floating point
/// precision loss in calculations. Differences below this threshold are safe
/// to disregard.
const double precisionErrorTolerance = 1e-10;
/// Enumerates all persisted surfaces in the tree rooted at [root].
///
/// If [root] is `null` returns all surfaces from the last rendered scene.
@ -60,7 +67,7 @@ Iterable<PersistedOffset> enumerateOffsets([PersistedSurface? root]) {
///
/// This makes it useful for comparing numbers, [Color]s, [Offset]s and other
/// sets of value for which a metric space is defined.
typedef DistanceFunction<T> = num Function(T a, T b);
typedef DistanceFunction<T> = double Function(T a, T b);
/// The type of a union of instances of [DistanceFunction<T>] for various types
/// T.
@ -73,7 +80,7 @@ typedef DistanceFunction<T> = num Function(T a, T b);
///
/// Calling an instance of this type must either be done dynamically, or by
/// first casting it to a [DistanceFunction<T>] for some concrete T.
typedef AnyDistanceFunction = num Function(Never a, Never b);
typedef AnyDistanceFunction = double Function(Never a, Never b);
const Map<Type, AnyDistanceFunction> _kStandardDistanceFunctions =
<Type, AnyDistanceFunction>{
@ -85,7 +92,7 @@ const Map<Type, AnyDistanceFunction> _kStandardDistanceFunctions =
Size: _sizeDistance,
};
int _intDistance(int a, int b) => (b - a).abs();
double _intDistance(int a, int b) => (b - a).abs().toDouble();
double _doubleDistance(double a, double b) => (b - a).abs();
double _offsetDistance(Offset a, Offset b) => (b - a).distance;
@ -131,8 +138,8 @@ double _sizeDistance(Size a, Size b) {
/// [double]s and has an optional `epsilon` parameter.
/// * [closeTo], which specializes in numbers only.
Matcher within<T>({
required num distance,
required T from,
double distance = precisionErrorTolerance,
DistanceFunction<T>? distanceFunction,
}) {
distanceFunction ??= _kStandardDistanceFunctions[T] as DistanceFunction<T>?;
@ -152,7 +159,7 @@ class _IsWithinDistance<T> extends Matcher {
final DistanceFunction<T> distanceFunction;
final T value;
final num epsilon;
final double epsilon;
@override
bool matches(Object? object, Map<dynamic, dynamic> matchState) {
@ -163,7 +170,7 @@ class _IsWithinDistance<T> extends Matcher {
return true;
}
final T test = object;
final num distance = distanceFunction(test, value);
final double distance = distanceFunction(test, value);
if (distance < 0) {
throw ArgumentError(
'Invalid distance function was used to compare a ${value.runtimeType} '