custom_paint_test.dart 6.31 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7
import 'package:flutter/rendering.dart';
8
import 'package:flutter/widgets.dart';
9
import 'package:flutter_test/flutter_test.dart';
10 11

import '../flutter_test_alternative.dart' show Fake;
12 13 14 15

class TestCustomPainter extends CustomPainter {
  TestCustomPainter({ this.log, this.name });

16 17
  final List<String> log;
  final String name;
18

19
  @override
20 21 22 23
  void paint(Canvas canvas, Size size) {
    log.add(name);
  }

24
  @override
25 26 27
  bool shouldRepaint(TestCustomPainter oldPainter) => true;
}

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
class TestCustomPainterWithCustomSemanticsBuilder extends TestCustomPainter {
  TestCustomPainterWithCustomSemanticsBuilder() : super(log: <String>[]);

  @override
  SemanticsBuilderCallback get semanticsBuilder => (Size size) {
    const Key key = Key('0');
    const Rect rect = Rect.fromLTRB(0, 0, 0, 0);
    const SemanticsProperties semanticsProperties = SemanticsProperties();
    return <CustomPainterSemantics>[
      const CustomPainterSemantics(key: key, rect: rect, properties: semanticsProperties),
      const CustomPainterSemantics(key: key, rect: rect, properties: semanticsProperties),
    ];
  };
}

43 44 45
class MockCanvas extends Fake implements Canvas {
  int saveCount = 0;
  int saveCountDelta = 1;
46

47 48 49 50 51 52 53 54 55 56 57 58 59
  @override
  int getSaveCount() {
    return saveCount += saveCountDelta;
  }

  @override
  void save() { }
}

class MockPaintingContext extends Fake implements PaintingContext {
  @override
  final MockCanvas canvas = MockCanvas();
}
60

61
void main() {
62
  testWidgets('Control test for custom painting', (WidgetTester tester) async {
63
    final List<String> log = <String>[];
64 65
    await tester.pumpWidget(CustomPaint(
      painter: TestCustomPainter(
66
        log: log,
67
        name: 'background',
68
      ),
69
      foregroundPainter: TestCustomPainter(
70
        log: log,
71
        name: 'foreground',
72
      ),
73 74
      child: CustomPaint(
        painter: TestCustomPainter(
75
          log: log,
76 77 78
          name: 'child',
        ),
      ),
79
    ));
80

81
    expect(log, equals(<String>['background', 'child', 'foreground']));
82
  });
Ian Hickson's avatar
Ian Hickson committed
83

84 85 86 87 88 89 90 91 92
  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),
    ));
93
    final RenderCustomPaint renderCustom = target.currentContext.findRenderObject() as RenderCustomPaint;
94 95
    final MockPaintingContext paintingContext = MockPaintingContext();
    final MockCanvas canvas = paintingContext.canvas;
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

    FlutterError getError() {
      FlutterError error;
      try {
        renderCustom.paint(paintingContext, const Offset(0, 0));
      } 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'
    ));

119
    canvas.saveCountDelta = -1;
120 121 122 123 124 125 126 127 128 129 130 131
    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'
    ));

132
    canvas.saveCountDelta = 2;
133 134 135
    error = getError();
    expect(error.toStringDeep(), contains('2 more times'));

136
    canvas.saveCountDelta = -2;
137 138 139 140
    error = getError();
    expect(error.toStringDeep(), contains('2 more times'));
  });

Ian Hickson's avatar
Ian Hickson committed
141
  testWidgets('CustomPaint sizing', (WidgetTester tester) async {
142
    final GlobalKey target = GlobalKey();
Ian Hickson's avatar
Ian Hickson committed
143

144
    await tester.pumpWidget(Center(
145
      child: CustomPaint(key: target),
Ian Hickson's avatar
Ian Hickson committed
146 147 148
    ));
    expect(target.currentContext.size, Size.zero);

149
    await tester.pumpWidget(Center(
150
      child: CustomPaint(key: target, child: Container()),
Ian Hickson's avatar
Ian Hickson committed
151 152 153
    ));
    expect(target.currentContext.size, const Size(800.0, 600.0));

154
    await tester.pumpWidget(Center(
155
      child: CustomPaint(key: target, size: const Size(20.0, 20.0)),
Ian Hickson's avatar
Ian Hickson committed
156 157 158
    ));
    expect(target.currentContext.size, const Size(20.0, 20.0));

159
    await tester.pumpWidget(Center(
160
      child: CustomPaint(key: target, size: const Size(2000.0, 100.0)),
Ian Hickson's avatar
Ian Hickson committed
161 162 163
    ));
    expect(target.currentContext.size, const Size(800.0, 100.0));

164
    await tester.pumpWidget(Center(
165
      child: CustomPaint(key: target, size: Size.zero, child: Container()),
Ian Hickson's avatar
Ian Hickson committed
166 167 168
    ));
    expect(target.currentContext.size, const Size(800.0, 600.0));

169
    await tester.pumpWidget(Center(
170
      child: CustomPaint(key: target, child: const SizedBox(height: 0.0, width: 0.0)),
Ian Hickson's avatar
Ian Hickson committed
171 172 173 174
    ));
    expect(target.currentContext.size, Size.zero);

  });
175 176

  testWidgets('Raster cache hints', (WidgetTester tester) async {
177
    final GlobalKey target = GlobalKey();
178 179

    final List<String> log = <String>[];
180
    await tester.pumpWidget(CustomPaint(
181 182
      key: target,
      isComplex: true,
183
      painter: TestCustomPainter(log: log),
184
    ));
185
    RenderCustomPaint renderCustom = target.currentContext.findRenderObject() as RenderCustomPaint;
186 187 188
    expect(renderCustom.isComplex, true);
    expect(renderCustom.willChange, false);

189
    await tester.pumpWidget(CustomPaint(
190 191
      key: target,
      willChange: true,
192
      foregroundPainter: TestCustomPainter(log: log),
193
    ));
194
    renderCustom = target.currentContext.findRenderObject() as RenderCustomPaint;
195 196 197
    expect(renderCustom.isComplex, false);
    expect(renderCustom.willChange, true);
  });
198 199 200 201 202

  test('Raster cache hints cannot be set with null painters', () {
    expect(() => CustomPaint(isComplex: true), throwsAssertionError);
    expect(() => CustomPaint(willChange: true), throwsAssertionError);
  });
203
}