Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
3fe2cbab
Unverified
Commit
3fe2cbab
authored
Apr 29, 2022
by
Yegor
Committed by
GitHub
Apr 29, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[web] allow small golden deltas in HTML renderer (#102791)
parent
0f280811
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
149 additions
and
6 deletions
+149
-6
flutter_goldens_test.dart
packages/flutter_goldens/test/flutter_goldens_test.dart
+83
-0
skia_client.dart
packages/flutter_goldens_client/lib/skia_client.dart
+66
-6
No files found.
packages/flutter_goldens/test/flutter_goldens_test.dart
View file @
3fe2cbab
...
...
@@ -62,6 +62,89 @@ void main() {
);
});
test
(
'web HTML test'
,
()
async
{
platform
=
FakePlatform
(
environment:
<
String
,
String
>{
'GOLDCTL'
:
'goldctl'
,
'FLUTTER_ROOT'
:
_kFlutterRoot
,
'FLUTTER_TEST_BROWSER'
:
'Chrome'
,
'FLUTTER_WEB_RENDERER'
:
'html'
,
},
operatingSystem:
'macos'
);
skiaClient
=
SkiaGoldClient
(
workDirectory
,
fs:
fs
,
process:
process
,
platform:
platform
,
httpClient:
fakeHttpClient
,
);
final
File
goldenFile
=
fs
.
file
(
'/workDirectory/temp/golden_file_test.png'
)
..
createSync
(
recursive:
true
);
const
RunInvocation
goldctlInvocation
=
RunInvocation
(
<
String
>[
'goldctl'
,
'imgtest'
,
'add'
,
'--work-dir'
,
'/workDirectory/temp'
,
'--test-name'
,
'golden_file_test'
,
'--png-file'
,
'/workDirectory/temp/golden_file_test.png'
,
'--passfail'
,
'--add-test-optional-key'
,
'image_matching_algorithm:fuzzy'
,
'--add-test-optional-key'
,
'fuzzy_max_different_pixels:20'
,
'--add-test-optional-key'
,
'fuzzy_pixel_delta_threshold:4'
,
],
null
,
);
process
.
processResults
[
goldctlInvocation
]
=
ProcessResult
(
123
,
0
,
''
,
''
);
expect
(
await
skiaClient
.
imgtestAdd
(
'golden_file_test.png'
,
goldenFile
),
isTrue
,
);
});
test
(
'web CanvasKit test'
,
()
async
{
platform
=
FakePlatform
(
environment:
<
String
,
String
>{
'GOLDCTL'
:
'goldctl'
,
'FLUTTER_ROOT'
:
_kFlutterRoot
,
'FLUTTER_TEST_BROWSER'
:
'Chrome'
,
'FLUTTER_WEB_RENDERER'
:
'canvaskit'
,
},
operatingSystem:
'macos'
);
skiaClient
=
SkiaGoldClient
(
workDirectory
,
fs:
fs
,
process:
process
,
platform:
platform
,
httpClient:
fakeHttpClient
,
);
final
File
goldenFile
=
fs
.
file
(
'/workDirectory/temp/golden_file_test.png'
)
..
createSync
(
recursive:
true
);
const
RunInvocation
goldctlInvocation
=
RunInvocation
(
<
String
>[
'goldctl'
,
'imgtest'
,
'add'
,
'--work-dir'
,
'/workDirectory/temp'
,
'--test-name'
,
'golden_file_test'
,
'--png-file'
,
'/workDirectory/temp/golden_file_test.png'
,
'--passfail'
,
],
null
,
);
process
.
processResults
[
goldctlInvocation
]
=
ProcessResult
(
123
,
0
,
''
,
''
);
expect
(
await
skiaClient
.
imgtestAdd
(
'golden_file_test.png'
,
goldenFile
),
isTrue
,
);
});
test
(
'auth performs minimal work if already authorized'
,
()
async
{
final
File
authFile
=
fs
.
file
(
'/workDirectory/temp/auth_opt.json'
)
..
createSync
(
recursive:
true
);
...
...
packages/flutter_goldens_client/lib/skia_client.dart
View file @
3fe2cbab
...
...
@@ -192,6 +192,7 @@ class SkiaGoldClient {
'--test-name'
,
cleanTestName
(
testName
),
'--png-file'
,
goldenFile
.
path
,
'--passfail'
,
...
_getPixelMatchingArguments
(),
];
final
io
.
ProcessResult
result
=
await
process
.
run
(
imgtestCommand
);
...
...
@@ -303,6 +304,7 @@ class SkiaGoldClient {
.
path
,
'--test-name'
,
cleanTestName
(
testName
),
'--png-file'
,
goldenFile
.
path
,
...
_getPixelMatchingArguments
(),
];
final
io
.
ProcessResult
result
=
await
process
.
run
(
imgtestCommand
);
...
...
@@ -323,6 +325,51 @@ class SkiaGoldClient {
}
}
// Constructs arguments for `goldctl` for controlling how pixels are compared.
//
// For AOT and CanvasKit exact pixel matching is used. For the HTML renderer
// on the web a fuzzy matching algorithm is used that allows very small deltas
// because Chromium cannot exactly reproduce the same golden on all computers.
// It seems to depend on the hardware/OS/driver combination. However, those
// differences are very small (typically not noticeable to human eye).
List
<
String
>
_getPixelMatchingArguments
()
{
// Only use fuzzy pixel matching in the HTML renderer.
if
(!
_isBrowserTest
||
_isBrowserCanvasKitTest
)
{
return
const
<
String
>[];
}
// 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.
//
// The chosen number - 20 - is arbitrary. Even for a small golden file, say
// 50 x 50, it would be less than 1% of the total number of pixels. This
// number should not grow too much. If it's growing, it is probably due to a
// larger issue that needs to be addressed at the infra level.
const
int
maxDifferentPixels
=
20
;
// The maximum acceptable difference per pixel.
//
// Uses the Manhattan distance using the RGBA color components as
// coordinates. The chosen number - 4 - is arbitrary. It's small enough to
// both not be noticeable and not trigger test flakes due to sub-pixel
// golden deltas. This number should not grow too much. If it's growing, it
// is probably due to a larger issue that needs to be addressed at the infra
// level.
const
int
pixelDeltaThreshold
=
4
;
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 Flutter
/// Gold at head.
Future
<
String
?>
getExpectationForTest
(
String
testName
)
async
{
...
...
@@ -405,10 +452,10 @@ class SkiaGoldClient {
'Platform'
:
platform
.
operatingSystem
,
'CI'
:
'luci'
,
};
if
(
platform
.
environment
[
_kTestBrowserKey
]
!=
null
)
{
keys
[
'Browser'
]
=
platform
.
environment
[
_kTestBrowserKey
]
;
if
(
_isBrowserTest
)
{
keys
[
'Browser'
]
=
_browserKey
;
keys
[
'Platform'
]
=
'
${keys['Platform']}
-browser'
;
if
(
platform
.
environment
[
_kWebRendererKey
]
==
'canvaskit'
)
{
if
(
_isBrowserCanvasKitTest
)
{
keys
[
'WebRenderer'
]
=
'canvaskit'
;
}
}
...
...
@@ -451,14 +498,27 @@ class SkiaGoldClient {
];
}
bool
get
_isBrowserTest
{
return
platform
.
environment
[
_kTestBrowserKey
]
!=
null
;
}
bool
get
_isBrowserCanvasKitTest
{
return
_isBrowserTest
&&
platform
.
environment
[
_kWebRendererKey
]
==
'canvaskit'
;
}
String
get
_browserKey
{
assert
(
_isBrowserTest
);
return
platform
.
environment
[
_kTestBrowserKey
]!;
}
/// Returns a trace id based on the current testing environment to lookup
/// the latest positive digest on Flutter Gold with a hex-encoded md5 hash of
/// the image keys.
String
getTraceID
(
String
testName
)
{
final
Map
<
String
,
dynamic
>
keys
=
<
String
,
dynamic
>{
if
(
platform
.
environment
[
_kTestBrowserKey
]
!=
null
)
'Browser'
:
platform
.
environment
[
_kTestBrowserKey
]
,
if
(
platform
.
environment
[
_kTestBrowserKey
]
!=
null
&&
platform
.
environment
[
_kWebRendererKey
]
==
'canvaskit'
)
if
(
_isBrowserTest
)
'Browser'
:
_browserKey
,
if
(
_isBrowserCanvasKitTest
)
'WebRenderer'
:
'canvaskit'
,
'CI'
:
'luci'
,
'Platform'
:
platform
.
operatingSystem
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment