1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | /// @docImport 'dart:io'; |
6 | library; |
7 | |
8 | import 'dart:async' show FutureOr; |
9 | import 'dart:io' as io show HttpClient, OSError, SocketException; |
10 | |
11 | import 'package:file/file.dart'; |
12 | import 'package:file/local.dart'; |
13 | import 'package:flutter/foundation.dart'; |
14 | import 'package:flutter_test/flutter_test.dart'; |
15 | import 'package:platform/platform.dart'; |
16 | import 'package:process/process.dart'; |
17 | |
18 | import 'skia_client.dart'; |
19 | export 'skia_client.dart'; |
20 | |
21 | // If you are here trying to figure out how to use golden files in the Flutter |
22 | // repo itself, consider reading this wiki page: |
23 | // https://github.com/flutter/flutter/blob/main/docs/contributing/testing/Writing-a-golden-file-test-for-package-flutter.md |
24 | |
25 | // If you are trying to debug this package, you may like to use the golden test |
26 | // titled "Inconsequential golden test" in this file: |
27 | // /packages/flutter/test/widgets/basic_test.dart |
28 | |
29 | // TODO(ianh): sort the parameters and arguments in this file so they use a consistent order throughout. |
30 | |
31 | const String _kFlutterRootKey = 'FLUTTER_ROOT'; |
32 | |
33 | bool _isMainBranch(String? branch) { |
34 | return branch == 'main' |
35 | || branch == 'master'; |
36 | } |
37 | |
38 | |
39 | /// Main method that can be used in a `flutter_test_config.dart` file to set |
40 | /// [goldenFileComparator] to an instance of [FlutterGoldenFileComparator] that |
41 | /// works for the current test. _Which_ [FlutterGoldenFileComparator] is |
42 | /// instantiated is based on the current testing environment. |
43 | /// |
44 | /// When set, the `namePrefix` is prepended to the names of all gold images. |
45 | /// |
46 | /// This function assumes the [goldenFileComparator] has been set to a |
47 | /// [LocalFileComparator], which happens in the bootstrap code used when running |
48 | /// tests using `flutter test`. This should not be called when running a test |
49 | /// using `flutter run`, as in that environment, the [goldenFileComparator] is a |
50 | /// [TrivialComparator]. |
51 | /// |
52 | /// An [HttpClient] is created when this method is called. That client is used |
53 | /// to communicate with the Skia Gold servers. Any [HttpOverrides] set in this |
54 | /// will affect whether this is effective or not. For example, if the current |
55 | /// override provides a mock client that always fails, then all calls to gold |
56 | /// comparison functions will fail. |
57 | Future<void> testExecutable(FutureOr<void> Function() testMain, {String? namePrefix}) async { |
58 | assert( |
59 | goldenFileComparator is LocalFileComparator, |
60 | 'The flutter_goldens package should be used from a flutter_test_config.dart ' |
61 | 'file, which is only invoked when using "flutter test". The "flutter test" ' |
62 | 'bootstrap logic sets "goldenFileComparator" to a LocalFileComparator. It ' |
63 | 'appears in this instance however that the "goldenFileComparator" is a ' |
64 | '${goldenFileComparator.runtimeType} .\n' |
65 | 'See also: https://flutter.dev/to/flutter-test-docs', |
66 | ); |
67 | const Platform platform = LocalPlatform(); |
68 | const FileSystem fs = LocalFileSystem(); |
69 | const ProcessManager process = LocalProcessManager(); |
70 | final io.HttpClient httpClient = io.HttpClient(); |
71 | if (FlutterPostSubmitFileComparator.isForEnvironment(platform)) { |
72 | goldenFileComparator = await FlutterPostSubmitFileComparator.fromLocalFileComparator( |
73 | localFileComparator: goldenFileComparator as LocalFileComparator, |
74 | platform: platform, |
75 | namePrefix: namePrefix, |
76 | log: print, |
77 | fs: fs, |
78 | process: process, |
79 | httpClient: httpClient, |
80 | ); |
81 | } else if (FlutterPreSubmitFileComparator.isForEnvironment(platform)) { |
82 | goldenFileComparator = await FlutterPreSubmitFileComparator.fromLocalFileComparator( |
83 | localFileComparator: goldenFileComparator as LocalFileComparator, |
84 | platform: platform, |
85 | namePrefix: namePrefix, |
86 | log: print, |
87 | fs: fs, |
88 | process: process, |
89 | httpClient: httpClient, |
90 | ); |
91 | } else if (FlutterSkippingFileComparator.isForEnvironment(platform)) { |
92 | goldenFileComparator = FlutterSkippingFileComparator.fromLocalFileComparator( |
93 | localFileComparator: goldenFileComparator as LocalFileComparator, |
94 | 'Golden file testing is not executed on Cirrus, or LUCI environments ' |
95 | 'outside of flutter/flutter, or in test shards that are not configured ' |
96 | 'for using goldctl.', |
97 | platform: platform, |
98 | namePrefix: namePrefix, |
99 | log: print, |
100 | fs: fs, |
101 | process: process, |
102 | httpClient: httpClient, |
103 | ); |
104 | } else { |
105 | goldenFileComparator = await FlutterLocalFileComparator.fromLocalFileComparator( |
106 | localFileComparator: goldenFileComparator as LocalFileComparator, |
107 | platform: platform, |
108 | log: print, |
109 | fs: fs, |
110 | process: process, |
111 | httpClient: httpClient, |
112 | ); |
113 | } |
114 | await testMain(); |
115 | } |
116 | |
117 | /// Abstract base class golden file comparator specific to the `flutter/flutter` |
118 | /// repository. |
119 | /// |
120 | /// Golden file testing for the `flutter/flutter` repository is handled by three |
121 | /// different [FlutterGoldenFileComparator]s, depending on the current testing |
122 | /// environment. |
123 | /// |
124 | /// * The [FlutterPostSubmitFileComparator] is utilized during post-submit |
125 | /// testing, after a pull request has landed on the master branch. This |
126 | /// comparator uses the [SkiaGoldClient] and the `goldctl` tool to upload |
127 | /// tests to the [Flutter Gold dashboard](https://flutter-gold.skia.org). |
128 | /// Flutter Gold manages the master golden files for the `flutter/flutter` |
129 | /// repository. |
130 | /// |
131 | /// * The [FlutterPreSubmitFileComparator] is utilized in pre-submit testing, |
132 | /// before a pull request lands on the master branch. This |
133 | /// comparator uses the [SkiaGoldClient] to execute tryjobs, allowing |
134 | /// contributors to view and check in visual differences before landing the |
135 | /// change. |
136 | /// |
137 | /// * The [FlutterLocalFileComparator] is used for local development testing. |
138 | /// This comparator will use the [SkiaGoldClient] to request baseline images |
139 | /// from [Flutter Gold](https://flutter-gold.skia.org) and manually compare |
140 | /// pixels. If a difference is detected, this comparator will |
141 | /// generate failure output illustrating the found difference. If a baseline |
142 | /// is not found for a given test image, it will consider it a new test and |
143 | /// output the new image for verification. |
144 | /// |
145 | /// The [FlutterSkippingFileComparator] is utilized to skip tests outside |
146 | /// of the appropriate environments described above. Currently, some Luci |
147 | /// environments do not execute golden file testing, and as such do not require |
148 | /// a comparator. This comparator is also used when an internet connection is |
149 | /// unavailable. |
150 | abstract class FlutterGoldenFileComparator extends GoldenFileComparator { |
151 | /// Creates a [FlutterGoldenFileComparator] that will resolve golden file |
152 | /// URIs relative to the specified [basedir], and retrieve golden baselines |
153 | /// using the [skiaClient]. The [basedir] is used for writing and accessing |
154 | /// information and files for interacting with the [skiaClient]. When testing |
155 | /// locally, the [basedir] will also contain any diffs from failed tests, or |
156 | /// goldens generated from newly introduced tests. |
157 | @visibleForTesting |
158 | FlutterGoldenFileComparator( |
159 | this.basedir, |
160 | this.skiaClient, { |
161 | required this.fs, |
162 | required this.platform, |
163 | this.namePrefix, |
164 | required this.log, |
165 | }); |
166 | |
167 | /// The directory to which golden file URIs will be resolved in [compare] and |
168 | /// [update]. |
169 | final Uri basedir; |
170 | |
171 | /// A client for uploading image tests and making baseline requests to the |
172 | /// Flutter Gold Dashboard. |
173 | final SkiaGoldClient skiaClient; |
174 | |
175 | /// The file system used to perform file access. |
176 | final FileSystem fs; |
177 | |
178 | /// The environment (current working directory, identity of the OS, |
179 | /// environment variables, etc). |
180 | final Platform platform; |
181 | |
182 | /// The prefix that is added to all golden names. |
183 | final String? namePrefix; |
184 | |
185 | /// The logging function to use when reporting messages to the console. |
186 | final LogCallback log; |
187 | |
188 | @override |
189 | Future<void> update(Uri golden, Uint8List imageBytes) async { |
190 | final File goldenFile = getGoldenFile(golden); |
191 | await goldenFile.parent.create(recursive: true); |
192 | await goldenFile.writeAsBytes(imageBytes, flush: true); |
193 | } |
194 | |
195 | @override |
196 | Uri getTestUri(Uri key, int? version) => key; |
197 | |
198 | /// Calculate the appropriate basedir for the current test context. |
199 | /// |
200 | /// The optional [suffix] argument is used by the |
201 | /// [FlutterPostSubmitFileComparator] and the [FlutterPreSubmitFileComparator]. |
202 | /// These [FlutterGoldenFileComparator]s randomize their base directories to |
203 | /// maintain thread safety while using the `goldctl` tool. |
204 | @protected |
205 | @visibleForTesting |
206 | static Directory getBaseDirectory( |
207 | LocalFileComparator defaultComparator, { |
208 | required Platform platform, |
209 | String? suffix, |
210 | required FileSystem fs, |
211 | }) { |
212 | final Directory flutterRoot = fs.directory(platform.environment[_kFlutterRootKey]); |
213 | final Directory comparisonRoot = switch (suffix) { |
214 | null => flutterRoot.childDirectory(fs.path.join('bin','cache','pkg','skia_goldens')), |
215 | _ => fs.systemTempDirectory.createTempSync(suffix), |
216 | }; |
217 | |
218 | final String testPath = fs.directory(defaultComparator.basedir).path; |
219 | return comparisonRoot.childDirectory( |
220 | fs.path.relative(testPath, from: flutterRoot.path), |
221 | ); |
222 | } |
223 | |
224 | /// Returns the golden [File] identified by the given [Uri]. |
225 | @protected |
226 | File getGoldenFile(Uri uri) { |
227 | final File goldenFile = fs.directory(basedir).childFile(fs.file(uri).path); |
228 | return goldenFile; |
229 | } |
230 | |
231 | /// Prepends the golden URL with the library name that encloses the current |
232 | /// test. |
233 | Uri _addPrefix(Uri golden) { |
234 | // Ensure the Uri ends in .png as the SkiaClient expects |
235 | assert( |
236 | golden.toString().split('.').last =='png', |
237 | 'Golden files in the Flutter framework must end with the file extension ' |
238 | '.png.' |
239 | ); |
240 | return Uri.parse(<String>[ |
241 | if (namePrefix != null) |
242 | namePrefix!, |
243 | basedir.pathSegments[basedir.pathSegments.length - 2], |
244 | golden.toString(), |
245 | ].join('.')); |
246 | } |
247 | } |
248 | |
249 | /// A [FlutterGoldenFileComparator] for testing golden images with Skia Gold in |
250 | /// post-submit. |
251 | /// |
252 | /// For testing across all platforms, the [SkiaGoldClient] is used to upload |
253 | /// images for framework-related golden tests and process results. |
254 | /// |
255 | /// See also: |
256 | /// |
257 | /// * [GoldenFileComparator], the abstract class that |
258 | /// [FlutterGoldenFileComparator] implements. |
259 | /// * [FlutterPreSubmitFileComparator], another |
260 | /// [FlutterGoldenFileComparator] that tests golden images before changes are |
261 | /// merged into the master branch. |
262 | /// * [FlutterLocalFileComparator], another |
263 | /// [FlutterGoldenFileComparator] that tests golden images locally on your |
264 | /// current machine. |
265 | class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator { |
266 | /// Creates a [FlutterPostSubmitFileComparator] that will test golden file |
267 | /// images against Skia Gold. |
268 | /// |
269 | /// The [fs] parameter is useful in tests, where the default |
270 | /// file system can be replaced by mock instances. |
271 | FlutterPostSubmitFileComparator( |
272 | super.basedir, |
273 | super.skiaClient, { |
274 | required super.fs, |
275 | required super.platform, |
276 | super.namePrefix, |
277 | required super.log, |
278 | }); |
279 | |
280 | /// Creates a new [FlutterPostSubmitFileComparator] that mirrors the relative |
281 | /// path resolution of the provided `localFileComparator`. |
282 | /// |
283 | /// The [goldens] parameter is visible for testing purposes only. |
284 | static Future<FlutterPostSubmitFileComparator> fromLocalFileComparator({ |
285 | SkiaGoldClient? goldens, |
286 | required LocalFileComparator localFileComparator, |
287 | required Platform platform, |
288 | String? namePrefix, |
289 | required LogCallback log, |
290 | required FileSystem fs, |
291 | required ProcessManager process, |
292 | required io.HttpClient httpClient, |
293 | }) async { |
294 | final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory( |
295 | localFileComparator, |
296 | platform: platform, |
297 | suffix:'flutter_goldens_postsubmit.', |
298 | fs: fs, |
299 | ); |
300 | baseDirectory.createSync(recursive: true); |
301 | |
302 | goldens ??= SkiaGoldClient( |
303 | baseDirectory, |
304 | log: log, |
305 | platform: platform, |
306 | fs: fs, |
307 | process: process, |
308 | httpClient: httpClient, |
309 | ); |
310 | await goldens.auth(); |
311 | return FlutterPostSubmitFileComparator( |
312 | baseDirectory.uri, |
313 | goldens, |
314 | platform: platform, |
315 | namePrefix: namePrefix, |
316 | log: log, |
317 | fs: fs, |
318 | ); |
319 | } |
320 | |
321 | @override |
322 | Future<bool> compare(Uint8List imageBytes, Uri golden) async { |
323 | await skiaClient.imgtestInit(); |
324 | golden = _addPrefix(golden); |
325 | await update(golden, imageBytes); |
326 | final File goldenFile = getGoldenFile(golden); |
327 | return skiaClient.imgtestAdd(golden.path, goldenFile); |
328 | } |
329 | |
330 | /// Decides based on the current environment if goldens tests should be |
331 | /// executed through Skia Gold. |
332 | static bool isForEnvironment(Platform platform) { |
333 | final bool luciPostSubmit = platform.environment.containsKey('SWARMING_TASK_ID') |
334 | && platform.environment.containsKey('GOLDCTL') |
335 | // Luci tryjob environments contain this value to inform the [FlutterPreSubmitComparator]. |
336 | && !platform.environment.containsKey('GOLD_TRYJOB') |
337 | // Only run on main branch. |
338 | && _isMainBranch(platform.environment['GIT_BRANCH']); |
339 | return luciPostSubmit; |
340 | } |
341 | } |
342 | |
343 | /// A [FlutterGoldenFileComparator] for testing golden images before changes are |
344 | /// merged into the master branch. The comparator executes tryjobs using the |
345 | /// [SkiaGoldClient]. |
346 | /// |
347 | /// See also: |
348 | /// |
349 | /// * [GoldenFileComparator], the abstract class that |
350 | /// [FlutterGoldenFileComparator] implements. |
351 | /// * [FlutterPostSubmitFileComparator], another |
352 | /// [FlutterGoldenFileComparator] that uploads tests to the Skia Gold |
353 | /// dashboard in post-submit. |
354 | /// * [FlutterLocalFileComparator], another |
355 | /// [FlutterGoldenFileComparator] that tests golden images locally on your |
356 | /// current machine. |
357 | class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { |
358 | /// Creates a [FlutterPreSubmitFileComparator] that will test golden file |
359 | /// images against baselines requested from Flutter Gold. |
360 | /// |
361 | /// The [fs] parameter is useful in tests, where the default |
362 | /// file system can be replaced by mock instances. |
363 | FlutterPreSubmitFileComparator( |
364 | super.basedir, |
365 | super.skiaClient, { |
366 | required super.fs, |
367 | required super.platform, |
368 | super.namePrefix, |
369 | required super.log, |
370 | }); |
371 | |
372 | /// Creates a new [FlutterPreSubmitFileComparator] that mirrors the |
373 | /// relative path resolution of the default [goldenFileComparator]. |
374 | /// |
375 | /// The [goldens] parameter is visible for testing purposes only. |
376 | static Future<FlutterGoldenFileComparator> fromLocalFileComparator({ |
377 | SkiaGoldClient? goldens, |
378 | required LocalFileComparator localFileComparator, |
379 | required Platform platform, |
380 | Directory? testBasedir, |
381 | String? namePrefix, |
382 | required LogCallback log, |
383 | required FileSystem fs, |
384 | required ProcessManager process, |
385 | required io.HttpClient httpClient, |
386 | }) async { |
387 | final Directory baseDirectory = testBasedir ?? FlutterGoldenFileComparator.getBaseDirectory( |
388 | localFileComparator, |
389 | platform: platform, |
390 | suffix:'flutter_goldens_presubmit.', |
391 | fs: fs, |
392 | ); |
393 | |
394 | if (!baseDirectory.existsSync()) { |
395 | baseDirectory.createSync(recursive: true); |
396 | } |
397 | |
398 | goldens ??= SkiaGoldClient( |
399 | baseDirectory, |
400 | platform: platform, |
401 | log: log, |
402 | fs: fs, |
403 | process: process, |
404 | httpClient: httpClient, |
405 | ); |
406 | |
407 | await goldens.auth(); |
408 | return FlutterPreSubmitFileComparator( |
409 | baseDirectory.uri, |
410 | goldens, |
411 | platform: platform, |
412 | namePrefix: namePrefix, |
413 | log: log, |
414 | fs: fs, |
415 | ); |
416 | } |
417 | |
418 | @override |
419 | Future<bool> compare(Uint8List imageBytes, Uri golden) async { |
420 | await skiaClient.tryjobInit(); |
421 | golden = _addPrefix(golden); |
422 | await update(golden, imageBytes); |
423 | final File goldenFile = getGoldenFile(golden); |
424 | |
425 | await skiaClient.tryjobAdd(golden.path, goldenFile); |
426 | |
427 | // This will always return true since golden file test failures are managed |
428 | // in pre-submit checks by the flutter-gold status check. |
429 | return true; |
430 | } |
431 | |
432 | /// Decides based on the current environment if goldens tests should be |
433 | /// executed as pre-submit tests with Skia Gold. |
434 | static bool isForEnvironment(Platform platform) { |
435 | final bool luciPreSubmit = platform.environment.containsKey('SWARMING_TASK_ID') |
436 | && platform.environment.containsKey('GOLDCTL') |
437 | && platform.environment.containsKey('GOLD_TRYJOB') |
438 | // Only run on the main branch |
439 | && _isMainBranch(platform.environment['GIT_BRANCH']); |
440 | return luciPreSubmit; |
441 | } |
442 | } |
443 | |
444 | /// A [FlutterGoldenFileComparator] for testing conditions that do not execute |
445 | /// golden file tests. |
446 | /// |
447 | /// Currently, this comparator is used on Cirrus, or in Luci environments when executing tests |
448 | /// outside of the flutter/flutter repository. |
449 | /// |
450 | /// See also: |
451 | /// |
452 | /// * [FlutterPostSubmitFileComparator], another [FlutterGoldenFileComparator] |
453 | /// that tests golden images through Skia Gold. |
454 | /// * [FlutterPreSubmitFileComparator], another |
455 | /// [FlutterGoldenFileComparator] that tests golden images before changes are |
456 | /// merged into the master branch. |
457 | /// * [FlutterLocalFileComparator], another |
458 | /// [FlutterGoldenFileComparator] that tests golden images locally on your |
459 | /// current machine. |
460 | class FlutterSkippingFileComparator extends FlutterGoldenFileComparator { |
461 | /// Creates a [FlutterSkippingFileComparator] that will skip tests that |
462 | /// are not in the right environment for golden file testing. |
463 | FlutterSkippingFileComparator( |
464 | super.basedir, |
465 | super.skiaClient, |
466 | this.reason, { |
467 | super.namePrefix, |
468 | required super.platform, |
469 | required super.log, |
470 | required super.fs, |
471 | }); |
472 | |
473 | /// Describes the reason for using the [FlutterSkippingFileComparator]. |
474 | final String reason; |
475 | |
476 | /// Creates a new [FlutterSkippingFileComparator] that mirrors the |
477 | /// relative path resolution of the given [localFileComparator]. |
478 | static FlutterSkippingFileComparator fromLocalFileComparator( |
479 | String reason, { |
480 | required LocalFileComparator localFileComparator, |
481 | String? namePrefix, |
482 | required Platform platform, |
483 | required LogCallback log, |
484 | required FileSystem fs, |
485 | required ProcessManager process, |
486 | required io.HttpClient httpClient, |
487 | }) { |
488 | final Uri basedir = localFileComparator.basedir; |
489 | final SkiaGoldClient skiaClient = SkiaGoldClient( |
490 | fs.directory(basedir), |
491 | platform: platform, |
492 | log: log, |
493 | fs: fs, |
494 | process: process, |
495 | httpClient: httpClient, |
496 | ); |
497 | return FlutterSkippingFileComparator( |
498 | basedir, |
499 | skiaClient, |
500 | reason, |
501 | namePrefix: namePrefix, |
502 | platform: platform, |
503 | log: log, |
504 | fs: fs, |
505 | ); |
506 | } |
507 | |
508 | @override |
509 | Future<bool> compare(Uint8List imageBytes, Uri golden) async { |
510 | log('Skipping "$golden" test:$reason'); |
511 | return true; |
512 | } |
513 | |
514 | @override |
515 | Future<void> update(Uri golden, Uint8List imageBytes) async {} |
516 | |
517 | /// Decides, based on the current environment, if this comparator should be |
518 | /// used. |
519 | /// |
520 | /// If we are in a CI environment, LUCI or Cirrus, but are not using the other |
521 | /// comparators, we skip. Otherwise we would fallback to the local comparator, |
522 | /// for which failures cannot be resolved in a CI environment. |
523 | static bool isForEnvironment(Platform platform) { |
524 | return platform.environment.containsKey('SWARMING_TASK_ID') |
525 | // Some builds are still being run on Cirrus, we should skip these. |
526 | || platform.environment.containsKey('CIRRUS_CI'); |
527 | } |
528 | } |
529 | |
530 | /// A [FlutterGoldenFileComparator] for testing golden images locally on your |
531 | /// current machine. |
532 | /// |
533 | /// This comparator utilizes the [SkiaGoldClient] to request baseline images for |
534 | /// the given device under test for comparison. This comparator is initialized |
535 | /// when conditions for all other [FlutterGoldenFileComparator]s have not been |
536 | /// met, see the `isForEnvironment` method for each one listed below. |
537 | /// |
538 | /// The [FlutterLocalFileComparator] is intended to run on local machines and |
539 | /// serve as a smoke test during development. As such, it will not be able to |
540 | /// detect unintended changes on environments other than the currently executing |
541 | /// machine, until they are tested using the [FlutterPreSubmitFileComparator]. |
542 | /// |
543 | /// See also: |
544 | /// |
545 | /// * [GoldenFileComparator], the abstract class that |
546 | /// [FlutterGoldenFileComparator] implements. |
547 | /// * [FlutterPostSubmitFileComparator], another |
548 | /// [FlutterGoldenFileComparator] that uploads tests to the Skia Gold |
549 | /// dashboard. |
550 | /// * [FlutterPreSubmitFileComparator], another |
551 | /// [FlutterGoldenFileComparator] that tests golden images before changes are |
552 | /// merged into the master branch. |
553 | /// * [FlutterSkippingFileComparator], another |
554 | /// [FlutterGoldenFileComparator] that controls post-submit testing |
555 | /// conditions that do not execute golden file tests. |
556 | class FlutterLocalFileComparator extends FlutterGoldenFileComparator with LocalComparisonOutput { |
557 | /// Creates a [FlutterLocalFileComparator] that will test golden file |
558 | /// images against baselines requested from Flutter Gold. |
559 | /// |
560 | /// The [fs] parameter is useful in tests, where the default |
561 | /// file system can be replaced by mock instances. |
562 | FlutterLocalFileComparator( |
563 | super.basedir, |
564 | super.skiaClient, { |
565 | required super.fs, |
566 | required super.platform, |
567 | required super.log, |
568 | }); |
569 | |
570 | /// Creates a new [FlutterLocalFileComparator] that mirrors the |
571 | /// relative path resolution of the given [localFileComparator]. |
572 | /// |
573 | /// The [goldens] and [baseDirectory] parameters are |
574 | /// visible for testing purposes only. |
575 | static Future<FlutterGoldenFileComparator> fromLocalFileComparator({ |
576 | SkiaGoldClient? goldens, |
577 | required LocalFileComparator localFileComparator, |
578 | required Platform platform, |
579 | Directory? baseDirectory, |
580 | required LogCallback log, |
581 | required FileSystem fs, |
582 | required ProcessManager process, |
583 | required io.HttpClient httpClient, |
584 | }) async { |
585 | baseDirectory ??= FlutterGoldenFileComparator.getBaseDirectory( |
586 | localFileComparator, |
587 | platform: platform, |
588 | fs: fs, |
589 | ); |
590 | |
591 | if (!baseDirectory.existsSync()) { |
592 | baseDirectory.createSync(recursive: true); |
593 | } |
594 | |
595 | goldens ??= SkiaGoldClient( |
596 | baseDirectory, |
597 | platform: platform, |
598 | log: log, |
599 | fs: fs, |
600 | process: process, |
601 | httpClient: httpClient, |
602 | ); |
603 | try { |
604 | // Check if we can reach Gold. |
605 | await goldens.getExpectationForTest(''); |
606 | } on io.OSError catch (_) { |
607 | return FlutterSkippingFileComparator( |
608 | baseDirectory.uri, |
609 | goldens, |
610 | 'OSError occurred, could not reach Gold. ' |
611 | 'Switching to FlutterSkippingGoldenFileComparator.', |
612 | platform: platform, |
613 | log: log, |
614 | fs: fs, |
615 | ); |
616 | } on io.SocketException catch (_) { |
617 | return FlutterSkippingFileComparator( |
618 | baseDirectory.uri, |
619 | goldens, |
620 | 'SocketException occurred, could not reach Gold. ' |
621 | 'Switching to FlutterSkippingGoldenFileComparator.', |
622 | platform: platform, |
623 | log: log, |
624 | fs: fs, |
625 | ); |
626 | } on FormatException catch (_) { |
627 | return FlutterSkippingFileComparator( |
628 | baseDirectory.uri, |
629 | goldens, |
630 | 'FormatException occurred, could not reach Gold. ' |
631 | 'Switching to FlutterSkippingGoldenFileComparator.', |
632 | platform: platform, |
633 | log: log, |
634 | fs: fs, |
635 | ); |
636 | } |
637 | |
638 | return FlutterLocalFileComparator( |
639 | baseDirectory.uri, |
640 | goldens, |
641 | platform: platform, |
642 | log: log, |
643 | fs: fs, |
644 | ); |
645 | } |
646 | |
647 | @override |
648 | Future<bool> compare(Uint8List imageBytes, Uri golden) async { |
649 | golden = _addPrefix(golden); |
650 | final String testName = skiaClient.cleanTestName(golden.path); |
651 | late String? testExpectation; |
652 | testExpectation = await skiaClient.getExpectationForTest(testName); |
653 | |
654 | if (testExpectation == null || testExpectation.isEmpty) { |
655 | log( |
656 | 'No expectations provided by Skia Gold for test:$golden. ' |
657 | 'This may be a new test. If this is an unexpected result, check ' |
658 | 'https://flutter-gold.skia.org.\n' |
659 | 'Validate image output found at$basedir' |
660 | ); |
661 | update(golden, imageBytes); |
662 | return true; |
663 | } |
664 | |
665 | ComparisonResult result; |
666 | final List<int> goldenBytes = await skiaClient.getImageBytes(testExpectation); |
667 | |
668 | result = await GoldenFileComparator.compareLists( |
669 | imageBytes, |
670 | goldenBytes, |
671 | ); |
672 | |
673 | if (result.passed) { |
674 | result.dispose(); |
675 | return true; |
676 | } |
677 | |
678 | final String error = await generateFailureOutput(result, golden, basedir); |
679 | result.dispose(); |
680 | throw FlutterError(error); |
681 | } |
682 | } |
683 |
Definitions
- _kFlutterRootKey
- _isMainBranch
- testExecutable
- FlutterGoldenFileComparator
- FlutterGoldenFileComparator
- update
- getTestUri
- getBaseDirectory
- getGoldenFile
- _addPrefix
- FlutterPostSubmitFileComparator
- FlutterPostSubmitFileComparator
- fromLocalFileComparator
- compare
- isForEnvironment
- FlutterPreSubmitFileComparator
- FlutterPreSubmitFileComparator
- fromLocalFileComparator
- compare
- isForEnvironment
- FlutterSkippingFileComparator
- FlutterSkippingFileComparator
- fromLocalFileComparator
- compare
- update
- isForEnvironment
- FlutterLocalFileComparator
- FlutterLocalFileComparator
- fromLocalFileComparator
Learn more about Flutter for embedded and desktop on industrialflutter.com