Commit 58348612 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Factor out debugPaintPadding and test it (#7598)

I plan to use this to implement similar logic in SliverPadding.

To make this easier to test I extended the paints matcher to accept a
function that takes a canvas. While I was at it I also made it accept
a Finder, it'll go and find the render object for you.

Also added support for paints..path and fixed some grammar in the
error messages.

Also improved the docs for debugPaint*.
parent 559621ca
...@@ -17,6 +17,7 @@ export 'dart:ui' show ...@@ -17,6 +17,7 @@ export 'dart:ui' show
Paint, Paint,
PaintingStyle, PaintingStyle,
Path, Path,
PathFillType,
Point, Point,
Radius, Radius,
RRect, RRect,
......
...@@ -31,12 +31,22 @@ Color debugPaintSizeColor = _kDebugPaintSizeColor; ...@@ -31,12 +31,22 @@ Color debugPaintSizeColor = _kDebugPaintSizeColor;
/// The color to use when painting some boxes that just add space (e.g. an empty /// The color to use when painting some boxes that just add space (e.g. an empty
/// RenderConstrainedBox or RenderPadding). /// RenderConstrainedBox or RenderPadding).
///
/// Used by, among other methods, [debugPaintPadding], which is called by
/// [RenderPadding.debugPaintSize] when [debugPaintSizeEnabled] is true.
Color debugPaintSpacingColor = _kDebugPaintSpacingColor; Color debugPaintSpacingColor = _kDebugPaintSpacingColor;
/// The color to use when painting RenderPadding edges. /// The color to use when painting RenderPadding edges.
///
/// Used by, among other methods, [debugPaintPadding], which is called by
/// [RenderPadding.debugPaintSize] when [debugPaintSizeEnabled] is true.
Color debugPaintPaddingColor = _kDebugPaintPaddingColor; Color debugPaintPaddingColor = _kDebugPaintPaddingColor;
/// The color to use when painting RenderPadding edges. /// The color to use when painting RenderPadding edges. This color is painted on
/// top of [debugPaintPaddingColor].
///
/// Used by, among other methods, [debugPaintPadding], which is called by
/// [RenderPadding.debugPaintSize] when [debugPaintSizeEnabled] is true.
Color debugPaintPaddingInnerEdgeColor = _kDebugPaintPaddingInnerEdgeColor; Color debugPaintPaddingInnerEdgeColor = _kDebugPaintPaddingInnerEdgeColor;
/// The color to use when painting the arrows used to show RenderPositionedBox alignment. /// The color to use when painting the arrows used to show RenderPositionedBox alignment.
...@@ -105,6 +115,35 @@ List<String> debugDescribeTransform(Matrix4 transform) { ...@@ -105,6 +115,35 @@ List<String> debugDescribeTransform(Matrix4 transform) {
return matrix; return matrix;
} }
void _debugDrawDoubleRect(Canvas canvas, Rect outerRect, Rect innerRect, Color color) {
final Path path = new Path()
..fillType = PathFillType.evenOdd
..addRect(outerRect)
..addRect(innerRect);
final Paint paint = new Paint()
..color = color;
canvas.drawPath(path, paint);
}
/// Paint padding using the [debugPaintPaddingColor],
/// [debugPaintPaddingInnerEdgeColor], and [debugPaintSpacingColor] colors.
///
/// Called by [RenderPadding.debugPaintSize] when [debugPaintSizeEnabled] is
/// true.
void debugPaintPadding(Canvas canvas, Rect outerRect, Rect innerRect, { double outlineWidth: 2.0 }) {
assert(() {
if (innerRect != null && !innerRect.isEmpty) {
_debugDrawDoubleRect(canvas, outerRect, innerRect, debugPaintPaddingColor);
_debugDrawDoubleRect(canvas, innerRect.inflate(outlineWidth).intersect(outerRect), innerRect, debugPaintPaddingInnerEdgeColor);
} else {
final Paint paint = new Paint()
..color = debugPaintSpacingColor;
canvas.drawRect(outerRect, paint);
}
return true;
});
}
/// Returns true if none of the rendering library debug variables have been changed. /// Returns true if none of the rendering library debug variables have been changed.
/// ///
/// This function is used by the test framework to ensure that debug variables /// This function is used by the test framework to ensure that debug variables
......
...@@ -172,43 +172,8 @@ class RenderPadding extends RenderShiftedBox { ...@@ -172,43 +172,8 @@ class RenderPadding extends RenderShiftedBox {
void debugPaintSize(PaintingContext context, Offset offset) { void debugPaintSize(PaintingContext context, Offset offset) {
super.debugPaintSize(context, offset); super.debugPaintSize(context, offset);
assert(() { assert(() {
Paint paint; final Rect outerRect = offset & size;
if (child != null && !child.size.isEmpty) { debugPaintPadding(context.canvas, outerRect, child != null ? padding.deflateRect(outerRect) : null);
Path path;
paint = new Paint()
..color = debugPaintPaddingColor;
path = new Path()
..moveTo(offset.dx, offset.dy)
..lineTo(offset.dx + size.width, offset.dy)
..lineTo(offset.dx + size.width, offset.dy + size.height)
..lineTo(offset.dx, offset.dy + size.height)
..close()
..moveTo(offset.dx + padding.left, offset.dy + padding.top)
..lineTo(offset.dx + padding.left, offset.dy + size.height - padding.bottom)
..lineTo(offset.dx + size.width - padding.right, offset.dy + size.height - padding.bottom)
..lineTo(offset.dx + size.width - padding.right, offset.dy + padding.top)
..close();
context.canvas.drawPath(path, paint);
paint = new Paint()
..color = debugPaintPaddingInnerEdgeColor;
const double kOutline = 2.0;
path = new Path()
..moveTo(offset.dx + math.max(padding.left - kOutline, 0.0), offset.dy + math.max(padding.top - kOutline, 0.0))
..lineTo(offset.dx + math.min(size.width - padding.right + kOutline, size.width), offset.dy + math.max(padding.top - kOutline, 0.0))
..lineTo(offset.dx + math.min(size.width - padding.right + kOutline, size.width), offset.dy + math.min(size.height - padding.bottom + kOutline, size.height))
..lineTo(offset.dx + math.max(padding.left - kOutline, 0.0), offset.dy + math.min(size.height - padding.bottom + kOutline, size.height))
..close()
..moveTo(offset.dx + padding.left, offset.dy + padding.top)
..lineTo(offset.dx + padding.left, offset.dy + size.height - padding.bottom)
..lineTo(offset.dx + size.width - padding.right, offset.dy + size.height - padding.bottom)
..lineTo(offset.dx + size.width - padding.right, offset.dy + padding.top)
..close();
context.canvas.drawPath(path, paint);
} else {
paint = new Paint()
..color = debugPaintSpacingColor;
context.canvas.drawRect(offset & size, paint);
}
return true; return true;
}); });
} }
......
...@@ -203,13 +203,14 @@ void main() { ...@@ -203,13 +203,14 @@ void main() {
) )
); );
RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
final BoxShadow boxShadow = kElevationToShadow[2][0]; final BoxShadow boxShadow = kElevationToShadow[2][0];
final RRect rrect = kMaterialEdges[MaterialType.card].toRRect( final RRect rrect = kMaterialEdges[MaterialType.card].toRRect(
new Rect.fromLTRB(0.0, 0.0, 800.0, 100.0) new Rect.fromLTRB(0.0, 0.0, 800.0, 100.0)
); );
expect(box, paints..rrect(rrect: rrect, color: boxShadow.color, hasMaskFilter: true)); expect(
find.byType(MergeableMaterial),
paints..rrect(rrect: rrect, color: boxShadow.color, hasMaskFilter: true),
);
}); });
testWidgets('MergeableMaterial merge gap', (WidgetTester tester) async { testWidgets('MergeableMaterial merge gap', (WidgetTester tester) async {
......
...@@ -6,8 +6,10 @@ import 'package:flutter/rendering.dart'; ...@@ -6,8 +6,10 @@ import 'package:flutter/rendering.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
import 'mock_canvas.dart';
void main() { void main() {
test("Describe transform control test", () { test('Describe transform control test', () {
Matrix4 identity = new Matrix4.identity(); Matrix4 identity = new Matrix4.identity();
List<String> description = debugDescribeTransform(identity); List<String> description = debugDescribeTransform(identity);
expect(description, equals(<String>[ expect(description, equals(<String>[
...@@ -17,4 +19,16 @@ void main() { ...@@ -17,4 +19,16 @@ void main() {
' [3] 0.0,0.0,0.0,1.0', ' [3] 0.0,0.0,0.0,1.0',
])); ]));
}); });
test('debugPaintPadding', () {
expect((Canvas canvas) {
debugPaintPadding(canvas, new Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), null);
}, paints..rect(color: debugPaintSpacingColor));
expect((Canvas canvas) {
debugPaintPadding(canvas, new Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), new Rect.fromLTRB(11.0, 11.0, 19.0, 19.0));
}, paints..path(color: debugPaintPaddingColor)..path(color: debugPaintPaddingInnerEdgeColor));
expect((Canvas canvas) {
debugPaintPadding(canvas, new Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), new Rect.fromLTRB(15.0, 15.0, 15.0, 15.0));
}, paints..rect(rect: new Rect.fromLTRB(10.0, 10.0, 20.0, 20.0), color: debugPaintSpacingColor));
});
} }
...@@ -4,10 +4,22 @@ ...@@ -4,10 +4,22 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:test/test.dart'; import 'package:flutter_test/flutter_test.dart';
/// Matches [RenderObject]s that paint a display list that matches the canvas /// Matches objects or functions that paint a display list that matches the
/// calls described by the pattern. /// canvas calls described by the pattern.
///
/// Specifically, this can be applied to [RenderObject]s, [Finder]s that
/// correspond to a single [RenderObject], and functions that have either of the
/// following signatures:
///
/// ```dart
/// void function(PaintingContext context, Offset offset);
/// void function(Canvas canvas);
/// ```
///
/// In the case of functions that take a [PaintingContext] and an [Offset], the
/// [paints] matcher will always pass a zero offset.
/// ///
/// To specify the pattern, call the methods on the returned object. For example: /// To specify the pattern, call the methods on the returned object. For example:
/// ///
...@@ -34,6 +46,12 @@ PaintPattern get paints => new _TestRecordingCanvasPatternMatcher(); ...@@ -34,6 +46,12 @@ PaintPattern get paints => new _TestRecordingCanvasPatternMatcher();
/// ``` /// ```
typedef bool PaintPatternPredicate(Symbol methodName, List<dynamic> arguments); typedef bool PaintPatternPredicate(Symbol methodName, List<dynamic> arguments);
/// The signature of [RenderObject.paint] functions.
typedef void _ContextPainterFunction(PaintingContext context, Offset offset);
/// The signature of functions that paint directly on a canvas.
typedef void _CanvasPainterFunction(Canvas canvas);
/// Builder interface for patterns used to match display lists (canvas calls). /// Builder interface for patterns used to match display lists (canvas calls).
/// ///
/// The [paints] matcher returns a [PaintPattern] so that you can build the /// The [paints] matcher returns a [PaintPattern] so that you can build the
...@@ -125,6 +143,21 @@ abstract class PaintPattern { ...@@ -125,6 +143,21 @@ abstract class PaintPattern {
/// [Canvas.drawCircle] call are ignored. /// [Canvas.drawCircle] call are ignored.
void circle({ double x, double y, double radius, Color color, bool hasMaskFilter, PaintingStyle style }); void circle({ double x, double y, double radius, Color color, bool hasMaskFilter, PaintingStyle style });
/// Indicates that a path is expected next.
///
/// The next path is examined. Any arguments that are passed to this method
/// are compared to the actual [Canvas.drawPath] call's `paint` argument, and
/// any mismatches result in failure.
///
/// There is currently no way to check the actual path itself.
// See https://github.com/flutter/flutter/issues/93 which tracks that issue.
///
/// If no call to [Canvas.drawPath] was made, then this results in failure.
///
/// Any calls made between the last matched call (if any) and the
/// [Canvas.drawPath] call are ignored.
void path({ Color color, bool hasMaskFilter, PaintingStyle style });
/// Provides a custom matcher. /// Provides a custom matcher.
/// ///
/// Each method call after the last matched call (if any) will be passed to /// Each method call after the last matched call (if any) will be passed to
...@@ -192,12 +225,16 @@ class _TestRecordingCanvasPatternMatcher extends Matcher implements PaintPattern ...@@ -192,12 +225,16 @@ class _TestRecordingCanvasPatternMatcher extends Matcher implements PaintPattern
_predicates.add(new _RRectPaintPredicate(rrect: rrect, color: color, hasMaskFilter: hasMaskFilter, style: style)); _predicates.add(new _RRectPaintPredicate(rrect: rrect, color: color, hasMaskFilter: hasMaskFilter, style: style));
} }
@override @override
void circle({ double x, double y, double radius, Color color, bool hasMaskFilter, PaintingStyle style }) { void circle({ double x, double y, double radius, Color color, bool hasMaskFilter, PaintingStyle style }) {
_predicates.add(new _CirclePaintPredicate(x: x, y: y, radius: radius, color: color, hasMaskFilter: hasMaskFilter, style: style)); _predicates.add(new _CirclePaintPredicate(x: x, y: y, radius: radius, color: color, hasMaskFilter: hasMaskFilter, style: style));
} }
@override
void path({ Color color, bool hasMaskFilter, PaintingStyle style }) {
_predicates.add(new _PathPaintPredicate(color: color, hasMaskFilter: hasMaskFilter, style: style));
}
@override @override
void something(PaintPatternPredicate predicate) { void something(PaintPatternPredicate predicate) {
_predicates.add(new _SomethingPaintPredicate(predicate)); _predicates.add(new _SomethingPaintPredicate(predicate));
...@@ -205,12 +242,28 @@ class _TestRecordingCanvasPatternMatcher extends Matcher implements PaintPattern ...@@ -205,12 +242,28 @@ class _TestRecordingCanvasPatternMatcher extends Matcher implements PaintPattern
@override @override
bool matches(Object object, Map<dynamic, dynamic> matchState) { bool matches(Object object, Map<dynamic, dynamic> matchState) {
if (object is! RenderObject)
return false;
final _TestRecordingCanvas canvas = new _TestRecordingCanvas(); final _TestRecordingCanvas canvas = new _TestRecordingCanvas();
final _TestRecordingPaintingContext context = new _TestRecordingPaintingContext(canvas); final _TestRecordingPaintingContext context = new _TestRecordingPaintingContext(canvas);
final RenderObject renderObject = object; if (object is _ContextPainterFunction) {
renderObject.paint(context, Offset.zero); final _ContextPainterFunction function = object;
function(context, Offset.zero);
} else if (object is _CanvasPainterFunction) {
final _CanvasPainterFunction function = object;
function(canvas);
} else {
if (object is Finder) {
TestAsyncUtils.guardSync();
final Finder finder = object;
object = finder.evaluate().single.renderObject;
}
if (object is RenderObject) {
final RenderObject renderObject = object;
renderObject.paint(context, Offset.zero);
} else {
matchState[this] = 'was not one of the supported objects for the "paints" matcher.';
return false;
}
}
final StringBuffer description = new StringBuffer(); final StringBuffer description = new StringBuffer();
final bool result = _evaluatePredicates(canvas._invocations, description); final bool result = _evaluatePredicates(canvas._invocations, description);
if (!result) { if (!result) {
...@@ -226,7 +279,7 @@ class _TestRecordingCanvasPatternMatcher extends Matcher implements PaintPattern ...@@ -226,7 +279,7 @@ class _TestRecordingCanvasPatternMatcher extends Matcher implements PaintPattern
@override @override
Description describe(Description description) { Description describe(Description description) {
description.add('RenderObject painting: '); description.add('Object or closure painting: ');
return description.addAll( return description.addAll(
'', ', ', '', '', ', ', '',
_predicates.map((_PaintPredicate predicate) => predicate.toString()), _predicates.map((_PaintPredicate predicate) => predicate.toString()),
...@@ -387,7 +440,7 @@ abstract class _DrawCommandPaintPredicate extends _PaintPredicate { ...@@ -387,7 +440,7 @@ abstract class _DrawCommandPaintPredicate extends _PaintPredicate {
if (hasMaskFilter) if (hasMaskFilter)
throw 'called $methodName with a paint that did not have a mask filter, despite expecting one.'; throw 'called $methodName with a paint that did not have a mask filter, despite expecting one.';
else else
throw 'called $methodName with a paint that did had a mask filter, despite not expecting one.'; throw 'called $methodName with a paint that did have a mask filter, despite not expecting one.';
} }
if (style != null && paintArgument.style != style) if (style != null && paintArgument.style != style)
throw 'called $methodName with a paint whose style, ${paintArgument.style}, was not exactly the expected style ($style).'; throw 'called $methodName with a paint whose style, ${paintArgument.style}, was not exactly the expected style ($style).';
...@@ -437,8 +490,13 @@ class _OneParameterPaintPredicate<T> extends _DrawCommandPaintPredicate { ...@@ -437,8 +490,13 @@ class _OneParameterPaintPredicate<T> extends _DrawCommandPaintPredicate {
@override @override
void debugFillDescription(List<String> description) { void debugFillDescription(List<String> description) {
super.debugFillDescription(description); super.debugFillDescription(description);
if (expected != null) if (expected != null) {
description.add('${T.runtimeType}: $expected'); if (expected.toString().contains(T.toString())) {
description.add('$expected');
} else {
description.add('$T: $expected');
}
}
} }
} }
...@@ -509,6 +567,12 @@ class _CirclePaintPredicate extends _DrawCommandPaintPredicate { ...@@ -509,6 +567,12 @@ class _CirclePaintPredicate extends _DrawCommandPaintPredicate {
} }
} }
class _PathPaintPredicate extends _DrawCommandPaintPredicate {
_PathPaintPredicate({ Color color, bool hasMaskFilter, PaintingStyle style }) : super(
#drawPath, 'a path', 2, 1, color: color, hasMaskFilter: hasMaskFilter, style: style
);
}
class _SomethingPaintPredicate extends _PaintPredicate { class _SomethingPaintPredicate extends _PaintPredicate {
_SomethingPaintPredicate(this.predicate); _SomethingPaintPredicate(this.predicate);
......
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