216 lines
7.1 KiB
Dart
216 lines
7.1 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
class TestCustomPainter extends CustomPainter {
|
|
TestCustomPainter({ required this.log, this.name });
|
|
|
|
final List<String?> log;
|
|
final String? name;
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
log.add(name);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(TestCustomPainter oldPainter) => true;
|
|
}
|
|
|
|
class TestCustomPainterWithCustomSemanticsBuilder extends TestCustomPainter {
|
|
TestCustomPainterWithCustomSemanticsBuilder() : super(log: <String>[]);
|
|
|
|
@override
|
|
SemanticsBuilderCallback get semanticsBuilder => (Size size) {
|
|
const Key key = Key('0');
|
|
const Rect rect = Rect.zero;
|
|
const SemanticsProperties semanticsProperties = SemanticsProperties();
|
|
return <CustomPainterSemantics>[
|
|
const CustomPainterSemantics(key: key, rect: rect, properties: semanticsProperties),
|
|
const CustomPainterSemantics(key: key, rect: rect, properties: semanticsProperties),
|
|
];
|
|
};
|
|
}
|
|
|
|
class MockCanvas extends Fake implements Canvas {
|
|
int saveCount = 0;
|
|
int saveCountDelta = 1;
|
|
|
|
@override
|
|
int getSaveCount() {
|
|
return saveCount += saveCountDelta;
|
|
}
|
|
|
|
@override
|
|
void save() { }
|
|
}
|
|
|
|
class MockPaintingContext extends Fake implements PaintingContext {
|
|
@override
|
|
final MockCanvas canvas = MockCanvas();
|
|
}
|
|
|
|
void main() {
|
|
testWidgets('Control test for custom painting', (WidgetTester tester) async {
|
|
final List<String?> log = <String?>[];
|
|
await tester.pumpWidget(CustomPaint(
|
|
painter: TestCustomPainter(
|
|
log: log,
|
|
name: 'background',
|
|
),
|
|
foregroundPainter: TestCustomPainter(
|
|
log: log,
|
|
name: 'foreground',
|
|
),
|
|
child: CustomPaint(
|
|
painter: TestCustomPainter(
|
|
log: log,
|
|
name: 'child',
|
|
),
|
|
),
|
|
));
|
|
|
|
expect(log, equals(<String>['background', 'child', 'foreground']));
|
|
});
|
|
|
|
testWidgets('Throws FlutterError on custom painter incorrect restore/save calls', (
|
|
WidgetTester tester) async {
|
|
final GlobalKey target = GlobalKey();
|
|
final List<String?> log = <String?>[];
|
|
await tester.pumpWidget(CustomPaint(
|
|
key: target,
|
|
isComplex: true,
|
|
painter: TestCustomPainter(log: log),
|
|
));
|
|
final RenderCustomPaint renderCustom = target.currentContext!.findRenderObject()! as RenderCustomPaint;
|
|
final MockPaintingContext paintingContext = MockPaintingContext();
|
|
final MockCanvas canvas = paintingContext.canvas;
|
|
|
|
FlutterError getError() {
|
|
late FlutterError error;
|
|
try {
|
|
renderCustom.paint(paintingContext, Offset.zero);
|
|
} on FlutterError catch (e) {
|
|
error = e;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
FlutterError error = getError();
|
|
expect(error.toStringDeep(), equalsIgnoringHashCodes(
|
|
'FlutterError\n'
|
|
' The TestCustomPainter#00000() custom painter called canvas.save()\n'
|
|
' or canvas.saveLayer() at least 1 more time than it called\n'
|
|
' canvas.restore().\n'
|
|
' This leaves the canvas in an inconsistent state and will probably\n'
|
|
' result in a broken display.\n'
|
|
' You must pair each call to save()/saveLayer() with a later\n'
|
|
' matching call to restore().\n'
|
|
));
|
|
|
|
canvas.saveCountDelta = -1;
|
|
error = getError();
|
|
expect(error.toStringDeep(), equalsIgnoringHashCodes(
|
|
'FlutterError\n'
|
|
' The TestCustomPainter#00000() custom painter called\n'
|
|
' canvas.restore() 1 more time than it called canvas.save() or\n'
|
|
' canvas.saveLayer().\n'
|
|
' This leaves the canvas in an inconsistent state and will result\n'
|
|
' in a broken display.\n'
|
|
' You should only call restore() if you first called save() or\n'
|
|
' saveLayer().\n'
|
|
));
|
|
|
|
canvas.saveCountDelta = 2;
|
|
error = getError();
|
|
expect(error.toStringDeep(), contains('2 more times'));
|
|
|
|
canvas.saveCountDelta = -2;
|
|
error = getError();
|
|
expect(error.toStringDeep(), contains('2 more times'));
|
|
});
|
|
|
|
testWidgets('CustomPaint sizing', (WidgetTester tester) async {
|
|
final GlobalKey target = GlobalKey();
|
|
|
|
await tester.pumpWidget(Center(
|
|
child: CustomPaint(key: target),
|
|
));
|
|
expect(target.currentContext!.size, Size.zero);
|
|
|
|
await tester.pumpWidget(Center(
|
|
child: CustomPaint(key: target, child: Container()),
|
|
));
|
|
expect(target.currentContext!.size, const Size(800.0, 600.0));
|
|
|
|
await tester.pumpWidget(Center(
|
|
child: CustomPaint(key: target, size: const Size(20.0, 20.0)),
|
|
));
|
|
expect(target.currentContext!.size, const Size(20.0, 20.0));
|
|
|
|
await tester.pumpWidget(Center(
|
|
child: CustomPaint(key: target, size: const Size(2000.0, 100.0)),
|
|
));
|
|
expect(target.currentContext!.size, const Size(800.0, 100.0));
|
|
|
|
await tester.pumpWidget(Center(
|
|
child: CustomPaint(key: target, size: Size.zero, child: Container()),
|
|
));
|
|
expect(target.currentContext!.size, const Size(800.0, 600.0));
|
|
|
|
await tester.pumpWidget(Center(
|
|
child: CustomPaint(key: target, child: const SizedBox(height: 0.0, width: 0.0)),
|
|
));
|
|
expect(target.currentContext!.size, Size.zero);
|
|
|
|
});
|
|
|
|
testWidgets('Raster cache hints', (WidgetTester tester) async {
|
|
final GlobalKey target = GlobalKey();
|
|
|
|
final List<String?> log = <String?>[];
|
|
await tester.pumpWidget(CustomPaint(
|
|
key: target,
|
|
isComplex: true,
|
|
painter: TestCustomPainter(log: log),
|
|
));
|
|
RenderCustomPaint renderCustom = target.currentContext!.findRenderObject()! as RenderCustomPaint;
|
|
expect(renderCustom.isComplex, true);
|
|
expect(renderCustom.willChange, false);
|
|
|
|
await tester.pumpWidget(CustomPaint(
|
|
key: target,
|
|
willChange: true,
|
|
foregroundPainter: TestCustomPainter(log: log),
|
|
));
|
|
renderCustom = target.currentContext!.findRenderObject()! as RenderCustomPaint;
|
|
expect(renderCustom.isComplex, false);
|
|
expect(renderCustom.willChange, true);
|
|
});
|
|
|
|
test('Raster cache hints cannot be set with null painters', () {
|
|
expect(() => CustomPaint(isComplex: true), throwsAssertionError);
|
|
expect(() => CustomPaint(willChange: true), throwsAssertionError);
|
|
});
|
|
|
|
test('RenderCustomPaint consults preferred size for intrinsics when it has no child', () {
|
|
final RenderCustomPaint inner = RenderCustomPaint(preferredSize: const Size(20, 30));
|
|
expect(inner.getMinIntrinsicWidth(double.infinity), 20);
|
|
expect(inner.getMaxIntrinsicWidth(double.infinity), 20);
|
|
expect(inner.getMinIntrinsicHeight(double.infinity), 30);
|
|
expect(inner.getMaxIntrinsicHeight(double.infinity), 30);
|
|
});
|
|
|
|
test('RenderCustomPaint does not return infinity for its intrinsics', () {
|
|
final RenderCustomPaint inner = RenderCustomPaint(preferredSize: Size.infinite);
|
|
expect(inner.getMinIntrinsicWidth(double.infinity), 0);
|
|
expect(inner.getMaxIntrinsicWidth(double.infinity), 0);
|
|
expect(inner.getMinIntrinsicHeight(double.infinity), 0);
|
|
expect(inner.getMaxIntrinsicHeight(double.infinity), 0);
|
|
});
|
|
}
|