From 9b3e1639db189866af55ed3a0dfd47b217106fcd Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Tue, 4 Feb 2020 13:28:55 -0800 Subject: [PATCH] Revert "Reland: Skia gold driver test (#49905)" (#50127) This reverts commit e03f439145ba5ab0f63092d71546128df165d221. --- ...lutter_driver_screenshot_test_fuchsia.dart | 2 +- .../lib/tasks/integration_tests.dart | 11 +- .../flutter_driver_screenshot_test/README.md | 12 +- .../test_driver/flutter_gold_main_test.dart | 38 ---- .../flutter_test/lib/src/_goldens_io.dart | 92 ++++++++ .../flutter_test/lib/src/_goldens_web.dart | 130 ----------- .../flutter_test/lib/src/_matchers_web.dart | 1 - .../flutter_test/lib/src/buffer_matcher.dart | 75 ------- packages/flutter_test/lib/src/goldens.dart | 209 +++++++++++------- 9 files changed, 232 insertions(+), 338 deletions(-) delete mode 100644 dev/integration_tests/flutter_driver_screenshot_test/test_driver/flutter_gold_main_test.dart delete mode 100644 packages/flutter_test/lib/src/buffer_matcher.dart diff --git a/dev/devicelab/bin/tasks/flutter_driver_screenshot_test_fuchsia.dart b/dev/devicelab/bin/tasks/flutter_driver_screenshot_test_fuchsia.dart index 424bd15a815..8d97bb9ad5d 100644 --- a/dev/devicelab/bin/tasks/flutter_driver_screenshot_test_fuchsia.dart +++ b/dev/devicelab/bin/tasks/flutter_driver_screenshot_test_fuchsia.dart @@ -10,5 +10,5 @@ import 'package:flutter_devicelab/tasks/integration_tests.dart'; Future main() async { deviceOperatingSystem = DeviceOperatingSystem.fuchsia; - await task(createFlutterDriverScreenshotTest(useFlutterGold: true)); + await task(createFlutterDriverScreenshotTest()); } diff --git a/dev/devicelab/lib/tasks/integration_tests.dart b/dev/devicelab/lib/tasks/integration_tests.dart index 5ea6c267619..17ce1d636c3 100644 --- a/dev/devicelab/lib/tasks/integration_tests.dart +++ b/dev/devicelab/lib/tasks/integration_tests.dart @@ -107,22 +107,15 @@ TaskFunction createAndroidSplashScreenKitchenSinkTest() { ); } -/// Executes a driver test that takes a screenshot and compares it against a golden image. -/// If [useFlutterGold] is true, the golden image is served by Flutter Gold -/// (https://flutter-gold.skia.org/), otherwise the golden image is read from the disk. -TaskFunction createFlutterDriverScreenshotTest({ - bool useFlutterGold = false, -}) { +TaskFunction createFlutterDriverScreenshotTest() { return DriverTest( '${flutterDirectory.path}/dev/integration_tests/flutter_driver_screenshot_test', 'lib/main.dart', - extraOptions: useFlutterGold ? const [ - '--driver', 'test_driver/flutter_gold_main_test.dart' - ] : const [] ); } class DriverTest { + DriverTest( this.testDirectory, this.testTarget, { diff --git a/dev/integration_tests/flutter_driver_screenshot_test/README.md b/dev/integration_tests/flutter_driver_screenshot_test/README.md index 110d016d2a1..071d966dadb 100644 --- a/dev/integration_tests/flutter_driver_screenshot_test/README.md +++ b/dev/integration_tests/flutter_driver_screenshot_test/README.md @@ -4,12 +4,7 @@ This tests contains an app with a main page and sub pages. The main page contains a list of buttons; each button leads to a designated sub page when tapped on. Each sub page should displays some simple UIs to screenshot tested. -The flutter driver test runs the app and opens each page to take a screenshot. - -Use `test_driver/flutter_gold_main_test.dart` to test against golden files stored on Flutter Gold. -Otherwise, use `main_test.dart` to test against golden files stored on `test_driver/goldens//.png`. - -Note that new binaries can't be checked in the Flutter repo, so use [Flutter Gold](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter) instead. +The flutter driver test runs the app and opens each page to take a screenshot. Then it compares the screenshot against a golden image stored in `test_driver/goldens//.png`. # Add a new page to test @@ -21,7 +16,6 @@ Note that new binaries can't be checked in the Flutter repo, so use [Flutter Gol An example of a `Page` subclass can be found in `lib/image_page.dart` -# Environments +# Experiments -* Device Lab which runs the app on iPhone 6s. -* LUCI which runs the app on a Fuchsia NUC device. +The test currently only runs on device lab ["mac/ios"] which runs the app on iPhone 6s. \ No newline at end of file diff --git a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/flutter_gold_main_test.dart b/dev/integration_tests/flutter_driver_screenshot_test/test_driver/flutter_gold_main_test.dart deleted file mode 100644 index b8dd6ab73a2..00000000000 --- a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/flutter_gold_main_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2014 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:async'; -import 'package:flutter_driver/flutter_driver.dart'; -import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; -import 'package:flutter_test/src/buffer_matcher.dart'; - -Future main() async { - FlutterDriver driver; - String deviceModel; - - setUpAll(() async { - driver = await FlutterDriver.connect(); - deviceModel = await driver.requestData('device_model'); - }); - - tearDownAll(() => driver.close()); - - test('A page with an image screenshot', () async { - final SerializableFinder imagePageListTile = - find.byValueKey('image_page'); - await driver.waitFor(imagePageListTile); - await driver.tap(imagePageListTile); - await driver.waitFor(find.byValueKey('red_square_image')); - await driver.waitUntilNoTransientCallbacks(); - - // TODO(egarciad): This is currently a no-op on LUCI. - // https://github.com/flutter/flutter/issues/49837 - await expectLater( - driver.screenshot(), - bufferMatchesGoldenFile('red_square_driver_screenshot__$deviceModel.png'), - ); - - await driver.tap(find.byTooltip('Back')); - }); -} diff --git a/packages/flutter_test/lib/src/_goldens_io.dart b/packages/flutter_test/lib/src/_goldens_io.dart index 2f729b37dbc..eb029cd351b 100644 --- a/packages/flutter_test/lib/src/_goldens_io.dart +++ b/packages/flutter_test/lib/src/_goldens_io.dart @@ -4,8 +4,11 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math' as math; import 'dart:typed_data'; +import 'dart:ui'; +import 'package:flutter/widgets.dart' show Element; import 'package:image/image.dart'; import 'package:path/path.dart' as path; // ignore: deprecated_member_use @@ -163,3 +166,92 @@ class LocalComparisonOutput { )); } } + +/// Returns a [ComparisonResult] to describe the pixel differential of the +/// [test] and [master] image bytes provided. +ComparisonResult compareLists(List test, List master) { + if (identical(test, master)) + return ComparisonResult(passed: true); + + if (test == null || master == null || test.isEmpty || master.isEmpty) { + return ComparisonResult( + passed: false, + error: 'Pixel test failed, null image provided.', + ); + } + + final Image testImage = decodePng(test); + final Image masterImage = decodePng(master); + + assert(testImage != null); + assert(masterImage != null); + + final int width = testImage.width; + final int height = testImage.height; + + if (width != masterImage.width || height != masterImage.height) { + return ComparisonResult( + passed: false, + error: 'Pixel test failed, image sizes do not match.\n' + 'Master Image: ${masterImage.width} X ${masterImage.height}\n' + 'Test Image: ${testImage.width} X ${testImage.height}', + ); + } + + int pixelDiffCount = 0; + final int totalPixels = width * height; + final Image invertedMaster = invert(Image.from(masterImage)); + final Image invertedTest = invert(Image.from(testImage)); + + final Map diffs = { + 'masterImage' : masterImage, + 'testImage' : testImage, + 'maskedDiff' : Image.from(testImage), + 'isolatedDiff' : Image(width, height), + }; + + for (int x = 0; x < width; x++) { + for (int y =0; y < height; y++) { + final int testPixel = testImage.getPixel(x, y); + final int masterPixel = masterImage.getPixel(x, y); + + final int diffPixel = (getRed(testPixel) - getRed(masterPixel)).abs() + + (getGreen(testPixel) - getGreen(masterPixel)).abs() + + (getBlue(testPixel) - getBlue(masterPixel)).abs() + + (getAlpha(testPixel) - getAlpha(masterPixel)).abs(); + + if (diffPixel != 0 ) { + final int invertedMasterPixel = invertedMaster.getPixel(x, y); + final int invertedTestPixel = invertedTest.getPixel(x, y); + final int maskPixel = math.max(invertedMasterPixel, invertedTestPixel); + diffs['maskedDiff'].setPixel(x, y, maskPixel); + diffs['isolatedDiff'].setPixel(x, y, maskPixel); + pixelDiffCount++; + } + } + } + + if (pixelDiffCount > 0) { + return ComparisonResult( + passed: false, + error: 'Pixel test failed, ' + '${((pixelDiffCount/totalPixels) * 100).toStringAsFixed(2)}% ' + 'diff detected.', + diffs: diffs, + ); + } + return ComparisonResult(passed: true); +} + +/// An unsupported [WebGoldenComparator] that exists for API compatibility. +class DefaultWebGoldenComparator extends WebGoldenComparator { + @override + Future compare(Element element, Size size, Uri golden) { + throw UnsupportedError('DefaultWebGoldenComparator is only supported on the web.'); + } + + @override + Future update(Uri golden, Element element, Size size) { + throw UnsupportedError('DefaultWebGoldenComparator is only supported on the web.'); + } +} diff --git a/packages/flutter_test/lib/src/_goldens_web.dart b/packages/flutter_test/lib/src/_goldens_web.dart index d282cf40543..055a8ff19e5 100644 --- a/packages/flutter_test/lib/src/_goldens_web.dart +++ b/packages/flutter_test/lib/src/_goldens_web.dart @@ -9,7 +9,6 @@ import 'dart:ui'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; -import 'package:path/path.dart' as path; // ignore: deprecated_member_use import 'package:test_api/test_api.dart' as test_package show TestFailure; @@ -36,115 +35,6 @@ ComparisonResult compareLists(List test, List master) { throw UnsupportedError('Golden testing is not supported on the web.'); } -/// Compares image pixels against a golden image file. -/// -/// Instances of this comparator will be used as the backend for -/// [matchesGoldenFile] when tests are running on Flutter Web, and will usually -/// implemented by deferring the screenshot taking and image comparison to a -/// test server. -/// -/// Instances of this comparator will be invoked by the test framework in the -/// [TestWidgetsFlutterBinding.runAsync] zone and are thus not subject to the -/// fake async constraints that are normally imposed on widget tests (i.e. the -/// need or the ability to call [WidgetTester.pump] to advance the microtask -/// queue). Prior to the invocation, the test framework will render only the -/// [Element] to be compared on the screen. -/// -/// See also: -/// -/// * [GoldenFileComparator] for the comparator to be used when the test is -/// not running in a web browser. -/// * [DefaultWebGoldenComparator] for the default [WebGoldenComparator] -/// implementation for `flutter test`. -/// * [matchesGoldenFile], the function from [flutter_test] that invokes the -/// comparator. -abstract class WebGoldenComparator { - /// Compares the rendered pixels of [element] of size [size] that is being - /// rendered on the top left of the screen against the golden file identified - /// by [golden]. - /// - /// The returned future completes with a boolean value that indicates whether - /// the pixels rendered on screen match the golden file's pixels. - /// - /// In the case of comparison mismatch, the comparator may choose to throw a - /// [TestFailure] if it wants to control the failure message, often in the - /// form of a [ComparisonResult] that provides detailed information about the - /// mismatch. - /// - /// The method by which [golden] is located and by which its bytes are loaded - /// is left up to the implementation class. For instance, some implementations - /// may load files from the local file system, whereas others may load files - /// over the network or from a remote repository. - Future compare(Element element, Size size, Uri golden); - - /// Updates the golden file identified by [golden] with rendered pixels of - /// [element]. - /// - /// This will be invoked in lieu of [compare] when [autoUpdateGoldenFiles] - /// is `true` (which gets set automatically by the test framework when the - /// user runs `flutter test --update-goldens --platform=chrome`). - /// - /// The method by which [golden] is located and by which its bytes are written - /// is left up to the implementation class. - Future update(Uri golden, Element element, Size size); - - /// Returns a new golden file [Uri] to incorporate any [version] number with - /// the [key]. - /// - /// The [version] is an optional int that can be used to differentiate - /// historical golden files. - /// - /// Version numbers are used in golden file tests for package:flutter. You can - /// learn more about these tests [here](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter). - Uri getTestUri(Uri key, int version) { - if (version == null) - return key; - final String keyString = key.toString(); - final String extension = path.extension(keyString); - return Uri.parse( - keyString - .split(extension) - .join() + '.' + version.toString() + extension - ); - } -} - -/// Compares pixels against those of a golden image file. -/// -/// This comparator is used as the backend for [matchesGoldenFile] when tests -/// are running in a web browser. -/// -/// When using `flutter test --platform=chrome`, a comparator implemented by -/// [DefaultWebGoldenComparator] is used if no other comparator is specified. It -/// will send a request to the test server, which uses [goldenFileComparator] -/// for golden file compatison. -/// -/// When using `flutter test --update-goldens`, the [DefaultWebGoldenComparator] -/// updates the files on disk to match the rendering. -/// -/// When using `flutter run`, the default comparator -/// ([_TrivialWebGoldenComparator]) is used. It prints a message to the console -/// but otherwise does nothing. This allows tests to be developed visually on a -/// web browser. -/// -/// Callers may choose to override the default comparator by setting this to a -/// custom comparator during test set-up (or using directory-level test -/// configuration). For example, some projects may wish to install a comparator -/// with tolerance levels for allowable differences. -/// -/// See also: -/// -/// * [flutter_test] for more information about how to configure tests at the -/// directory-level. -/// * [goldenFileComparator], the comparator used when tests are not running on -/// a web browser. -WebGoldenComparator get webGoldenComparator => _webGoldenComparator; -WebGoldenComparator _webGoldenComparator = const _TrivialWebGoldenComparator._(); -set webGoldenComparator(WebGoldenComparator value) { - assert(value != null); - _webGoldenComparator = value; -} - /// The default [WebGoldenComparator] implementation for `flutter test`. /// /// This comparator will send a request to the test server for golden comparison @@ -197,23 +87,3 @@ class DefaultWebGoldenComparator extends WebGoldenComparator { await compare(element, size, golden); } } - -class _TrivialWebGoldenComparator implements WebGoldenComparator { - const _TrivialWebGoldenComparator._(); - - @override - Future compare(Element element, Size size, Uri golden) { - print('Golden comparison requested for "$golden"; skipping...'); - return Future.value(true); - } - - @override - Future update(Uri golden, Element element, Size size) { - throw StateError('webGoldenComparator has not been initialized'); - } - - @override - Uri getTestUri(Uri key, int version) { - return key; - } -} diff --git a/packages/flutter_test/lib/src/_matchers_web.dart b/packages/flutter_test/lib/src/_matchers_web.dart index 4ef3bf64aea..38d1a3177e3 100644 --- a/packages/flutter_test/lib/src/_matchers_web.dart +++ b/packages/flutter_test/lib/src/_matchers_web.dart @@ -11,7 +11,6 @@ import 'package:test_api/src/frontend/async_matcher.dart'; // ignore: implementa // ignore: deprecated_member_use import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf; -import '_goldens_web.dart'; import 'binding.dart'; import 'finders.dart'; import 'goldens.dart'; diff --git a/packages/flutter_test/lib/src/buffer_matcher.dart b/packages/flutter_test/lib/src/buffer_matcher.dart deleted file mode 100644 index b83665675d5..00000000000 --- a/packages/flutter_test/lib/src/buffer_matcher.dart +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2014 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:async'; -import 'dart:typed_data'; - -import 'package:test_api/src/frontend/async_matcher.dart'; // ignore: implementation_imports -// ignore: deprecated_member_use -import 'package:test_api/test_api.dart' show Description, TestFailure; - -import 'goldens.dart'; - -/// Matcher created by [bufferMatchesGoldenFile]. -class _BufferGoldenMatcher extends AsyncMatcher { - /// Creates an instance of [BufferGoldenMatcher]. Called by [bufferMatchesGoldenFile]. - const _BufferGoldenMatcher(this.key, this.version); - - /// The [key] to the golden image. - final Uri key; - - /// The [version] of the golden image. - final int version; - - @override - Future matchAsync(dynamic item) async { - Uint8List buffer; - if (item is List) { - buffer = Uint8List.fromList(item); - } else if (item is Future>) { - buffer = Uint8List.fromList(await item); - } else { - throw 'Expected `List` or `Future>`, instead found: ${item.runtimeType}'; - } - final Uri testNameUri = goldenFileComparator.getTestUri(key, version); - if (autoUpdateGoldenFiles) { - await goldenFileComparator.update(testNameUri, buffer); - return null; - } - try { - final bool success = await goldenFileComparator.compare(buffer, testNameUri); - return success ? null : 'does not match'; - } on TestFailure catch (ex) { - return ex.message; - } - } - - @override - Description describe(Description description) { - final Uri testNameUri = goldenFileComparator.getTestUri(key, version); - return description.add('Byte buffer matches golden image "$testNameUri"'); - } -} - -/// Asserts that a [Future>], or [List[], -/// bufferMatchesGoldenFile('sample.png'), -/// ); -/// ``` -/// {@end-tool} -AsyncMatcher bufferMatchesGoldenFile(String key, {int version}) { - return _BufferGoldenMatcher(Uri.parse(key), version); -} diff --git a/packages/flutter_test/lib/src/goldens.dart b/packages/flutter_test/lib/src/goldens.dart index dadd756132d..d390d7dc1e2 100644 --- a/packages/flutter_test/lib/src/goldens.dart +++ b/packages/flutter_test/lib/src/goldens.dart @@ -3,12 +3,12 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:math' as math; import 'dart:typed_data'; -import 'package:meta/meta.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; import 'package:path/path.dart' as path; -import 'package:image/image.dart'; +import '_goldens_io.dart' if (dart.library.html) '_goldens_web.dart' as _goldens; /// Compares image pixels against a golden image file. /// @@ -98,77 +98,7 @@ abstract class GoldenFileComparator { /// Returns a [ComparisonResult] to describe the pixel differential of the /// [test] and [master] image bytes provided. static ComparisonResult compareLists(List test, List master) { - if (identical(test, master)) - return ComparisonResult(passed: true); - - if (test == null || master == null || test.isEmpty || master.isEmpty) { - return ComparisonResult( - passed: false, - error: 'Pixel test failed, null image provided.', - ); - } - - final Image testImage = decodePng(test); - final Image masterImage = decodePng(master); - - assert(testImage != null); - assert(masterImage != null); - - final int width = testImage.width; - final int height = testImage.height; - - if (width != masterImage.width || height != masterImage.height) { - return ComparisonResult( - passed: false, - error: 'Pixel test failed, image sizes do not match.\n' - 'Master Image: ${masterImage.width} X ${masterImage.height}\n' - 'Test Image: ${testImage.width} X ${testImage.height}', - ); - } - - int pixelDiffCount = 0; - final int totalPixels = width * height; - final Image invertedMaster = invert(Image.from(masterImage)); - final Image invertedTest = invert(Image.from(testImage)); - - final Map diffs = { - 'masterImage' : masterImage, - 'testImage' : testImage, - 'maskedDiff' : Image.from(testImage), - 'isolatedDiff' : Image(width, height), - }; - - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - final int testPixel = testImage.getPixel(x, y); - final int masterPixel = masterImage.getPixel(x, y); - - final int diffPixel = (getRed(testPixel) - getRed(masterPixel)).abs() - + (getGreen(testPixel) - getGreen(masterPixel)).abs() - + (getBlue(testPixel) - getBlue(masterPixel)).abs() - + (getAlpha(testPixel) - getAlpha(masterPixel)).abs(); - - if (diffPixel != 0 ) { - final int invertedMasterPixel = invertedMaster.getPixel(x, y); - final int invertedTestPixel = invertedTest.getPixel(x, y); - final int maskPixel = math.max(invertedMasterPixel, invertedTestPixel); - diffs['maskedDiff'].setPixel(x, y, maskPixel); - diffs['isolatedDiff'].setPixel(x, y, maskPixel); - pixelDiffCount++; - } - } - } - - if (pixelDiffCount > 0) { - return ComparisonResult( - passed: false, - error: 'Pixel test failed, ' - '${((pixelDiffCount/totalPixels) * 100).toStringAsFixed(2)}% ' - 'diff detected.', - diffs: diffs, - ); - } - return ComparisonResult(passed: true); + return _goldens.compareLists(test, master); } } @@ -205,6 +135,115 @@ set goldenFileComparator(GoldenFileComparator value) { _goldenFileComparator = value; } +/// Compares image pixels against a golden image file. +/// +/// Instances of this comparator will be used as the backend for +/// [matchesGoldenFile] when tests are running on Flutter Web, and will usually +/// implemented by deferring the screenshot taking and image comparison to a +/// test server. +/// +/// Instances of this comparator will be invoked by the test framework in the +/// [TestWidgetsFlutterBinding.runAsync] zone and are thus not subject to the +/// fake async constraints that are normally imposed on widget tests (i.e. the +/// need or the ability to call [WidgetTester.pump] to advance the microtask +/// queue). Prior to the invocation, the test framework will render only the +/// [Element] to be compared on the screen. +/// +/// See also: +/// +/// * [GoldenFileComparator] for the comparator to be used when the test is +/// not running in a web browser. +/// * [DefaultWebGoldenComparator] for the default [WebGoldenComparator] +/// implementation for `flutter test`. +/// * [matchesGoldenFile], the function from [flutter_test] that invokes the +/// comparator. +abstract class WebGoldenComparator { + /// Compares the rendered pixels of [element] of size [size] that is being + /// rendered on the top left of the screen against the golden file identified + /// by [golden]. + /// + /// The returned future completes with a boolean value that indicates whether + /// the pixels rendered on screen match the golden file's pixels. + /// + /// In the case of comparison mismatch, the comparator may choose to throw a + /// [TestFailure] if it wants to control the failure message, often in the + /// form of a [ComparisonResult] that provides detailed information about the + /// mismatch. + /// + /// The method by which [golden] is located and by which its bytes are loaded + /// is left up to the implementation class. For instance, some implementations + /// may load files from the local file system, whereas others may load files + /// over the network or from a remote repository. + Future compare(Element element, Size size, Uri golden); + + /// Updates the golden file identified by [golden] with rendered pixels of + /// [element]. + /// + /// This will be invoked in lieu of [compare] when [autoUpdateGoldenFiles] + /// is `true` (which gets set automatically by the test framework when the + /// user runs `flutter test --update-goldens --platform=chrome`). + /// + /// The method by which [golden] is located and by which its bytes are written + /// is left up to the implementation class. + Future update(Uri golden, Element element, Size size); + + /// Returns a new golden file [Uri] to incorporate any [version] number with + /// the [key]. + /// + /// The [version] is an optional int that can be used to differentiate + /// historical golden files. + /// + /// Version numbers are used in golden file tests for package:flutter. You can + /// learn more about these tests [here](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter). + Uri getTestUri(Uri key, int version) { + if (version == null) + return key; + final String keyString = key.toString(); + final String extension = path.extension(keyString); + return Uri.parse( + keyString + .split(extension) + .join() + '.' + version.toString() + extension + ); + } +} + +/// Compares pixels against those of a golden image file. +/// +/// This comparator is used as the backend for [matchesGoldenFile] when tests +/// are running in a web browser. +/// +/// When using `flutter test --platform=chrome`, a comparator implemented by +/// [DefaultWebGoldenComparator] is used if no other comparator is specified. It +/// will send a request to the test server, which uses [goldenFileComparator] +/// for golden file compatison. +/// +/// When using `flutter test --update-goldens`, the [DefaultWebGoldenComparator] +/// updates the files on disk to match the rendering. +/// +/// When using `flutter run`, the default comparator +/// ([_TrivialWebGoldenComparator]) is used. It prints a message to the console +/// but otherwise does nothing. This allows tests to be developed visually on a +/// web browser. +/// +/// Callers may choose to override the default comparator by setting this to a +/// custom comparator during test set-up (or using directory-level test +/// configuration). For example, some projects may wish to install a comparator +/// with tolerance levels for allowable differences. +/// +/// See also: +/// +/// * [flutter_test] for more information about how to configure tests at the +/// directory-level. +/// * [goldenFileComparator], the comparator used when tests are not running on +/// a web browser. +WebGoldenComparator get webGoldenComparator => _webGoldenComparator; +WebGoldenComparator _webGoldenComparator = const _TrivialWebGoldenComparator._(); +set webGoldenComparator(WebGoldenComparator value) { + assert(value != null); + _webGoldenComparator = value; +} + /// Whether golden files should be automatically updated during tests rather /// than compared to the image bytes recorded by the tests. /// @@ -241,7 +280,7 @@ class TrivialComparator implements GoldenFileComparator { @override Future compare(Uint8List imageBytes, Uri golden) { - print('Golden file comparison requested for "$golden"; skipping...'); + debugPrint('Golden file comparison requested for "$golden"; skipping...'); return Future.value(true); } @@ -256,6 +295,26 @@ class TrivialComparator implements GoldenFileComparator { } } +class _TrivialWebGoldenComparator implements WebGoldenComparator { + const _TrivialWebGoldenComparator._(); + + @override + Future compare(Element element, Size size, Uri golden) { + debugPrint('Golden comparison requested for "$golden"; skipping...'); + return Future.value(true); + } + + @override + Future update(Uri golden, Element element, Size size) { + throw StateError('webGoldenComparator has not been initialized'); + } + + @override + Uri getTestUri(Uri key, int version) { + return key; + } +} + /// The result of a pixel comparison test. /// /// The [ComparisonResult] will always indicate if a test has [passed]. The