Add drawRSuperellipse support to mock_canvas. (#165744)
Closes https://github.com/flutter/flutter/issues/165743
### Description
- Adds `rsuperellipse` to `mock_canvas.dart` to verify
`drawRSuperellipse` calls
- Adds tests for `paints..rsuperellipse`
## Pre-launch Checklist
- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [X] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [X] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [X] I signed the [CLA].
- [X] I listed at least one issue that this PR fixes in the description
above.
- [X] I updated/added relevant documentation (doc comments with `///`).
- [X] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [X] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [X] All existing and new tests are passing.
diff --git a/packages/flutter_test/lib/src/mock_canvas.dart b/packages/flutter_test/lib/src/mock_canvas.dart
index 02c9d1a..fb8f368 100644
--- a/packages/flutter_test/lib/src/mock_canvas.dart
+++ b/packages/flutter_test/lib/src/mock_canvas.dart
@@ -289,6 +289,30 @@
PaintingStyle style,
});
+ /// Indicates that a rounded superellipse is expected next.
+ ///
+ /// The next rounded superellipse is examined. Any arguments that are passed
+ /// to this method are compared to the actual [Canvas.drawRSuperellipse]
+ /// call's arguments and any mismatches result in failure.
+ ///
+ /// If no call to [Canvas.drawRSuperellipse] was made, then this results in
+ /// failure.
+ ///
+ /// Any calls made between the last matched call (if any) and the
+ /// [Canvas.drawRSuperellipse] call are ignored.
+ ///
+ /// The [Paint]-related arguments (`color`, `strokeWidth`, `hasMaskFilter`)
+ /// are compared against the state of the [Paint] object after the
+ /// painting has completed, not at the time of the call. If the same [Paint]
+ /// object is reused multiple times, then this may not match the actual
+ /// arguments as they were seen by the method.
+ void rsuperellipse({
+ RSuperellipse? rsuperellipse,
+ Color? color,
+ double? strokeWidth,
+ bool? hasMaskFilter,
+ });
+
/// Indicates that a rounded superellipse clip is expected next.
///
/// The next rounded superellipse clip is examined. Any arguments that are
@@ -917,6 +941,23 @@
}
@override
+ void rsuperellipse({
+ RSuperellipse? rsuperellipse,
+ Color? color,
+ double? strokeWidth,
+ bool? hasMaskFilter,
+ }) {
+ _predicates.add(
+ _RSuperellipsePaintPredicate(
+ rsuperellipse: rsuperellipse,
+ color: color,
+ strokeWidth: strokeWidth,
+ hasMaskFilter: hasMaskFilter,
+ ),
+ );
+ }
+
+ @override
void clipRSuperellipse({RSuperellipse? rsuperellipse}) {
_predicates.add(_FunctionPaintPredicate(#clipRSuperellipse, <dynamic>[rsuperellipse]));
}
@@ -1450,6 +1491,38 @@
}) : super(#drawDRRect, 'a rounded rectangle outline', expected1: outer, expected2: inner);
}
+class _RSuperellipsePaintPredicate extends _DrawCommandPaintPredicate {
+ _RSuperellipsePaintPredicate({
+ this.rsuperellipse,
+ super.color,
+ super.strokeWidth,
+ super.hasMaskFilter,
+ }) : super(#drawRSuperellipse, 'a rounded superellipse', 2, 1);
+
+ final RSuperellipse? rsuperellipse;
+
+ @override
+ void verifyArguments(List<dynamic> arguments) {
+ super.verifyArguments(arguments);
+ final RSuperellipse rsuperellipseArgument = arguments[0] as RSuperellipse;
+ if (rsuperellipse != null && rsuperellipseArgument != rsuperellipse) {
+ throw FlutterError(
+ 'It called $methodName with a rounded superellipse, '
+ '$rsuperellipseArgument, which was not exactly the expected rounded '
+ 'superellipse ($rsuperellipse).',
+ );
+ }
+ }
+
+ @override
+ void debugFillDescription(List<String> description) {
+ super.debugFillDescription(description);
+ if (rsuperellipse != null) {
+ description.add('rounded superellipse $rsuperellipse');
+ }
+ }
+}
+
class _CirclePaintPredicate extends _DrawCommandPaintPredicate {
_CirclePaintPredicate({
this.x,
diff --git a/packages/flutter_test/test/mock_canvas_test.dart b/packages/flutter_test/test/mock_canvas_test.dart
index e345a8f..a0479ac 100644
--- a/packages/flutter_test/test/mock_canvas_test.dart
+++ b/packages/flutter_test/test/mock_canvas_test.dart
@@ -351,6 +351,56 @@
);
});
});
+
+ group('rsuperellipse', () {
+ final RSuperellipse rsuperellipse = RSuperellipse.fromRectAndRadius(
+ Offset.zero & const Size.square(50),
+ const Radius.circular(5),
+ );
+ final Paint paint = Paint()..color = Colors.blue;
+
+ Future<void> pumpPainter(WidgetTester tester) async {
+ await tester.pumpWidget(
+ Center(
+ child: CustomPaint(
+ painter: _RSuperellipsePainter(rsuperellipse: rsuperellipse, paint: paint),
+ size: rsuperellipse.outerRect.size,
+ ),
+ ),
+ );
+ }
+
+ testWidgets('matches when rsuperellipse is correct', (WidgetTester tester) async {
+ await pumpPainter(tester);
+ expect(
+ tester.renderObject(find.byType(CustomPaint)),
+ paints..rsuperellipse(rsuperellipse: rsuperellipse),
+ );
+ });
+
+ testWidgets('does not match when rsuperellipse is incorrect', (WidgetTester tester) async {
+ await pumpPainter(tester);
+
+ expect(
+ () => expect(
+ tester.renderObject(find.byType(CustomPaint)),
+ paints..rsuperellipse(rsuperellipse: rsuperellipse.deflate(10)),
+ ),
+ throwsA(
+ isA<TestFailure>().having(
+ (TestFailure failure) => failure.message,
+ 'message',
+ contains(
+ 'It called drawRSuperellipse with a rounded superellipse, '
+ 'RSuperellipse.fromLTRBR(0.0, 0.0, 50.0, 50.0, 5.0), which was '
+ 'not exactly the expected rounded superellipse '
+ '(RSuperellipse.fromLTRBR(10.0, 10.0, 40.0, 40.0, 0.0))',
+ ),
+ ),
+ ),
+ );
+ });
+ });
}
class _ArcPainter extends CustomPainter {
@@ -379,3 +429,21 @@
return true;
}
}
+
+class _RSuperellipsePainter extends CustomPainter {
+ const _RSuperellipsePainter({required this.rsuperellipse, required Paint paint}) : _paint = paint;
+
+ final RSuperellipse rsuperellipse;
+
+ final Paint _paint;
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ canvas.drawRSuperellipse(rsuperellipse, _paint);
+ }
+
+ @override
+ bool shouldRepaint(MyPainter oldDelegate) {
+ return true;
+ }
+}