[web] Use fuzzy matching in Gold (flutter/engine#29847)

This commit is contained in:
Mouad Debbar
2021-11-29 13:07:33 -08:00
committed by GitHub
parent cde08a7f21
commit b44dfd45b3
4 changed files with 86 additions and 17 deletions

View File

@@ -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;
}
@@ -387,6 +390,7 @@ class BrowserPlatform extends PlatformPlugin {
pixelComparison,
maxDiffRateFailure,
skiaClient,
isCanvaskitTest: isCanvaskitTest,
goldensDirectory: goldensDirectory,
filenameSuffix: _screenshotManager!.filenameSuffix,
write: write,

View File

@@ -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(
@@ -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

View File

@@ -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 = '',
@@ -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');
@@ -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);
@@ -172,11 +174,13 @@ 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);
}
}
@@ -184,16 +188,20 @@ 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);
}

View File

@@ -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 {
@@ -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>[
@@ -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);
@@ -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>[
@@ -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);
@@ -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 {
@@ -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,
};
}