Unverified Commit 0da6bad6 authored by amirh's avatar amirh Committed by GitHub

add a matcher for area covered by paths (#14469)

parent 28c8fd10
......@@ -224,6 +224,18 @@ Matcher isMethodCall(String name, {@required dynamic arguments}) {
return new _IsMethodCall(name, arguments);
}
/// Asserts that 2 paths cover the same area by sampling multiple points.
///
/// Samples at least [sampleSize]^2 points inside [areaToCompare], and asserts
/// that the [Path.contains] method returns the same value for each of the
/// points for both paths.
///
/// When using this matcher you typically want to use a rectangle larger than
/// the area you expect to paint in for [areaToCompare] to catch errors where
/// the path draws outside the expected area.
Matcher coversSameAreaAs(Path expectedPath, {@required Rect areaToCompare, int sampleSize = 20})
=> new _CoversSameAreaAs(expectedPath, areaToCompare: areaToCompare, sampleSize: sampleSize);
class _FindsWidgetMatcher extends Matcher {
const _FindsWidgetMatcher(this.min, this.max);
......@@ -1090,3 +1102,75 @@ class _ClipsWithShapeBorder extends _MatchRenderObject<RenderClipPath, Null> {
Description describe(Description description) =>
description.add('clips with shape: $shape');
}
class _CoversSameAreaAs extends Matcher {
_CoversSameAreaAs(
this.expectedPath, {
@required this.areaToCompare,
this.sampleSize = 20,
}) : maxHorizontalNoise = areaToCompare.width / sampleSize,
maxVerticalNoise = areaToCompare.height / sampleSize {
// Use a fixed random seed to make sure tests are deterministic.
random = new math.Random(1);
}
final Path expectedPath;
final Rect areaToCompare;
final int sampleSize;
final double maxHorizontalNoise;
final double maxVerticalNoise;
math.Random random;
@override
bool matches(covariant Path actualPath, Map<dynamic, dynamic> matchState) {
for (int i = 0; i < sampleSize; i += 1) {
for (int j = 0; j < sampleSize; j += 1) {
final Offset offset = new Offset(
i * (areaToCompare.width / sampleSize),
j * (areaToCompare.height / sampleSize)
);
if (!_samplePoint(matchState, actualPath, offset))
return false;
final Offset noise = new Offset(
maxHorizontalNoise * random.nextDouble(),
maxVerticalNoise * random.nextDouble(),
);
if (!_samplePoint(matchState, actualPath, offset + noise))
return false;
}
}
return true;
}
bool _samplePoint(Map<dynamic, dynamic> matchState, Path actualPath, Offset offset) {
if (expectedPath.contains(offset) == actualPath.contains(offset))
return true;
if (actualPath.contains(offset))
return failWithDescription(matchState, '$offset is contained in the actual path but not in the expected path');
else
return failWithDescription(matchState, '$offset is contained in the expected path but not in the actual path');
}
bool failWithDescription(Map<dynamic, dynamic> matchState, String description) {
matchState['failure'] = description;
return false;
}
@override
Description describeMismatch(
dynamic item,
Description mismatchDescription,
Map<dynamic, dynamic> matchState,
bool verbose
) {
return mismatchDescription.add(matchState['failure']);
}
@override
Description describe(Description description) =>
description.add('covers expected area and only expected area');
}
......@@ -209,4 +209,77 @@ void main() {
throwsArgumentError,
);
});
group('coversSameAreaAs', () {
test('empty Paths', () {
expect(
new Path(),
coversSameAreaAs(
new Path(),
areaToCompare: new Rect.fromLTRB(0.0, 0.0, 10.0, 10.0)
),
);
});
test('mismatch', () {
final Path rectPath = new Path()
..addRect(new Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
expect(
new Path(),
isNot(coversSameAreaAs(
rectPath,
areaToCompare: new Rect.fromLTRB(0.0, 0.0, 10.0, 10.0)
)),
);
});
test('mismatch out of examined area', () {
final Path rectPath = new Path()
..addRect(new Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
rectPath.addRect(new Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
expect(
new Path(),
coversSameAreaAs(
rectPath,
areaToCompare: new Rect.fromLTRB(0.0, 0.0, 4.0, 4.0)
),
);
});
test('differently constructed rects match', () {
final Path rectPath = new Path()
..addRect(new Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
final Path linePath = new Path()
..moveTo(5.0, 5.0)
..lineTo(5.0, 6.0)
..lineTo(6.0, 6.0)
..lineTo(6.0, 5.0)
..close();
expect(
linePath,
coversSameAreaAs(
rectPath,
areaToCompare: new Rect.fromLTRB(0.0, 0.0, 10.0, 10.0)
),
);
});
test('partially overlapping paths', () {
final Path rectPath = new Path()
..addRect(new Rect.fromLTRB(5.0, 5.0, 6.0, 6.0));
final Path linePath = new Path()
..moveTo(5.0, 5.0)
..lineTo(5.0, 6.0)
..lineTo(6.0, 6.0)
..lineTo(6.0, 5.5)
..close();
expect(
linePath,
isNot(coversSameAreaAs(
rectPath,
areaToCompare: new Rect.fromLTRB(0.0, 0.0, 10.0, 10.0)
)),
);
});
});
}
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