@@ -12,6 +12,7 @@ import 'package:path/path.dart' as path;
12
12
import 'package:process/process.dart' ;
13
13
14
14
import 'src/errors.dart' ;
15
+ import 'src/release_version.dart' ;
15
16
16
17
export 'src/errors.dart' show SkiaGoldProcessError;
17
18
@@ -22,25 +23,89 @@ const String _kLuciEnvName = 'LUCI_CONTEXT';
22
23
const String _skiaGoldHost = 'https://flutter-engine-gold.skia.org' ;
23
24
const String _instance = 'flutter-engine' ;
24
25
25
- /// A client for uploading image tests and making baseline requests to the
26
- /// Flutter Gold Dashboard.
26
+ /// Uploads images and makes baseline requests to Skia Gold.
27
+ ///
28
+ /// For an example of how to use this class, see `tool/e2e_test.dart` .
27
29
interface class SkiaGoldClient {
28
30
/// Creates a [SkiaGoldClient] with the given [workDirectory] .
29
31
///
30
- /// [dimensions] allows to add attributes about the environment
31
- /// used to generate the screenshots.
32
- SkiaGoldClient (
32
+ /// A set of [dimensions] can be provided to add attributes about the
33
+ /// environment used to generate the screenshots, which are treated as keys
34
+ /// for the image:
35
+ ///
36
+ /// ```dart
37
+ /// final SkiaGoldClient skiaGoldClient = SkiaGoldClient(
38
+ /// someDir,
39
+ /// dimensions: <String, String>{
40
+ /// 'platform': 'linux',
41
+ /// },
42
+ /// );
43
+ /// ```
44
+ ///
45
+ /// The [verbose] flag is intended for use in debugging CI issues, and
46
+ /// produces more detailed output that some may find useful, but would be too
47
+ /// spammy for regular use.
48
+ factory SkiaGoldClient (
49
+ io.Directory workDirectory, {
50
+ Map <String , String >? dimensions,
51
+ bool verbose = false ,
52
+ }) {
53
+ return SkiaGoldClient .forTesting (
54
+ workDirectory,
55
+ dimensions: dimensions,
56
+ verbose: verbose,
57
+ );
58
+ }
59
+
60
+ /// Creates a [SkiaGoldClient] for testing.
61
+ ///
62
+ /// Similar to the default constructor, but allows for dependency injection
63
+ /// for testing purposes:
64
+ ///
65
+ /// - [httpClient] makes requests to Skia Gold to fetch expectations.
66
+ /// - [processManager] launches sub-processes.
67
+ /// - [stderr] is where output is written for diagnostics.
68
+ /// - [environment] is the environment variables for the currently running
69
+ /// process, and is used to determine if Skia Gold is available, and whether
70
+ /// the current environment is CI, and if so, if it's pre-submit or
71
+ /// post-submit.
72
+ /// - [engineRoot] is the root of the engine repository, which is used for
73
+ /// finding the current commit hash, as well as the location of the
74
+ /// `.engine-release.version` file.
75
+ @visibleForTesting
76
+ SkiaGoldClient .forTesting (
33
77
this .workDirectory, {
34
78
this .dimensions,
35
79
this .verbose = false ,
36
80
io.HttpClient ? httpClient,
37
81
ProcessManager ? processManager,
38
82
StringSink ? stderr,
39
83
Map <String , String >? environment,
84
+ Engine ? engineRoot,
40
85
}) : httpClient = httpClient ?? io.HttpClient (),
41
86
process = processManager ?? const LocalProcessManager (),
42
87
_stderr = stderr ?? io.stderr,
43
- _environment = environment ?? io.Platform .environment;
88
+ _environment = environment ?? io.Platform .environment,
89
+ _engineRoot = engineRoot ?? Engine .findWithin () {
90
+ // Lookup the release version from the engine repository.
91
+ final io.File releaseVersionFile = io.File (path.join (
92
+ _engineRoot.flutterDir.path,
93
+ '.engine-release.version' ,
94
+ ));
95
+
96
+ // If the file is not found or cannot be read, we are in an invalid state.
97
+ try {
98
+ _releaseVersion = ReleaseVersion .parse (releaseVersionFile.readAsStringSync ());
99
+ } on FormatException catch (error) {
100
+ throw StateError ('Failed to parse release version file: $error .' );
101
+ } on io.FileSystemException catch (error) {
102
+ throw StateError ('Failed to read release version file: $error .' );
103
+ }
104
+ }
105
+
106
+ /// The root of the engine repository.
107
+ final Engine _engineRoot;
108
+ ReleaseVersion ? _releaseVersion;
44
109
45
110
/// Whether the client is available and can be used in this environment.
46
111
static bool isAvailable ({
@@ -244,6 +309,14 @@ interface class SkiaGoldClient {
244
309
/// determined by the [pixelColorDelta] parameter. It's in the range [0.0,
245
310
/// 1.0] and defaults to 0.01. A value of 0.01 means that 1% of the pixels are
246
311
/// allowed to be different.
312
+ ///
313
+ /// ## Release Testing
314
+ ///
315
+ /// In release branches, we add a unique test suffix to the test name. For
316
+ /// example "testName" -> "testName_Release_3_21", based on the version in the
317
+ /// `.engine-release.version` file at the root of the engine repository.
318
+ ///
319
+ /// See <../README.md#release-testing> for more information.
247
320
Future <void > addImg (
248
321
String testName,
249
322
io.File goldenFile, {
@@ -252,6 +325,17 @@ interface class SkiaGoldClient {
252
325
required int screenshotSize,
253
326
}) async {
254
327
assert (_isPresubmit || _isPostsubmit);
328
+
329
+ // Clean the test name to remove the file extension.
330
+ testName = path.basenameWithoutExtension (testName);
331
+
332
+ // In release branches, we add a unique test suffix to the test name.
333
+ // For example "testName" -> "testName_Release_3_21".
334
+ // See ../README.md#release-testing for more information.
335
+ if (_releaseVersion case final ReleaseVersion v) {
336
+ testName = '${testName }_Release_${v .major }_${v .minor }' ;
337
+ }
338
+
255
339
if (_isPresubmit) {
256
340
await _tryjobAdd (testName, goldenFile, screenshotSize, pixelColorDelta, differentPixelsRate);
257
341
}
@@ -287,7 +371,7 @@ interface class SkiaGoldClient {
287
371
'--work-dir' ,
288
372
_tempPath,
289
373
'--test-name' ,
290
- _cleanTestName ( testName) ,
374
+ testName,
291
375
'--png-file' ,
292
376
goldenFile.path,
293
377
// Otherwise post submit will not fail.
@@ -391,7 +475,7 @@ interface class SkiaGoldClient {
391
475
'--work-dir' ,
392
476
_tempPath,
393
477
'--test-name' ,
394
- _cleanTestName ( testName) ,
478
+ testName,
395
479
'--png-file' ,
396
480
goldenFile.path,
397
481
..._getMatchingArguments (testName, screenshotSize, pixelDeltaThreshold, differentPixelsRate),
@@ -489,7 +573,7 @@ interface class SkiaGoldClient {
489
573
490
574
/// Returns the current commit hash of the engine repository.
491
575
Future <String > _getCurrentCommit () async {
492
- final String engineCheckout = Engine . findWithin () .flutterDir.path;
576
+ final String engineCheckout = _engineRoot .flutterDir.path;
493
577
final io.ProcessResult revParse = await process.run (
494
578
< String > ['git' , 'rev-parse' , 'HEAD' ],
495
579
workingDirectory: engineCheckout,
@@ -521,12 +605,6 @@ interface class SkiaGoldClient {
521
605
return json.encode (_getKeys ());
522
606
}
523
607
524
- /// Removes the file extension from the [fileName] to represent the test name
525
- /// properly.
526
- static String _cleanTestName (String fileName) {
527
- return path.basenameWithoutExtension (fileName);
528
- }
529
-
530
608
/// Returns a list of arguments for initializing a tryjob based on the testing
531
609
/// environment.
532
610
List <String > _getCIArguments () {
0 commit comments