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.
@Deprecated(
'Use GoldenFileComparator instead. '
'This feature was deprecated after v3.28.0-0.1.pre.',
)
class DefaultWebGoldenComparator extends WebGoldenComparator {
/// 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 _) {
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.');
}
/// 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`.
///
/// 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:
///
/// * [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 {
/// Creates a new [DefaultWebGoldenComparator] for the specified [testUri].
///
@ -50,13 +96,14 @@ class DefaultWebGoldenComparator extends WebGoldenComparator {
/// directory in which [testUri] resides.
///
/// 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.
///
/// Golden file keys will be interpreted as file paths relative to the
/// directory in which this file resides.
Uri testUri;
// TODO(matanlurey): Refactor as part of https://github.com/flutter/flutter/issues/160261.
final HttpProxyGoldenComparator _comparatorImpl;
@override
Future<bool> compare(double width, double height, Uri golden) async {
@ -69,7 +116,7 @@ class DefaultWebGoldenComparator extends WebGoldenComparator {
method: 'POST',
body:
json.encode(<String, Object>{
'testUri': testUri.toString(),
'testUri': _comparatorImpl._testUri.toString(),
'key': key,
'width': width.round(),
'height': height.round(),
@ -92,33 +139,11 @@ class DefaultWebGoldenComparator extends WebGoldenComparator {
@override
Future<bool> compareBytes(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);
return _comparatorImpl.compare(bytes, golden);
}
@override
Future<void> updateBytes(Uint8List bytes, Uri golden) async {
// Update is handled on the server side, just use the same logic here
await compareBytes(bytes, golden);
await _comparatorImpl.update(golden, bytes);
}
}

View File

@ -85,11 +85,11 @@ class MatchesGoldenFile extends AsyncMatcher {
return 'could not encode screenshot.';
}
if (autoUpdateGoldenFiles) {
await webGoldenComparator.updateBytes(bytes.buffer.asUint8List(), key);
await goldenFileComparator.update(key, bytes.buffer.asUint8List());
return null;
}
try {
final bool success = await webGoldenComparator.compareBytes(
final bool success = await goldenFileComparator.compare(
bytes.buffer.asUint8List(),
key,
);
@ -126,7 +126,7 @@ class MatchesGoldenFile extends AsyncMatcher {
@override
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"');
}
}

View File

@ -7,6 +7,7 @@ import 'dart:js_interop';
import 'dart:ui' as ui;
import 'dart:ui_web' as ui_web;
import 'package:flutter/foundation.dart' show isSkiaWeb;
import 'package:stream_channel/stream_channel.dart';
import 'package:test_api/backend.dart';
@ -41,7 +42,14 @@ Future<void> runWebTest(WebTest test) async {
final Completer<void> completer = Completer<void>();
await ui_web.bootstrapEngine(runApp: () => completer.complete());
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
/// size for the purposes of testing.

View File

@ -216,6 +216,10 @@ GoldenFileComparator goldenFileComparator = const TrivialComparator._();
/// * [DefaultWebGoldenComparator] for the default [WebGoldenComparator]
/// implementation for `flutter test`.
/// * [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 {
/// 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