From 05de81ed7ef479b47318bfe2f31ee7b8483c4bd0 Mon Sep 17 00:00:00 2001 From: Kostia Sokolovskyi Date: Sat, 29 Mar 2025 00:05:25 +0100 Subject: [PATCH] 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. --- .../flutter_test/lib/src/mock_canvas.dart | 73 +++++++++++++++++++ .../flutter_test/test/mock_canvas_test.dart | 68 +++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/packages/flutter_test/lib/src/mock_canvas.dart b/packages/flutter_test/lib/src/mock_canvas.dart index 02c9d1a78b..fb8f368cb3 100644 --- a/packages/flutter_test/lib/src/mock_canvas.dart +++ b/packages/flutter_test/lib/src/mock_canvas.dart @@ -289,6 +289,30 @@ abstract class PaintPattern { 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 @@ -916,6 +940,23 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher ); } + @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, [rsuperellipse])); @@ -1450,6 +1491,38 @@ class _DRRectPaintPredicate extends _TwoParameterPaintPredicate { }) : 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 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 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 e345a8f3a2..a0479ac7db 100644 --- a/packages/flutter_test/test/mock_canvas_test.dart +++ b/packages/flutter_test/test/mock_canvas_test.dart @@ -351,6 +351,56 @@ void main() { ); }); }); + + 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 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().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 @@ class _ArcPainter extends CustomPainter { 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; + } +}