blob: 79e23e65ba806951a9e37ae67800f5b1179a70b3 [file] [log] [blame]
// Copyright 2013 The Flutter 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:io';
import 'package:engine_repo_tools/engine_repo_tools.dart';
import 'package:path/path.dart' as path;
import 'package:skia_gold_client/skia_gold_client.dart';
/// An E2E test for the Skia Gold client.
///
/// Attempts to answer the question: "Does the Skia Gold client, and our custom
/// integration with it (GitHub, LUCI, etc), work as expected?" in an automated
/// way.
///
/// In a sibling directory, `e2e_fixtures`, there are static (checked-in) files
/// that represent (fake) generated output to be uploaded and verified by the
/// Skia Gold client. This test, when run on CI, will use these fixtures to
/// simulate the process of uploading and verifying real output.
///
/// For example, try changing the contents of a fixture file, and then uploading
/// a PR with this change. The CI will run this test, and it should fail (on
/// pre-submit), since the fixture file no longer matches the expected output.
///
/// Next, after the PR is merged, the CI will run this test again, and it should
/// pass (on post-submit), since the fixture file now matches the expected
/// output.
///
/// There are also tests for the "dimensions" feature of the Skia Gold client,
/// which live in `e2e_fixtures/dimensions`. These tests are similar to the
/// regular tests, but experiment with different fake dimensions to ensure that
/// our CI environment is correctly handling this feature.
void main() async {
// If the client is not available, we can't run the test.
if (!SkiaGoldClient.isAvailable()) {
stderr.writeln('Skia gold is unavailable in this environment.');
exitCode = 1;
return;
}
// If we're not in an engine repo, we can't run the test.
final Engine? engine = Engine.tryFindWithin();
if (engine == null) {
stderr.writeln('Must run within the engine repo.');
exitCode = 1;
return;
}
// Create a client.
final SkiaGoldClient skiaGoldClient = SkiaGoldClient(
engine.flutterDir,
);
// Authenticate the client.
await skiaGoldClient.auth();
const String prefix = 'SkiaGoldClientE2ETest';
const List<_Digest> digests = <_Digest>[
_Digest(
name: '${prefix}_SolidBlueSquare',
source: 'e2e_fixtures/solid_blue_square.png',
pixelCount: 512 * 512,
),
_Digest(
name: '${prefix}_SolidRedSquare',
source: 'e2e_fixtures/solid_red_square.png',
pixelCount: 768 * 768,
),
_Digest(
name: '${prefix}_SolidGreenSquare',
source: 'e2e_fixtures/solid_green_square.png',
pixelCount: 1200 * 1200,
),
];
// Upload the digests to Skia Gold.
final Set<_Digest> comparisonsFailed = <_Digest>{};
for (final _Digest digest in digests) {
final String digestPath = digest.source;
final String digestName = digest.name;
final File digestFile = File(path.join(
engine.flutterDir.path,
'testing',
'skia_gold_client',
'tool',
digestPath,
));
if (!digestFile.existsSync()) {
stderr.writeln('The digest file "$digestPath" does not exist.');
exitCode = 1;
return;
}
print('Uploading digest: $digestName ($digestPath): ${digest.pixelCount} pixels...');
try {
await skiaGoldClient.addImg(
digestName,
digestFile,
screenshotSize: 0,
);
stderr.writeln('Comparison success: $digestName');
} on Exception catch (e) {
stderr.writeln('Comparison failure: $digestName: $e');
comparisonsFailed.add(digest);
}
}
if (comparisonsFailed.isNotEmpty) {
stdout.writeln('${comparisonsFailed.length} digest(s) failed.');
for (final _Digest digest in comparisonsFailed) {
stderr.writeln(' ${digest.name} (${digest.source})');
}
exitCode = 1;
return;
}
}
final class _Digest {
const _Digest({
required this.name,
required this.source,
required this.pixelCount,
});
/// The name of the digest/test.
final String name;
/// The source of the digest (e.g. the path to the image file).
final String source;
/// The number of pixels in the image.
final int pixelCount;
@override
int get hashCode => source.hashCode;
@override
bool operator ==(Object other) {
return other is _Digest && other.source == source;
}
}