| // 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:typed_data'; |
| |
| import 'package:file/file.dart'; |
| import 'package:file/local.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'package:meta/meta.dart'; |
| |
| import 'package:flutter_goldens_client/client.dart'; |
| export 'package:flutter_goldens_client/client.dart'; |
| |
| /// Main method that can be used in a `flutter_test_config.dart` file to set |
| /// [goldenFileComparator] to an instance of [FlutterGoldenFileComparator] that |
| /// works for the current test. |
| Future<void> main(FutureOr<void> testMain()) async { |
| goldenFileComparator = await FlutterGoldenFileComparator.fromDefaultComparator(); |
| await testMain(); |
| } |
| |
| /// A golden file comparator specific to the `flutter/flutter` repository. |
| /// |
| /// Within the https://github.com/flutter/flutter repository, it's important |
| /// not to check-in binaries in order to keep the size of the repository to a |
| /// minimum. To satisfy this requirement, this comparator retrieves the golden |
| /// files from a sibling repository, `flutter/goldens`. |
| /// |
| /// This comparator will locally clone the `flutter/goldens` repository into |
| /// the `$FLUTTER_ROOT/bin/cache/pkg/goldens` folder, then perform the comparison against |
| /// the files therein. |
| class FlutterGoldenFileComparator implements GoldenFileComparator { |
| /// Creates a [FlutterGoldenFileComparator] that will resolve golden file |
| /// URIs relative to the specified [basedir]. |
| /// |
| /// The [fs] parameter exists for testing purposes only. |
| @visibleForTesting |
| FlutterGoldenFileComparator( |
| this.basedir, { |
| this.fs = const LocalFileSystem(), |
| }); |
| |
| /// The directory to which golden file URIs will be resolved in [compare] and [update]. |
| final Uri basedir; |
| |
| /// The file system used to perform file access. |
| @visibleForTesting |
| final FileSystem fs; |
| |
| /// Creates a new [FlutterGoldenFileComparator] that mirrors the relative |
| /// path resolution of the default [goldenFileComparator]. |
| /// |
| /// By the time the future completes, the clone of the `flutter/goldens` |
| /// repository is guaranteed to be ready use. |
| /// |
| /// The [goldens] and [defaultComparator] parameters are visible for testing |
| /// purposes only. |
| static Future<FlutterGoldenFileComparator> fromDefaultComparator({ |
| GoldensClient goldens, |
| LocalFileComparator defaultComparator, |
| }) async { |
| defaultComparator ??= goldenFileComparator; |
| |
| // Prepare the goldens repo. |
| goldens ??= GoldensClient(); |
| await goldens.prepare(); |
| |
| // Calculate the appropriate basedir for the current test context. |
| final FileSystem fs = goldens.fs; |
| final Directory testDirectory = fs.directory(defaultComparator.basedir); |
| final String testDirectoryRelativePath = fs.path.relative(testDirectory.path, from: goldens.flutterRoot.path); |
| return FlutterGoldenFileComparator(goldens.repositoryRoot.childDirectory(testDirectoryRelativePath).uri); |
| } |
| |
| @override |
| Future<bool> compare(Uint8List imageBytes, Uri golden) async { |
| final File goldenFile = _getGoldenFile(golden); |
| if (!goldenFile.existsSync()) { |
| throw TestFailure('Could not be compared against non-existent file: "$golden"'); |
| } |
| final List<int> goldenBytes = await goldenFile.readAsBytes(); |
| // TODO(tvolkert): Improve the intelligence of this comparison. |
| if (goldenBytes.length != imageBytes.length) { |
| return false; |
| } |
| for (int i = 0; i < goldenBytes.length; i++) { |
| if (goldenBytes[i] != imageBytes[i]) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @override |
| Future<void> update(Uri golden, Uint8List imageBytes) async { |
| final File goldenFile = _getGoldenFile(golden); |
| await goldenFile.parent.create(recursive: true); |
| await goldenFile.writeAsBytes(imageBytes, flush: true); |
| } |
| |
| File _getGoldenFile(Uri uri) { |
| return fs.directory(basedir).childFile(fs.file(uri).path); |
| } |
| } |