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;
+  }
+}