Proposal to deprecate webGoldenComparator. (#161196)

Towards https://github.com/flutter/flutter/issues/160261.

This is a PR and proposal all-in-one to deprecate, and remove, pending
the removal of the HTML backend, `WebGoldenComparator` (and related
symbols, `webGoldenComparator` and `DefaultWebGoldenComparator`).

The concept of `WebGoldenComparator` was added because the HTML-based
renderer could not support the `GoldenFileComparator` contract:


7141c2a136/packages/flutter_test/lib/src/_matchers_web.dart (L105-L108)

Once the Skia renderer (and now, SkWasm renderer) were added, it was now
possible to support the same API used by the native engine(s). This PR
conditionally, if _not_ using the HTML renderer, uses
`goldenFileComparator` instead, which re-uses the same code that
previously backed `DefaultWebGoldenFileComparator`.

No _new_ logic has been introduced in this change. This might need an
iteration or two to get right.

Feedback welcome!
This commit is contained in:
Matan Lurey 2025-01-09 11:26:08 -08:00 committed by GitHub
parent 3d433adf87
commit f2e59e738c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 80 additions and 35 deletions

View File

@ -284,8 +284,16 @@ ByteData _invert(ByteData imageBytes) {
} }
/// An unsupported [WebGoldenComparator] that exists for API compatibility. /// An unsupported [WebGoldenComparator] that exists for API compatibility.
@Deprecated(
'Use GoldenFileComparator instead. '
'This feature was deprecated after v3.28.0-0.1.pre.',
)
class DefaultWebGoldenComparator extends WebGoldenComparator { class DefaultWebGoldenComparator extends WebGoldenComparator {
/// This is provided to prevent warnings from the analyzer. /// This is provided to prevent warnings from the analyzer.
@Deprecated(
'Use a GoldenFileComparator implementation instead. '
'This feature was deprecated after v3.28.0-0.1.pre.',
)
DefaultWebGoldenComparator(Uri _) { DefaultWebGoldenComparator(Uri _) {
throw UnsupportedError('DefaultWebGoldenComparator is only supported on the web.'); throw UnsupportedError('DefaultWebGoldenComparator is only supported on the web.');
} }

View File

@ -35,6 +35,48 @@ Future<ComparisonResult> compareLists(List<int> test, List<int> master) async {
throw UnsupportedError('Golden testing is not supported on the web.'); throw UnsupportedError('Golden testing is not supported on the web.');
} }
/// Implements [GoldenFileComparator] by proxying calls to an HTTP service `/flutter_goldens`.
final class HttpProxyGoldenComparator extends GoldenFileComparator {
/// Creates a comparator with the given test file being executed.
///
/// Golden file keys will be interpreted as file paths relative to the
/// directory in which this file resides.
HttpProxyGoldenComparator(this._testUri);
final Uri _testUri;
@override
Future<bool> compare(Uint8List bytes, Uri golden) async {
final String key = golden.toString();
final String bytesEncoded = base64.encode(bytes);
final web.Response response =
await web.window
.fetch(
'flutter_goldens'.toJS,
web.RequestInit(
method: 'POST',
body:
json.encode(<String, Object>{
'testUri': _testUri.toString(),
'key': key,
'bytes': bytesEncoded,
}).toJS,
),
)
.toDart;
final String responseText = (await response.text().toDart).toDart;
if (responseText == 'true') {
return true;
}
fail(responseText);
}
@override
Future<void> update(Uri golden, Uint8List bytes) async {
// Update is handled on the server side, just use the same logic here
await compare(bytes, golden);
}
}
/// The default [WebGoldenComparator] implementation for `flutter test`. /// The default [WebGoldenComparator] implementation for `flutter test`.
/// ///
/// This comparator will send a request to the test server for golden comparison /// This comparator will send a request to the test server for golden comparison
@ -43,6 +85,10 @@ Future<ComparisonResult> compareLists(List<int> test, List<int> master) async {
/// See also: /// See also:
/// ///
/// * [matchesGoldenFile], the function that invokes the comparator. /// * [matchesGoldenFile], the function that invokes the comparator.
@Deprecated(
'Use goldenFileComparator instead. '
'This feature was deprecated after v3.28.0-0.1.pre.',
)
class DefaultWebGoldenComparator extends WebGoldenComparator { class DefaultWebGoldenComparator extends WebGoldenComparator {
/// Creates a new [DefaultWebGoldenComparator] for the specified [testUri]. /// Creates a new [DefaultWebGoldenComparator] for the specified [testUri].
/// ///
@ -50,13 +96,14 @@ class DefaultWebGoldenComparator extends WebGoldenComparator {
/// directory in which [testUri] resides. /// directory in which [testUri] resides.
/// ///
/// The [testUri] must represent a file. /// The [testUri] must represent a file.
DefaultWebGoldenComparator(this.testUri); @Deprecated(
'Use an implementation of GoldenFileComparator instead. '
'This feature was deprecated after v3.28.0-0.1.pre.',
)
DefaultWebGoldenComparator(Uri testUri) : _comparatorImpl = HttpProxyGoldenComparator(testUri);
/// The test file currently being executed. // TODO(matanlurey): Refactor as part of https://github.com/flutter/flutter/issues/160261.
/// final HttpProxyGoldenComparator _comparatorImpl;
/// Golden file keys will be interpreted as file paths relative to the
/// directory in which this file resides.
Uri testUri;
@override @override
Future<bool> compare(double width, double height, Uri golden) async { Future<bool> compare(double width, double height, Uri golden) async {
@ -69,7 +116,7 @@ class DefaultWebGoldenComparator extends WebGoldenComparator {
method: 'POST', method: 'POST',
body: body:
json.encode(<String, Object>{ json.encode(<String, Object>{
'testUri': testUri.toString(), 'testUri': _comparatorImpl._testUri.toString(),
'key': key, 'key': key,
'width': width.round(), 'width': width.round(),
'height': height.round(), 'height': height.round(),
@ -92,33 +139,11 @@ class DefaultWebGoldenComparator extends WebGoldenComparator {
@override @override
Future<bool> compareBytes(Uint8List bytes, Uri golden) async { Future<bool> compareBytes(Uint8List bytes, Uri golden) async {
final String key = golden.toString(); return _comparatorImpl.compare(bytes, golden);
final String bytesEncoded = base64.encode(bytes);
final web.Response response =
await web.window
.fetch(
'flutter_goldens'.toJS,
web.RequestInit(
method: 'POST',
body:
json.encode(<String, Object>{
'testUri': testUri.toString(),
'key': key,
'bytes': bytesEncoded,
}).toJS,
),
)
.toDart;
final String responseText = (await response.text().toDart).toDart;
if (responseText == 'true') {
return true;
}
fail(responseText);
} }
@override @override
Future<void> updateBytes(Uint8List bytes, Uri golden) async { Future<void> updateBytes(Uint8List bytes, Uri golden) async {
// Update is handled on the server side, just use the same logic here await _comparatorImpl.update(golden, bytes);
await compareBytes(bytes, golden);
} }
} }

View File

@ -85,11 +85,11 @@ class MatchesGoldenFile extends AsyncMatcher {
return 'could not encode screenshot.'; return 'could not encode screenshot.';
} }
if (autoUpdateGoldenFiles) { if (autoUpdateGoldenFiles) {
await webGoldenComparator.updateBytes(bytes.buffer.asUint8List(), key); await goldenFileComparator.update(key, bytes.buffer.asUint8List());
return null; return null;
} }
try { try {
final bool success = await webGoldenComparator.compareBytes( final bool success = await goldenFileComparator.compare(
bytes.buffer.asUint8List(), bytes.buffer.asUint8List(),
key, key,
); );
@ -126,7 +126,7 @@ class MatchesGoldenFile extends AsyncMatcher {
@override @override
Description describe(Description description) { Description describe(Description description) {
final Uri testNameUri = webGoldenComparator.getTestUri(key, version); final Uri testNameUri = goldenFileComparator.getTestUri(key, version);
return description.add('one widget whose rasterized image matches golden image "$testNameUri"'); return description.add('one widget whose rasterized image matches golden image "$testNameUri"');
} }
} }

View File

@ -7,6 +7,7 @@ import 'dart:js_interop';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'dart:ui_web' as ui_web; import 'dart:ui_web' as ui_web;
import 'package:flutter/foundation.dart' show isSkiaWeb;
import 'package:stream_channel/stream_channel.dart'; import 'package:stream_channel/stream_channel.dart';
import 'package:test_api/backend.dart'; import 'package:test_api/backend.dart';
@ -41,7 +42,14 @@ Future<void> runWebTest(WebTest test) async {
final Completer<void> completer = Completer<void>(); final Completer<void> completer = Completer<void>();
await ui_web.bootstrapEngine(runApp: () => completer.complete()); await ui_web.bootstrapEngine(runApp: () => completer.complete());
await completer.future; await completer.future;
webGoldenComparator = DefaultWebGoldenComparator(test.goldensUri);
// TODO(matanlurey): Remove webGoldenComparator when dart:html is deprecated.
// See https://github.com/flutter/flutter/issues/145954.
if (isSkiaWeb) {
goldenFileComparator = HttpProxyGoldenComparator(test.goldensUri);
} else {
webGoldenComparator = DefaultWebGoldenComparator(test.goldensUri);
}
/// This hard-codes the device pixel ratio to 3.0 and a 2400 x 1800 window /// This hard-codes the device pixel ratio to 3.0 and a 2400 x 1800 window
/// size for the purposes of testing. /// size for the purposes of testing.

View File

@ -216,6 +216,10 @@ GoldenFileComparator goldenFileComparator = const TrivialComparator._();
/// * [DefaultWebGoldenComparator] for the default [WebGoldenComparator] /// * [DefaultWebGoldenComparator] for the default [WebGoldenComparator]
/// implementation for `flutter test`. /// implementation for `flutter test`.
/// * [matchesGoldenFile], the function that invokes the comparator. /// * [matchesGoldenFile], the function that invokes the comparator.
@Deprecated(
'Use GoldenFileComparator instead. '
'This feature was deprecated after v3.28.0-0.1.pre.',
)
abstract class WebGoldenComparator { abstract class WebGoldenComparator {
/// Compares the rendered pixels of size [width]x[height] that is being /// Compares the rendered pixels of size [width]x[height] that is being
/// rendered on the top left of the screen against the golden file identified /// rendered on the top left of the screen against the golden file identified