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

import 'dart:ui' as ui;

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

12
Future<ui.Image> createTestImage(int width, int height, ui.Color color) async {
13 14 15 16 17 18 19 20
  final ui.Paint paint = ui.Paint()
    ..style = ui.PaintingStyle.stroke
    ..strokeWidth = 1.0
    ..color = color;
  final ui.PictureRecorder recorder = ui.PictureRecorder();
  final ui.Canvas pictureCanvas = ui.Canvas(recorder);
  pictureCanvas.drawCircle(Offset.zero, 20.0, paint);
  final ui.Picture picture = recorder.endRecording();
21 22 23
  final ui.Image image = await picture.toImage(width, height);
  picture.dispose();
  return image;
24 25 26 27 28 29 30 31
}

void main() {
  const ui.Color red = ui.Color.fromARGB(255, 255, 0, 0);
  const ui.Color green = ui.Color.fromARGB(255, 0, 255, 0);
  const ui.Color transparentRed = ui.Color.fromARGB(128, 255, 0, 0);

  group('succeeds', () {
32 33 34 35 36
    testWidgetsWithLeakTracking('when images have the same content', (WidgetTester tester) async {
      final ui.Image image1 = await createTestImage(100, 100, red);
      addTearDown(image1.dispose);
      final ui.Image referenceImage1 = await createTestImage(100, 100, red);
      addTearDown(referenceImage1.dispose);
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
      await expectLater(image1, matchesReferenceImage(referenceImage1));

      final ui.Image image2 = await createTestImage(100, 100, green);
      addTearDown(image2.dispose);
      final ui.Image referenceImage2 = await createTestImage(100, 100, green);
      addTearDown(referenceImage2.dispose);

      await expectLater(image2, matchesReferenceImage(referenceImage2));

      final ui.Image image3 = await createTestImage(100, 100, transparentRed);
      addTearDown(image3.dispose);
      final ui.Image referenceImage3 = await createTestImage(100, 100, transparentRed);
      addTearDown(referenceImage3.dispose);

      await expectLater(image3, matchesReferenceImage(referenceImage3));
53 54
    });

55
    testWidgetsWithLeakTracking('when images are identical', (WidgetTester tester) async {
56
      final ui.Image image = await createTestImage(100, 100, red);
57
      addTearDown(image.dispose);
58 59
      await expectLater(image, matchesReferenceImage(image));
    });
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

    testWidgetsWithLeakTracking('when widget looks the same', (WidgetTester tester) async {
      addTearDown(tester.view.reset);
      tester.view
        ..physicalSize = const Size(10, 10)
        ..devicePixelRatio = 1;

      const ValueKey<String> repaintBoundaryKey = ValueKey<String>('boundary');

      await tester.pumpWidget(
        const RepaintBoundary(
          key: repaintBoundaryKey,
          child: ColoredBox(color: red),
        ),
      );

      final ui.Image referenceImage = (tester.renderObject(find.byKey(repaintBoundaryKey)) as RenderRepaintBoundary).toImageSync();
      addTearDown(referenceImage.dispose);

      await expectLater(find.byKey(repaintBoundaryKey), matchesReferenceImage(referenceImage));
    });
81 82 83
  });

  group('fails', () {
84
    testWidgetsWithLeakTracking('when image sizes do not match', (WidgetTester tester) async {
85
      final ui.Image red50 = await createTestImage(50, 50, red);
86
      addTearDown(red50.dispose);
87
      final ui.Image red100 = await createTestImage(100, 100, red);
88 89
      addTearDown(red100.dispose);

90
      expect(
91
        await matchesReferenceImage(red50).matchAsync(red100),
92 93 94 95
        equals('does not match as width or height do not match. [100×100] != [50×50]'),
      );
    });

96
    testWidgetsWithLeakTracking('when image pixels do not match', (WidgetTester tester) async {
97
      final ui.Image red100 = await createTestImage(100, 100, red);
98
      addTearDown(red100.dispose);
99
      final ui.Image transparentRed100 = await createTestImage(100, 100, transparentRed);
100 101
      addTearDown(transparentRed100.dispose);

102
      expect(
103
        await matchesReferenceImage(red100).matchAsync(transparentRed100),
104
        equals('does not match on 57 pixels'),
105
      );
106

107
      final ui.Image green100 = await createTestImage(100, 100, green);
108 109
      addTearDown(green100.dispose);

110
      expect(
111
        await matchesReferenceImage(red100).matchAsync(green100),
112 113 114
        equals('does not match on 57 pixels'),
      );
    });
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

    testWidgetsWithLeakTracking('when widget does not look the same', (WidgetTester tester) async {
      addTearDown(tester.view.reset);
      tester.view
        ..physicalSize = const Size(10, 10)
        ..devicePixelRatio = 1;

      const ValueKey<String> repaintBoundaryKey = ValueKey<String>('boundary');

      await tester.pumpWidget(
        const RepaintBoundary(
          key: repaintBoundaryKey,
          child: ColoredBox(color: red),
        ),
      );

      final ui.Image referenceImage = (tester.renderObject(find.byKey(repaintBoundaryKey)) as RenderRepaintBoundary).toImageSync();
      addTearDown(referenceImage.dispose);

      await tester.pumpWidget(
        const RepaintBoundary(
          key: repaintBoundaryKey,
          child: ColoredBox(color: green),
        ),
      );

      expect(
        await matchesReferenceImage(referenceImage).matchAsync(
          find.byKey(repaintBoundaryKey),
        ),
        equals('does not match on 100 pixels'),
      );
    });
148 149
  });
}