Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions lib/web_ui/dev/test_platform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -339,17 +339,20 @@ class BrowserPlatform extends PlatformPlugin {
requestData['region'] as Map<String, dynamic>;
final PixelComparison pixelComparison = PixelComparison.values.firstWhere(
(PixelComparison value) => value.toString() == requestData['pixelComparison']);
final bool isCanvaskitTest = requestData['isCanvaskitTest'] as bool;
final String result = await _diffScreenshot(
filename, write, maxDiffRate, region, pixelComparison);
filename, write, maxDiffRate, region, pixelComparison, isCanvaskitTest);
return shelf.Response.ok(json.encode(result));
}

Future<String> _diffScreenshot(
String filename,
bool write,
double maxDiffRateFailure,
Map<String, dynamic> region,
PixelComparison pixelComparison) async {
String filename,
bool write,
double maxDiffRateFailure,
Map<String, dynamic> region,
PixelComparison pixelComparison,
bool isCanvaskitTest,
) async {
if (doUpdateScreenshotGoldens) {
write = true;
}
Expand Down Expand Up @@ -387,6 +390,7 @@ class BrowserPlatform extends PlatformPlugin {
pixelComparison,
maxDiffRateFailure,
skiaClient,
isCanvaskitTest: isCanvaskitTest,
goldensDirectory: goldensDirectory,
filenameSuffix: _screenshotManager!.filenameSuffix,
write: write,
Expand Down
6 changes: 3 additions & 3 deletions web_sdk/web_engine_tester/lib/golden_tester.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import 'dart:async';
import 'dart:convert';
import 'dart:html' as html;

import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart';

import 'package:test/test.dart';
import 'package:ui/src/engine.dart' show operatingSystem, OperatingSystem, useCanvasKit;
import 'package:ui/ui.dart';

Future<dynamic> _callScreenshotServer(dynamic requestData) async {
final html.HttpRequest request = await html.HttpRequest.request(
Expand Down Expand Up @@ -63,6 +62,7 @@ Future<void> matchGoldenFile(String filename,
'height': region.height
},
'pixelComparison': pixelComparison.toString(),
'isCanvaskitTest': useCanvasKit,
};

// Chrome on macOS renders slightly differently from Linux, so allow it an
Expand Down
18 changes: 13 additions & 5 deletions web_sdk/web_test_utils/lib/image_compare.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Future<String> compareImage(
PixelComparison pixelComparison,
double maxDiffRateFailure,
SkiaGoldClient? skiaClient, {
required bool isCanvaskitTest,
// TODO(mdebbar): Remove these args with goldens repo.
String goldensDirectory = '',
String filenameSuffix = '',
Expand All @@ -43,7 +44,7 @@ Future<String> compareImage(
// comparison.

// TODO(mdebbar): Use Skia Gold for comparison, not only for uploading.
await _uploadToSkiaGold(skiaClient, screenshot, filename);
await _uploadToSkiaGold(skiaClient, screenshot, filename, isCanvaskitTest);
}

filename = filename.replaceAll('.png', '$filenameSuffix.png');
Expand Down Expand Up @@ -163,6 +164,7 @@ Future<void> _uploadToSkiaGold(
SkiaGoldClient skiaClient,
Image screenshot,
String filename,
bool isCanvaskitTest,
) async {
// Can't upload to Gold Skia unless running in LUCI.
assert(_isLuci);
Expand All @@ -172,28 +174,34 @@ Future<void> _uploadToSkiaGold(
final File goldenFile = File(p.join(environment.webUiSkiaGoldDirectory.path, filename));
await goldenFile.writeAsBytes(encodePng(screenshot), flush: true);

final int screenshotSize = screenshot.width * screenshot.height;

if (_isPreSubmit) {
return _uploadInPreSubmit(skiaClient, filename, goldenFile);
return _uploadInPreSubmit(skiaClient, filename, goldenFile, screenshotSize, isCanvaskitTest);
}
if (_isPostSubmit) {
return _uploadInPostSubmit(skiaClient, filename, goldenFile);
return _uploadInPostSubmit(skiaClient, filename, goldenFile, screenshotSize, isCanvaskitTest);
}
}

Future<void> _uploadInPreSubmit(
SkiaGoldClient skiaClient,
String filename,
File goldenFile,
int screenshotSize,
bool isCanvaskitTest,
) {
assert(_isPreSubmit);
return skiaClient.tryjobAdd(filename, goldenFile);
return skiaClient.tryjobAdd(filename, goldenFile, screenshotSize, isCanvaskitTest);
}

Future<void> _uploadInPostSubmit(
SkiaGoldClient skiaClient,
String filename,
File goldenFile,
int screenshotSize,
bool isCanvaskitTest,
) {
assert(_isPostSubmit);
return skiaClient.imgtestAdd(filename, goldenFile);
return skiaClient.imgtestAdd(filename, goldenFile, screenshotSize, isCanvaskitTest);
}
63 changes: 60 additions & 3 deletions web_sdk/web_test_utils/lib/skia_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ const String _kGoldctlKey = 'GOLDCTL';
const String _skiaGoldHost = 'https://flutter-engine-gold.skia.org';
const String _instance = 'flutter-engine';

/// The percentage of accepted pixels to be wrong.
///
/// This should be a double between 0.0 and 1.0. A value of 0.0 means we don't
/// accept any pixel to be different. A value of 1.0 means we accept 100% of
/// pixels to be different.
const double kMaxDifferentPixelsRate = 0.1;

/// A client for uploading image tests and making baseline requests to the
/// Flutter Gold Dashboard.
class SkiaGoldClient {
Expand Down Expand Up @@ -162,7 +169,12 @@ class SkiaGoldClient {
///
/// The [testName] and [goldenFile] parameters reference the current
/// comparison being evaluated.
Future<bool> imgtestAdd(String testName, File goldenFile) async {
Future<bool> imgtestAdd(
String testName,
File goldenFile,
int screenshotSize,
bool isCanvaskitTest,
) async {
await _imgtestInit();

final List<String> imgtestCommand = <String>[
Expand All @@ -171,6 +183,7 @@ class SkiaGoldClient {
'--work-dir', _tempPath,
'--test-name', cleanTestName(testName),
'--png-file', goldenFile.path,
..._getMatchingArguments(testName, screenshotSize, isCanvaskitTest),
];

final ProcessResult result = await process.run(imgtestCommand);
Expand Down Expand Up @@ -250,7 +263,12 @@ class SkiaGoldClient {
///
/// The [testName] and [goldenFile] parameters reference the current
/// comparison being evaluated.
Future<void> tryjobAdd(String testName, File goldenFile) async {
Future<void> tryjobAdd(
String testName,
File goldenFile,
int screenshotSize,
bool isCanvaskitTest,
) async {
await _tryjobInit();

final List<String> imgtestCommand = <String>[
Expand All @@ -259,6 +277,7 @@ class SkiaGoldClient {
'--work-dir', _tempPath,
'--test-name', cleanTestName(testName),
'--png-file', goldenFile.path,
..._getMatchingArguments(testName, screenshotSize, isCanvaskitTest),
];

final ProcessResult result = await process.run(imgtestCommand);
Expand All @@ -279,6 +298,44 @@ class SkiaGoldClient {
}
}

List<String> _getMatchingArguments(
String testName,
int screenshotSize,
bool isCanvaskitTest,
) {
// The algorithm to be used when matching images. The available options are:
// - "fuzzy": Allows for customizing the thresholds of pixel differences.
// - "sobel": Same as "fuzzy" but performs edge detection before performing
// a fuzzy match.
const String algorithm = 'fuzzy';

// The number of pixels in this image that are allowed to differ from the
// baseline. It's okay for this to be a slightly high number like 10% of the
// image size because those wrong pixels are constrained by
// `pixelDeltaThreshold` below.
final int maxDifferentPixels = (screenshotSize * kMaxDifferentPixelsRate).toInt();

// The maximum acceptable difference in RGB channels of each pixel.
//
// ```
// abs(r(image) - r(golden)) + abs(g(image) - g(golden)) + abs(b(image) - b(golden)) <= pixelDeltaThreshold
// ```
final String pixelDeltaThreshold;
if (isCanvaskitTest) {
pixelDeltaThreshold = '21';
} else if (browserName == 'ios-safari') {
pixelDeltaThreshold = '15';
} else {
pixelDeltaThreshold = '3';
}

return <String>[
'--add-test-optional-key', 'image_matching_algorithm:$algorithm',
'--add-test-optional-key', 'fuzzy_max_different_pixels:$maxDifferentPixels',
'--add-test-optional-key', 'fuzzy_pixel_delta_threshold:$pixelDeltaThreshold',
];
}

/// Returns the latest positive digest for the given test known to Skia Gold
/// at head.
Future<String?> getExpectationForTest(String testName) async {
Expand Down Expand Up @@ -356,9 +413,9 @@ class SkiaGoldClient {
/// browser the image was rendered on.
Map<String, dynamic> _getKeys() {
return <String, dynamic>{
'Browser': browserName,
'CI': 'luci',
'Platform': Platform.operatingSystem,
'Browser': browserName,
};
}

Expand Down