goldens_test.dart 9.81 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2018 The Chromium 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 'dart:async';
import 'dart:io' as io;
import 'dart:typed_data';

import 'package:file/memory.dart';
10 11
import 'package:flutter_test/flutter_test.dart' hide test;
import 'package:flutter_test/flutter_test.dart' as test_package;
12

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// 1x1 transparent pixel
const List<int> _kExpectedPngBytes =
  <int>[137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
    1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 11, 73, 68, 65, 84,
    120, 1, 99, 97, 0, 2, 0, 0, 25, 0, 5, 144, 240, 54, 245, 0, 0, 0, 0, 73, 69,
    78, 68, 174, 66, 96, 130];

// 1x1 colored pixel
const List<int> _kColorFailurePngBytes =
  <int>[137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
    1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65, 84,
    120, 1, 99, 249, 207, 240, 255, 63, 0, 7, 18, 3, 2, 164, 147, 160, 197, 0,
    0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130];

// 1x2 transparent pixel
const List<int> _kSizeFailurePngBytes =
  <int>[137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
    1, 0, 0,0, 2, 8, 6, 0, 0, 0, 153, 129, 182, 39, 0, 0, 0, 14, 73, 68, 65, 84,
    120, 1, 99, 97, 0, 2, 22, 16, 1, 0, 0, 70, 0, 9, 112, 117, 150, 160, 0, 0,
    0, 0, 73, 69, 78, 68, 174, 66, 96, 130];
33 34 35 36 37 38 39 40

void main() {
  MemoryFileSystem fs;

  setUp(() {
    final FileSystemStyle style = io.Platform.isWindows
        ? FileSystemStyle.windows
        : FileSystemStyle.posix;
41
    fs = MemoryFileSystem(style: style);
42 43
  });

44 45 46 47 48 49 50 51 52 53
  /// Converts posix-style paths to the style associated with [fs].
  ///
  /// This allows us to deal in posix-style paths in the tests.
  String fix(String path) {
    if (path.startsWith('/')) {
      path = '${fs.style.drive}$path';
    }
    return path.replaceAll('/', fs.path.separator);
  }

54 55
  void test(String description, FutureOr<void> body()) {
    test_package.test(description, () {
56
      return io.IOOverrides.runZoned<FutureOr<void>>(
57 58 59 60 61 62 63 64 65 66 67 68 69
        body,
        createDirectory: (String path) => fs.directory(path),
        createFile: (String path) => fs.file(path),
        createLink: (String path) => fs.link(path),
        getCurrentDirectory: () => fs.currentDirectory,
        setCurrentDirectory: (String path) => fs.currentDirectory = path,
        getSystemTempDirectory: () => fs.systemTempDirectory,
        stat: (String path) => fs.stat(path),
        statSync: (String path) => fs.statSync(path),
        fseIdentical: (String p1, String p2) => fs.identical(p1, p2),
        fseIdenticalSync: (String p1, String p2) => fs.identicalSync(p1, p2),
        fseGetType: (String path, bool followLinks) => fs.type(path, followLinks: followLinks),
        fseGetTypeSync: (String path, bool followLinks) => fs.typeSync(path, followLinks: followLinks),
70
        fsWatch: (String a, int b, bool c) => throw UnsupportedError('unsupported'),
71 72 73 74 75 76 77 78
        fsWatchIsSupported: () => fs.isWatchSupported,
      );
    });
  }

  group('goldenFileComparator', () {
    test('is initialized by test framework', () {
      expect(goldenFileComparator, isNotNull);
79
      expect(goldenFileComparator, isInstanceOf<LocalFileComparator>());
80 81 82 83 84 85 86 87 88
      final LocalFileComparator comparator = goldenFileComparator;
      expect(comparator.basedir.path, contains('flutter_test'));
    });
  });

  group('LocalFileComparator', () {
    LocalFileComparator comparator;

    setUp(() {
89
      comparator = LocalFileComparator(fs.file(fix('/golden_test.dart')).uri, pathStyle: fs.path.style);
90 91 92
    });

    test('calculates basedir correctly', () {
93
      expect(comparator.basedir, fs.file(fix('/')).uri);
94
      comparator = LocalFileComparator(fs.file(fix('/foo/bar/golden_test.dart')).uri, pathStyle: fs.path.style);
95
      expect(comparator.basedir, fs.directory(fix('/foo/bar/')).uri);
96 97
    });

98
    test('can be instantiated with uri that represents file in same folder', () {
99
      comparator = LocalFileComparator(Uri.parse('foo_test.dart'), pathStyle: fs.path.style);
100 101 102
      expect(comparator.basedir, Uri.parse('./'));
    });

103
    group('compare', () {
104
      Future<bool> doComparison([ String golden = 'golden.png' ]) {
105
        final Uri uri = fs.file(fix(golden)).uri;
106
        return comparator.compare(
107
          Uint8List.fromList(_kExpectedPngBytes),
108 109 110 111 112 113
          uri,
        );
      }

      group('succeeds', () {
        test('when golden file is in same folder as test', () async {
114
          fs.file(fix('/golden.png')).writeAsBytesSync(_kExpectedPngBytes);
115 116 117 118 119
          final bool success = await doComparison();
          expect(success, isTrue);
        });

        test('when golden file is in subfolder of test', () async {
120
          fs.file(fix('/sub/foo.png'))
121
            ..createSync(recursive: true)
122
            ..writeAsBytesSync(_kExpectedPngBytes);
123 124 125
          final bool success = await doComparison('sub/foo.png');
          expect(success, isTrue);
        });
126 127 128 129 130

        group('when comparator instantiated with uri that represents file in same folder', () {
          test('and golden file is in same folder as test', () async {
            fs.file(fix('/foo/bar/golden.png'))
              ..createSync(recursive: true)
131
              ..writeAsBytesSync(_kExpectedPngBytes);
132
            fs.currentDirectory = fix('/foo/bar');
133
            comparator = LocalFileComparator(Uri.parse('local_test.dart'), pathStyle: fs.path.style);
134 135 136 137 138 139 140
            final bool success = await doComparison('golden.png');
            expect(success, isTrue);
          });

          test('and golden file is in subfolder of test', () async {
            fs.file(fix('/foo/bar/baz/golden.png'))
              ..createSync(recursive: true)
141
              ..writeAsBytesSync(_kExpectedPngBytes);
142
            fs.currentDirectory = fix('/foo/bar');
143
            comparator = LocalFileComparator(Uri.parse('local_test.dart'), pathStyle: fs.path.style);
144 145 146 147
            final bool success = await doComparison('baz/golden.png');
            expect(success, isTrue);
          });
        });
148 149 150
      });

      group('fails', () {
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

        test('and generates correct output in the correct location', () async {
          comparator = LocalFileComparator(Uri.parse('local_test.dart'), pathStyle: fs.path.style);
          await fs.file(fix('/golden.png')).writeAsBytes(_kColorFailurePngBytes);
          try {
            await doComparison();
            fail('TestFailure expected but not thrown.');
          } on TestFailure catch (error) {
            expect(error.message, contains('% diff detected'));
            final io.File master = fs.file(
              fix('/failures/golden_masterImage.png')
            );
            final io.File test = fs.file(
              fix('/failures/golden_testImage.png')
            );
            final io.File isolated = fs.file(
              fix('/failures/golden_isolatedDiff.png')
            );
            final io.File masked = fs.file(
              fix('/failures/golden_maskedDiff.png')
            );
            expect(master.existsSync(), isTrue);
            expect(test.existsSync(), isTrue);
            expect(isolated.existsSync(), isTrue);
            expect(masked.existsSync(), isTrue);
          }
        });

179
        test('when golden file does not exist', () async {
180 181 182 183 184 185
          try {
            await doComparison();
            fail('TestFailure expected but not thrown.');
          } on TestFailure catch (error) {
            expect(error.message, contains('Could not be compared against non-existent file'));
          }
186 187
        });

188 189 190 191 192 193 194 195
        test('when images are not the same size', () async{
          await fs.file(fix('/golden.png')).writeAsBytes(_kSizeFailurePngBytes);
          try {
            await doComparison();
            fail('TestFailure expected but not thrown.');
          } on TestFailure catch (error) {
            expect(error.message, contains('image sizes do not match'));
          }
196 197
        });

198 199 200 201 202 203 204 205
        test('when pixels do not match', () async{
          await fs.file(fix('/golden.png')).writeAsBytes(_kColorFailurePngBytes);
          try {
            await doComparison();
            fail('TestFailure expected but not thrown.');
          } on TestFailure catch (error) {
            expect(error.message, contains('% diff detected'));
          }
206 207 208
        });

        test('when golden bytes are empty', () async {
209 210 211 212 213 214 215
          await fs.file(fix('/golden.png')).writeAsBytes(<int>[]);
          try {
            await doComparison();
            fail('TestFailure expected but not thrown.');
          } on TestFailure catch (error) {
            expect(error.message, contains('null image provided'));
          }
216 217 218 219 220 221
        });
      });
    });

    group('update', () {
      test('updates existing file', () async {
222
        fs.file(fix('/golden.png')).writeAsBytesSync(_kExpectedPngBytes);
223
        const List<int> newBytes = <int>[11, 12, 13];
224
        await comparator.update(fs.file('golden.png').uri, Uint8List.fromList(newBytes));
225
        expect(fs.file(fix('/golden.png')).readAsBytesSync(), newBytes);
226 227 228
      });

      test('creates non-existent file', () async {
229
        expect(fs.file(fix('/foo.png')).existsSync(), isFalse);
230
        const List<int> newBytes = <int>[11, 12, 13];
231
        await comparator.update(fs.file('foo.png').uri, Uint8List.fromList(newBytes));
232 233
        expect(fs.file(fix('/foo.png')).existsSync(), isTrue);
        expect(fs.file(fix('/foo.png')).readAsBytesSync(), newBytes);
234 235
      });
    });
236 237 238 239 240 241 242 243 244 245 246 247 248

    group('getTestUri', () {
      test('updates file name with version number', () {
        final Uri key = Uri.parse('foo.png');
        final Uri key1 = comparator.getTestUri(key, 1);
        expect(key1, Uri.parse('foo.1.png'));
      });
      test('does nothing for null version number', () {
        final Uri key = Uri.parse('foo.png');
        final Uri keyNull = comparator.getTestUri(key, null);
        expect(keyNull, Uri.parse('foo.png'));
      });
    });
249 250
  });
}