1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'dart:ui' as ui; |
6 | |
7 | import 'package:flutter/rendering.dart'; |
8 | import 'package:flutter/widgets.dart'; |
9 | import 'package:flutter_test/flutter_test.dart'; |
10 | |
11 | Future<ui.Image> createTestImage(int width, int height, ui.Color color) async { |
12 | final ui.Paint paint = |
13 | ui.Paint() |
14 | ..style = ui.PaintingStyle.stroke |
15 | ..strokeWidth = 1.0 |
16 | ..color = color; |
17 | final ui.PictureRecorder recorder = ui.PictureRecorder(); |
18 | final ui.Canvas pictureCanvas = ui.Canvas(recorder); |
19 | pictureCanvas.drawCircle(Offset.zero, 20.0, paint); |
20 | final ui.Picture picture = recorder.endRecording(); |
21 | final ui.Image image = await picture.toImage(width, height); |
22 | picture.dispose(); |
23 | return image; |
24 | } |
25 | |
26 | void main() { |
27 | const ui.Color red = ui.Color.fromARGB(255, 255, 0, 0); |
28 | const ui.Color green = ui.Color.fromARGB(255, 0, 255, 0); |
29 | const ui.Color transparentRed = ui.Color.fromARGB(128, 255, 0, 0); |
30 | |
31 | group('succeeds' , () { |
32 | testWidgets('when images have the same content' , (WidgetTester tester) async { |
33 | final ui.Image image1 = await createTestImage(100, 100, red); |
34 | addTearDown(image1.dispose); |
35 | final ui.Image referenceImage1 = await createTestImage(100, 100, red); |
36 | addTearDown(referenceImage1.dispose); |
37 | |
38 | await expectLater(image1, matchesReferenceImage(referenceImage1)); |
39 | |
40 | final ui.Image image2 = await createTestImage(100, 100, green); |
41 | addTearDown(image2.dispose); |
42 | final ui.Image referenceImage2 = await createTestImage(100, 100, green); |
43 | addTearDown(referenceImage2.dispose); |
44 | |
45 | await expectLater(image2, matchesReferenceImage(referenceImage2)); |
46 | |
47 | final ui.Image image3 = await createTestImage(100, 100, transparentRed); |
48 | addTearDown(image3.dispose); |
49 | final ui.Image referenceImage3 = await createTestImage(100, 100, transparentRed); |
50 | addTearDown(referenceImage3.dispose); |
51 | |
52 | await expectLater(image3, matchesReferenceImage(referenceImage3)); |
53 | }); |
54 | |
55 | testWidgets('when images are identical' , (WidgetTester tester) async { |
56 | final ui.Image image = await createTestImage(100, 100, red); |
57 | addTearDown(image.dispose); |
58 | await expectLater(image, matchesReferenceImage(image)); |
59 | }); |
60 | |
61 | testWidgets('when widget looks the same' , (WidgetTester tester) async { |
62 | addTearDown(tester.view.reset); |
63 | tester.view |
64 | ..physicalSize = const Size(10, 10) |
65 | ..devicePixelRatio = 1; |
66 | |
67 | const ValueKey<String> repaintBoundaryKey = ValueKey<String>('boundary' ); |
68 | |
69 | await tester.pumpWidget( |
70 | const RepaintBoundary(key: repaintBoundaryKey, child: ColoredBox(color: red)), |
71 | ); |
72 | |
73 | final ui.Image referenceImage = |
74 | (tester.renderObject(find.byKey(repaintBoundaryKey)) as RenderRepaintBoundary) |
75 | .toImageSync(); |
76 | addTearDown(referenceImage.dispose); |
77 | |
78 | await expectLater(find.byKey(repaintBoundaryKey), matchesReferenceImage(referenceImage)); |
79 | }); |
80 | }); |
81 | |
82 | group('fails' , () { |
83 | testWidgets('when image sizes do not match' , (WidgetTester tester) async { |
84 | final ui.Image red50 = await createTestImage(50, 50, red); |
85 | addTearDown(red50.dispose); |
86 | final ui.Image red100 = await createTestImage(100, 100, red); |
87 | addTearDown(red100.dispose); |
88 | |
89 | expect( |
90 | await matchesReferenceImage(red50).matchAsync(red100), |
91 | equals('does not match as width or height do not match. [100×100] != [50×50]' ), |
92 | ); |
93 | }); |
94 | |
95 | testWidgets('when image pixels do not match' , (WidgetTester tester) async { |
96 | final ui.Image red100 = await createTestImage(100, 100, red); |
97 | addTearDown(red100.dispose); |
98 | final ui.Image transparentRed100 = await createTestImage(100, 100, transparentRed); |
99 | addTearDown(transparentRed100.dispose); |
100 | |
101 | expect( |
102 | await matchesReferenceImage(red100).matchAsync(transparentRed100), |
103 | equals('does not match on 57 pixels' ), |
104 | ); |
105 | |
106 | final ui.Image green100 = await createTestImage(100, 100, green); |
107 | addTearDown(green100.dispose); |
108 | |
109 | expect( |
110 | await matchesReferenceImage(red100).matchAsync(green100), |
111 | equals('does not match on 57 pixels' ), |
112 | ); |
113 | }); |
114 | |
115 | testWidgets('when widget does not look the same' , (WidgetTester tester) async { |
116 | addTearDown(tester.view.reset); |
117 | tester.view |
118 | ..physicalSize = const Size(10, 10) |
119 | ..devicePixelRatio = 1; |
120 | |
121 | const ValueKey<String> repaintBoundaryKey = ValueKey<String>('boundary' ); |
122 | |
123 | await tester.pumpWidget( |
124 | const RepaintBoundary(key: repaintBoundaryKey, child: ColoredBox(color: red)), |
125 | ); |
126 | |
127 | final ui.Image referenceImage = |
128 | (tester.renderObject(find.byKey(repaintBoundaryKey)) as RenderRepaintBoundary) |
129 | .toImageSync(); |
130 | addTearDown(referenceImage.dispose); |
131 | |
132 | await tester.pumpWidget( |
133 | const RepaintBoundary(key: repaintBoundaryKey, child: ColoredBox(color: green)), |
134 | ); |
135 | |
136 | expect( |
137 | await matchesReferenceImage(referenceImage).matchAsync(find.byKey(repaintBoundaryKey)), |
138 | equals('does not match on 100 pixels' ), |
139 | ); |
140 | }); |
141 | }); |
142 | } |
143 | |