mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

* Fresh PR for Gold integration. * Nits * WIP * Artifacts from merge * Changed some platform dependencies for web, added library prefix notation for Skia Gold test names. * Updating for CI implementation * Write out service account * Writing to skip out * WIP * ++ * Fixing depot tools deps * Windows depot_tools * Fixing setup scripts * ++ * depot tools * ++ * WIP * Tracing depot_tools clone * WIP * ++ * analyzer * WIP * chrome typo * copy artifact * Working on tests * Code cleanup * ++ * Code cleanup, updated tests * ++ review feedback * Review * Analyzer * Review feedback * Nits from review * PRogress * ++ * Fixing tests * ++ * Testing repo route * Just needing documention around new structures. * cleanup * Analyzer * Documentation updates * Documentation updates * Cirrus updates * cirrus nit * Review feedback * Review feedback * Fixing skip comparator * Fix base directory for Skia Gold case * ++ * Feedback * ++ * Fixed uri assertion * Made GoldensClient abstract, altered SkiaGoldClient constructor * Analyzer
294 lines
11 KiB
Dart
294 lines
11 KiB
Dart
// Copyright 2018 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:file/local.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:meta/meta.dart';
|
|
import 'package:platform/platform.dart';
|
|
|
|
import 'package:flutter_goldens_client/client.dart';
|
|
import 'package:flutter_goldens_client/skia_client.dart';
|
|
|
|
export 'package:flutter_goldens_client/client.dart';
|
|
export 'package:flutter_goldens_client/skia_client.dart';
|
|
|
|
/// Main method that can be used in a `flutter_test_config.dart` file to set
|
|
/// [goldenFileComparator] to an instance of [FlutterGoldenFileComparator] that
|
|
/// works for the current test. _Which_ FlutterGoldenFileComparator is
|
|
/// instantiated is based on the current testing environment.
|
|
Future<void> main(FutureOr<void> testMain()) async {
|
|
const Platform platform = LocalPlatform();
|
|
if (FlutterSkiaGoldFileComparator.isAvailableOnPlatform(platform)) {
|
|
goldenFileComparator = await FlutterSkiaGoldFileComparator.fromDefaultComparator();
|
|
} else if (FlutterGoldensRepositoryFileComparator.isAvailableOnPlatform(platform)) {
|
|
goldenFileComparator = await FlutterGoldensRepositoryFileComparator.fromDefaultComparator();
|
|
} else {
|
|
goldenFileComparator = FlutterSkippingGoldenFileComparator.fromDefaultComparator();
|
|
}
|
|
await testMain();
|
|
}
|
|
|
|
/// Abstract base class golden file comparator specific to the `flutter/flutter`
|
|
/// repository.
|
|
abstract class FlutterGoldenFileComparator extends GoldenFileComparator {
|
|
/// Creates a [FlutterGoldenFileComparator] that will resolve golden file
|
|
/// URIs relative to the specified [basedir].
|
|
///
|
|
/// The [fs] and [platform] parameters useful in tests, where the default file
|
|
/// system and platform can be replaced by mock instances.
|
|
@visibleForTesting
|
|
FlutterGoldenFileComparator(
|
|
this.basedir, {
|
|
this.fs = const LocalFileSystem(),
|
|
this.platform = const LocalPlatform(),
|
|
}) : assert(basedir != null),
|
|
assert(fs != null),
|
|
assert(platform != null);
|
|
|
|
/// The directory to which golden file URIs will be resolved in [compare] and
|
|
/// [update].
|
|
final Uri basedir;
|
|
|
|
/// The file system used to perform file access.
|
|
@visibleForTesting
|
|
final FileSystem fs;
|
|
|
|
/// A wrapper for the [dart:io.Platform] API.
|
|
@visibleForTesting
|
|
final Platform platform;
|
|
|
|
@override
|
|
Future<void> update(Uri golden, Uint8List imageBytes) async {
|
|
final File goldenFile = getGoldenFile(golden);
|
|
await goldenFile.parent.create(recursive: true);
|
|
await goldenFile.writeAsBytes(imageBytes, flush: true);
|
|
}
|
|
|
|
/// Calculate the appropriate basedir for the current test context.
|
|
@protected
|
|
@visibleForTesting
|
|
static Directory getBaseDirectory(GoldensClient goldens, LocalFileComparator defaultComparator) {
|
|
final FileSystem fs = goldens.fs;
|
|
final Directory testDirectory = fs.directory(defaultComparator.basedir);
|
|
final String testDirectoryRelativePath = fs.path.relative(testDirectory.path, from: goldens.flutterRoot.path);
|
|
return goldens.comparisonRoot.childDirectory(testDirectoryRelativePath);
|
|
}
|
|
|
|
/// Returns the golden [File] identified by the given [Uri].
|
|
@protected
|
|
File getGoldenFile(Uri uri) {
|
|
assert(basedir.scheme == 'file');
|
|
final File goldenFile = fs.directory(basedir).childFile(fs.file(uri).path);
|
|
assert(goldenFile.uri.scheme == 'file');
|
|
return goldenFile;
|
|
}
|
|
}
|
|
|
|
/// A [FlutterGoldenFileComparator] for testing golden images against the
|
|
/// `flutter/goldens` repository.
|
|
///
|
|
/// Within the https://github.com/flutter/flutter repository, it's important
|
|
/// not to check-in binaries in order to keep the size of the repository to a
|
|
/// minimum. To satisfy this requirement, this comparator retrieves the golden
|
|
/// files from a sibling repository, `flutter/goldens`.
|
|
///
|
|
/// This comparator will locally clone the `flutter/goldens` repository into
|
|
/// the `$FLUTTER_ROOT/bin/cache/pkg/goldens` folder using the
|
|
/// [GoldensRepositoryClient], then perform the comparison against the files
|
|
/// therein.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [GoldenFileComparator], the abstract class that
|
|
/// [FlutterGoldenFileComparator] implements.
|
|
/// * [FlutterSkiaGoldFileComparator], another [FlutterGoldenFileComparator]
|
|
/// that tests golden images through Skia Gold.
|
|
class FlutterGoldensRepositoryFileComparator extends FlutterGoldenFileComparator {
|
|
/// Creates a [FlutterGoldensRepositoryFileComparator] that will test golden
|
|
/// file images against the `flutter/goldens` repository.
|
|
///
|
|
/// The [fs] and [platform] parameters useful in tests, where the default file
|
|
/// system and platform can be replaced by mock instances.
|
|
FlutterGoldensRepositoryFileComparator(
|
|
Uri basedir, {
|
|
FileSystem fs = const LocalFileSystem(),
|
|
Platform platform = const LocalPlatform(),
|
|
}) : super(
|
|
basedir,
|
|
fs: fs,
|
|
platform: platform,
|
|
);
|
|
|
|
/// Creates a new [FlutterGoldensRespositoryFileComparator] that mirrors the
|
|
/// relative path resolution of the default [goldenFileComparator].
|
|
///
|
|
/// By the time the future completes, the clone of the `flutter/goldens`
|
|
/// repository is guaranteed to be ready to use.
|
|
///
|
|
/// The [goldens] and [defaultComparator] parameters are visible for testing
|
|
/// purposes only.
|
|
static Future<FlutterGoldensRepositoryFileComparator> fromDefaultComparator({
|
|
GoldensRepositoryClient goldens,
|
|
LocalFileComparator defaultComparator,
|
|
}) async {
|
|
defaultComparator ??= goldenFileComparator;
|
|
|
|
// Prepare the goldens repo.
|
|
goldens ??= GoldensRepositoryClient();
|
|
await goldens.prepare();
|
|
|
|
final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory(goldens, defaultComparator);
|
|
return FlutterGoldensRepositoryFileComparator(baseDirectory.uri);
|
|
}
|
|
|
|
@override
|
|
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
|
|
final File goldenFile = getGoldenFile(golden);
|
|
if (!goldenFile.existsSync()) {
|
|
throw TestFailure('Could not be compared against non-existent file: "$golden"');
|
|
}
|
|
final List<int> goldenBytes = await goldenFile.readAsBytes();
|
|
if (goldenBytes.length != imageBytes.length) {
|
|
return false;
|
|
}
|
|
for (int i = 0; i < goldenBytes.length; i++) {
|
|
if (goldenBytes[i] != imageBytes[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Decides based on the current platform whether goldens tests should be
|
|
/// performed against the flutter/goldens repository.
|
|
static bool isAvailableOnPlatform(Platform platform) => platform.isLinux;
|
|
}
|
|
|
|
/// A [FlutterGoldenFileComparator] for testing golden images with Skia Gold.
|
|
///
|
|
/// For testing across all platforms, the [SkiaGoldClient] is used to upload
|
|
/// images for framework-related golden tests and process results. Currently
|
|
/// these tests are designed to be run post-submit on Cirrus CI, informed by the
|
|
/// environment.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [GoldenFileComparator], the abstract class that
|
|
/// [FlutterGoldenFileComparator] implements.
|
|
/// * [FlutterGoldensRepositoryFileComparator], another
|
|
/// [FlutterGoldenFileComparator] that tests golden images using the
|
|
/// flutter/goldens repository.
|
|
class FlutterSkiaGoldFileComparator extends FlutterGoldenFileComparator {
|
|
/// Creates a [FlutterSkiaGoldFileComparator] that will test golden file
|
|
/// images against Skia Gold.
|
|
///
|
|
/// The [fs] and [platform] parameters useful in tests, where the default file
|
|
/// system and platform can be replaced by mock instances.
|
|
FlutterSkiaGoldFileComparator(
|
|
final Uri basedir,
|
|
this.skiaClient, {
|
|
FileSystem fs = const LocalFileSystem(),
|
|
Platform platform = const LocalPlatform(),
|
|
}) : super(
|
|
basedir,
|
|
fs: fs,
|
|
platform: platform,
|
|
);
|
|
|
|
final SkiaGoldClient skiaClient;
|
|
|
|
/// Creates a new [FlutterSkiaGoldFileComparator] that mirrors the relative
|
|
/// path resolution of the default [goldenFileComparator].
|
|
///
|
|
/// The [goldens] and [defaultComparator] parameters are visible for testing
|
|
/// purposes only.
|
|
static Future<FlutterSkiaGoldFileComparator> fromDefaultComparator({
|
|
SkiaGoldClient goldens,
|
|
LocalFileComparator defaultComparator,
|
|
}) async {
|
|
defaultComparator ??= goldenFileComparator;
|
|
goldens ??= SkiaGoldClient();
|
|
|
|
final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory(goldens, defaultComparator);
|
|
if (!baseDirectory.existsSync())
|
|
baseDirectory.createSync(recursive: true);
|
|
await goldens.auth(baseDirectory);
|
|
await goldens.imgtestInit();
|
|
return FlutterSkiaGoldFileComparator(baseDirectory.uri, goldens);
|
|
}
|
|
|
|
@override
|
|
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
|
|
golden = _addPrefix(golden);
|
|
await update(golden, imageBytes);
|
|
|
|
final File goldenFile = getGoldenFile(golden);
|
|
if (!goldenFile.existsSync()) {
|
|
throw TestFailure('Could not be compared against non-existent file: "$golden"');
|
|
}
|
|
return await skiaClient.imgtestAdd(golden.path, goldenFile);
|
|
}
|
|
|
|
@override
|
|
Uri getTestUri(Uri key, int version) => key;
|
|
|
|
/// Decides based on the current environment whether goldens tests should be
|
|
/// performed against Skia Gold.
|
|
static bool isAvailableOnPlatform(Platform platform) {
|
|
final String cirrusCI = platform.environment['CIRRUS_CI'] ?? '';
|
|
final String cirrusPR = platform.environment['CIRRUS_PR'] ?? '';
|
|
final String cirrusBranch = platform.environment['CIRRUS_BRANCH'] ?? '';
|
|
return cirrusCI.isNotEmpty && cirrusPR.isEmpty && cirrusBranch == 'master';
|
|
}
|
|
|
|
/// Prepends the golden Uri with the library name that encloses the current
|
|
/// test.
|
|
Uri _addPrefix(Uri golden) {
|
|
final String prefix = basedir.pathSegments[basedir.pathSegments.length - 2];
|
|
return Uri.parse(prefix + '.' + golden.toString());
|
|
}
|
|
}
|
|
|
|
/// A [FlutterGoldenFileComparator] for skipping golden image tests when Skia
|
|
/// Gold is unavailable or the current platform that is executing tests is not
|
|
/// Linux.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [FlutterGoldensRepositoryFileComparator], another
|
|
/// [FlutterGoldenFileComparator] that tests golden images using the
|
|
/// flutter/goldens repository.
|
|
/// * [FlutterSkiaGoldFileComparator], another [FlutterGoldenFileComparator]
|
|
/// that tests golden images through Skia Gold.
|
|
class FlutterSkippingGoldenFileComparator extends FlutterGoldenFileComparator {
|
|
/// Creates a [FlutterSkippingGoldenFileComparator] that will skip tests that
|
|
/// are not in the right environment for golden file testing.
|
|
FlutterSkippingGoldenFileComparator(Uri basedir) : super(basedir);
|
|
|
|
/// Creates a new [FlutterSkippingGoldenFileComparator] that mirrors the relative
|
|
/// path resolution of the default [goldenFileComparator].
|
|
static FlutterSkippingGoldenFileComparator fromDefaultComparator({
|
|
LocalFileComparator defaultComparator,
|
|
}) {
|
|
defaultComparator ??= goldenFileComparator;
|
|
return FlutterSkippingGoldenFileComparator(defaultComparator.basedir);
|
|
}
|
|
|
|
@override
|
|
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
|
|
print('Skipping "$golden" test : Skia Gold is not available in this testing '
|
|
'environment and flutter/goldens repository comparison is only available '
|
|
'on Linux machines.'
|
|
);
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
Future<void> update(Uri golden, Uint8List imageBytes) => null;
|
|
}
|