diff --git a/tools/compare_goldens/README.md b/tools/compare_goldens/README.md new file mode 100644 index 0000000000000..4c9b1fdf04302 --- /dev/null +++ b/tools/compare_goldens/README.md @@ -0,0 +1,37 @@ +# Compare Goldens + +This is a script that will let you check golden image diffs locally. + +The directories are scanned for png files that match in name, then the diff +is written to `diff_` in the CWD. This allows you to get +results quicker than having to upload to skia gold. By default it uses fuzzy +RMSE to compare. + +## Usage + +```sh +dart run compare_goldens +``` + +Here's the steps for using this with something like impeller golden tests: + +1) Checkout a base revision +2) Build impeller_golden_tests +3) Execute `impeller_golden_tests --working_dir=\ +4) Checkout test revision +5) Build impeller_golden_tests +6) Execute `impeller_golden_tests --working_dir=\ +7) Execute `compare_goldens \ \ + +## Requirements + +- ImageMagick is installed on $PATH + +## Testing + +To run the tests: + +```sh +dart pub get +find . -name "*_test.dart" | xargs -n 1 dart --enable-asserts +``` diff --git a/tools/compare_goldens/bin/compare_goldens.dart b/tools/compare_goldens/bin/compare_goldens.dart new file mode 100644 index 0000000000000..f5f2d5a1709d5 --- /dev/null +++ b/tools/compare_goldens/bin/compare_goldens.dart @@ -0,0 +1,10 @@ +// Copyright 2013 The Flutter 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:io' show exitCode; +import 'package:compare_goldens/compare_goldens.dart' as compare_goldens; + +void main(List args) { + exitCode = compare_goldens.run(args); +} diff --git a/tools/compare_goldens/lib/compare_goldens.dart b/tools/compare_goldens/lib/compare_goldens.dart new file mode 100644 index 0000000000000..8a865e5b7eca2 --- /dev/null +++ b/tools/compare_goldens/lib/compare_goldens.dart @@ -0,0 +1,85 @@ +// Copyright 2013 The Flutter 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:io'; + +bool _hasCommandOnPath(String name) { + final ProcessResult result = Process.runSync('which', [name]); + return result.exitCode == 0; +} + +List _findPairs(Set as, Set bs) { + final List result = []; + for (final String a in as) { + if (bs.contains(a)) { + result.add(a); + } else { + print('Mix match file $a.'); + } + } + for (final String b in bs) { + if (!as.contains(b)) { + print('Mix match file $b.'); + } + } + return result; +} + +String _basename(String path) { + return path.split(Platform.pathSeparator).last; +} + +Set _grabPngFilenames(Directory dir) { + return dir.listSync() + .map((FileSystemEntity e) => _basename(e.path)) + .where((String e) => e.endsWith('.png')) + .toSet(); +} + +/// The main entry point to the tool, execute it like `main`. Returns the +/// `exitCode`. +int run(List args) { + int returnCode = 0; + if (!_hasCommandOnPath('compare')) { + throw Exception(r'Could not find `compare` from ImageMagick on $PATH.'); + } + if (args.length != 2) { + throw Exception('Usage: compare_goldens.dart '); + } + + final Directory dirA = Directory(args[0]); + if (!dirA.existsSync()) { + throw Exception('Unable to find $dirA'); + } + final Directory dirB = Directory(args[1]); + if (!dirB.existsSync()) { + throw Exception('Unable to find $dirB'); + } + + final Set filesA = _grabPngFilenames(dirA); + final Set filesB = _grabPngFilenames(dirB); + final List pairs = _findPairs(filesA, filesB); + + if (filesA.length != pairs.length || filesB.length != pairs.length) { + returnCode = 1; + } + + int count = 0; + for (final String name in pairs) { + count += 1; + final String pathA = [dirA.path, name].join(Platform.pathSeparator); + final String pathB = [dirB.path, name].join(Platform.pathSeparator); + final String output = 'diff_$name'; + print('compare ($count / ${pairs.length}) $name'); + final ProcessResult result = Process.runSync('compare', + ['-metric', 'RMSE', '-fuzz', '5%', pathA, pathB, output]); + if (result.exitCode != 0) { + print('DIFF FOUND: saved to $output'); + returnCode = 1; + } else { + File(output).deleteSync(); + } + } + return returnCode; +} diff --git a/tools/compare_goldens/pubspec.yaml b/tools/compare_goldens/pubspec.yaml new file mode 100644 index 0000000000000..c93e9a825fee6 --- /dev/null +++ b/tools/compare_goldens/pubspec.yaml @@ -0,0 +1,3 @@ +name: compare_goldens +environment: + sdk: '>=3.2.0-0 <4.0.0' diff --git a/tools/compare_goldens/test/compare_goldens_test.dart b/tools/compare_goldens/test/compare_goldens_test.dart new file mode 100644 index 0000000000000..f9b0dd79fed95 --- /dev/null +++ b/tools/compare_goldens/test/compare_goldens_test.dart @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +void main() {} diff --git a/tools/pub_get_offline.py b/tools/pub_get_offline.py index ee42941cc5bf2..e436dcad216f8 100644 --- a/tools/pub_get_offline.py +++ b/tools/pub_get_offline.py @@ -34,6 +34,7 @@ os.path.join(ENGINE_DIR, 'tools', 'api_check'), os.path.join(ENGINE_DIR, 'tools', 'build_bucket_golden_scraper'), os.path.join(ENGINE_DIR, 'tools', 'clang_tidy'), + os.path.join(ENGINE_DIR, 'tools', 'compare_goldens'), os.path.join(ENGINE_DIR, 'tools', 'const_finder'), os.path.join(ENGINE_DIR, 'tools', 'engine_tool'), os.path.join(ENGINE_DIR, 'tools', 'gen_web_locale_keymap'),