From c12330fd21dcfd258fc0248948e975abdd92c10a Mon Sep 17 00:00:00 2001 From: Kostia Sokolovskyi Date: Tue, 18 Feb 2025 18:49:33 +0100 Subject: [PATCH] Add missing properties to _ArcPaintPredicate. (#162572) Fixes https://github.com/flutter/flutter/issues/162317 ### Description - Adds `startAngle`, `sweepAngle` and `useCenter` properties to `_ArcPaintPredicate` - Adds tests for `_ArcPaintPredicate` ## 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. - [ ] 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. Co-authored-by: Tong Mu --- .../flutter_test/lib/src/mock_canvas.dart | 48 ++++++ .../flutter_test/test/mock_canvas_test.dart | 158 ++++++++++++++++++ 2 files changed, 206 insertions(+) diff --git a/packages/flutter_test/lib/src/mock_canvas.dart b/packages/flutter_test/lib/src/mock_canvas.dart index 7b1c232e65f..020887e5d9e 100644 --- a/packages/flutter_test/lib/src/mock_canvas.dart +++ b/packages/flutter_test/lib/src/mock_canvas.dart @@ -388,6 +388,9 @@ abstract class PaintPattern { /// arguments as they were seen by the method. void arc({ Rect? rect, + double? startAngle, + double? sweepAngle, + bool? useCenter, Color? color, double? strokeWidth, bool? hasMaskFilter, @@ -968,6 +971,9 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher @override void arc({ Rect? rect, + double? startAngle, + double? sweepAngle, + bool? useCenter, Color? color, double? strokeWidth, bool? hasMaskFilter, @@ -977,6 +983,9 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher _predicates.add( _ArcPaintPredicate( rect: rect, + startAngle: startAngle, + sweepAngle: sweepAngle, + useCenter: useCenter, color: color, strokeWidth: strokeWidth, hasMaskFilter: hasMaskFilter, @@ -1598,6 +1607,9 @@ class _LinePaintPredicate extends _DrawCommandPaintPredicate { class _ArcPaintPredicate extends _DrawCommandPaintPredicate { _ArcPaintPredicate({ this.rect, + this.startAngle, + this.sweepAngle, + this.useCenter, super.color, super.strokeWidth, super.hasMaskFilter, @@ -1607,6 +1619,12 @@ class _ArcPaintPredicate extends _DrawCommandPaintPredicate { final Rect? rect; + final double? startAngle; + + final double? sweepAngle; + + final bool? useCenter; + @override void verifyArguments(List arguments) { super.verifyArguments(arguments); @@ -1617,6 +1635,27 @@ class _ArcPaintPredicate extends _DrawCommandPaintPredicate { 'exactly the expected rect ($rect).', ); } + final double startAngleArgument = arguments[1] as double; + if (startAngle != null && startAngleArgument != startAngle) { + throw FlutterError( + 'It called $methodName with a start angle, $startAngleArgument, which ' + 'was not exactly the expected start angle ($startAngle).', + ); + } + final double sweepAngleArgument = arguments[2] as double; + if (sweepAngle != null && sweepAngleArgument != sweepAngle) { + throw FlutterError( + 'It called $methodName with a sweep angle, $sweepAngleArgument, which ' + 'was not exactly the expected sweep angle ($sweepAngle).', + ); + } + final bool useCenterArgument = arguments[3] as bool; + if (useCenter != null && useCenterArgument != useCenter) { + throw FlutterError( + 'It called $methodName with a useCenter value, $useCenterArgument, ' + 'which was not exactly the expected value ($useCenter).', + ); + } } @override @@ -1625,6 +1664,15 @@ class _ArcPaintPredicate extends _DrawCommandPaintPredicate { if (rect != null) { description.add('rect $rect'); } + if (startAngle != null) { + description.add('startAngle $startAngle'); + } + if (sweepAngle != null) { + description.add('sweepAngle $sweepAngle'); + } + if (useCenter != null) { + description.add('useCenter $useCenter'); + } } } diff --git a/packages/flutter_test/test/mock_canvas_test.dart b/packages/flutter_test/test/mock_canvas_test.dart index cd2a8f33e93..e345a8f3a22 100644 --- a/packages/flutter_test/test/mock_canvas_test.dart +++ b/packages/flutter_test/test/mock_canvas_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:math' as math; + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -220,4 +222,160 @@ void main() { ]); }); }); + + group('arc', () { + final Rect rect = Offset.zero & const Size.square(50); + const double startAngle = math.pi / 4; + const double sweepAngle = math.pi / 2; + const bool useCenter = false; + final Paint paint = Paint()..color = Colors.blue; + + Future pumpPainter(WidgetTester tester) async { + await tester.pumpWidget( + Center( + child: CustomPaint( + painter: _ArcPainter( + startAngle: startAngle, + sweepAngle: sweepAngle, + useCenter: useCenter, + paint: paint, + ), + size: rect.size, + ), + ), + ); + } + + testWidgets('matches when rect is correct', (WidgetTester tester) async { + await pumpPainter(tester); + expect(tester.renderObject(find.byType(CustomPaint)), paints..arc(rect: rect)); + }); + + testWidgets('does not match when rect is incorrect', (WidgetTester tester) async { + await pumpPainter(tester); + + expect( + () => expect( + tester.renderObject(find.byType(CustomPaint)), + paints..arc(rect: rect.deflate(10)), + ), + throwsA( + isA().having( + (TestFailure failure) => failure.message, + 'message', + contains( + 'It called drawArc with a paint whose rect, ' + 'Rect.fromLTRB(0.0, 0.0, 50.0, 50.0), was not exactly the ' + 'expected rect (Rect.fromLTRB(10.0, 10.0, 40.0, 40.0)).', + ), + ), + ), + ); + }); + + testWidgets('matches when startAngle is correct', (WidgetTester tester) async { + await pumpPainter(tester); + expect(tester.renderObject(find.byType(CustomPaint)), paints..arc(startAngle: startAngle)); + }); + + testWidgets('does not match when startAngle is incorrect', (WidgetTester tester) async { + await pumpPainter(tester); + + expect( + () => expect( + tester.renderObject(find.byType(CustomPaint)), + paints..arc(startAngle: startAngle * 2), + ), + throwsA( + isA().having( + (TestFailure failure) => failure.message, + 'message', + contains( + 'It called drawArc with a start angle, 0.7853981633974483, which ' + 'was not exactly the expected start angle (1.5707963267948966).', + ), + ), + ), + ); + }); + + testWidgets('matches when sweepAngle is correct', (WidgetTester tester) async { + await pumpPainter(tester); + expect(tester.renderObject(find.byType(CustomPaint)), paints..arc(sweepAngle: sweepAngle)); + }); + + testWidgets('does not match when sweepAngle is incorrect', (WidgetTester tester) async { + await pumpPainter(tester); + + expect( + () => expect( + tester.renderObject(find.byType(CustomPaint)), + paints..arc(sweepAngle: sweepAngle * 2), + ), + throwsA( + isA().having( + (TestFailure failure) => failure.message, + 'message', + contains( + 'It called drawArc with a sweep angle, 1.5707963267948966, which ' + 'was not exactly the expected sweep angle (3.141592653589793).', + ), + ), + ), + ); + }); + + testWidgets('matches when useCenter is correct', (WidgetTester tester) async { + await pumpPainter(tester); + expect(tester.renderObject(find.byType(CustomPaint)), paints..arc(useCenter: useCenter)); + }); + + testWidgets('does not match when useCenter is incorrect', (WidgetTester tester) async { + await pumpPainter(tester); + + expect( + () => expect( + tester.renderObject(find.byType(CustomPaint)), + paints..arc(useCenter: !useCenter), + ), + throwsA( + isA().having( + (TestFailure failure) => failure.message, + 'message', + contains( + 'It called drawArc with a useCenter value, false, which was not ' + 'exactly the expected value (true)', + ), + ), + ), + ); + }); + }); +} + +class _ArcPainter extends CustomPainter { + const _ArcPainter({ + required this.startAngle, + required this.sweepAngle, + required this.useCenter, + required Paint paint, + }) : _paint = paint; + + final double startAngle; + + final double sweepAngle; + + final bool useCenter; + + final Paint _paint; + + @override + void paint(Canvas canvas, Size size) { + canvas.drawArc(Offset.zero & size, startAngle, sweepAngle, useCenter, _paint); + } + + @override + bool shouldRepaint(MyPainter oldDelegate) { + return true; + } }