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
212 lines
7.4 KiB
Dart
212 lines
7.4 KiB
Dart
// Copyright 2019 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:convert';
|
|
import 'dart:io' as io;
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:file/local.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:platform/platform.dart';
|
|
import 'package:process/process.dart';
|
|
|
|
import 'package:flutter_goldens_client/client.dart';
|
|
|
|
// If you are here trying to figure out how to use golden files in the Flutter
|
|
// repo itself, consider reading this wiki page:
|
|
// https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter
|
|
|
|
// TODO(Piinks): This file will replace ./client.dart when transition to Skia
|
|
// Gold testing is complete
|
|
|
|
const String _kGoldctlKey = 'GOLDCTL';
|
|
const String _kServiceAccountKey = 'GOLD_SERVICE_ACCOUNT';
|
|
|
|
/// An extension of the [GoldensClient] class that interfaces with Skia Gold
|
|
/// for golden file testing.
|
|
class SkiaGoldClient extends GoldensClient {
|
|
SkiaGoldClient({
|
|
FileSystem fs = const LocalFileSystem(),
|
|
ProcessManager process = const LocalProcessManager(),
|
|
Platform platform = const LocalPlatform(),
|
|
}) : super(
|
|
fs: fs,
|
|
process: process,
|
|
platform: platform,
|
|
);
|
|
|
|
/// The local [Directory] within the [comparisonRoot] for the current test
|
|
/// context. In this directory, the client will create image and json files
|
|
/// for the goldctl tool to use.
|
|
///
|
|
/// This is informed by the [FlutterGoldenFileComparator] [basedir]. It cannot
|
|
/// be null.
|
|
Directory _workDirectory;
|
|
|
|
/// The path to the local [Directory] where the goldctl tool is hosted.
|
|
///
|
|
/// Uses the [platform] environment in this implementation.
|
|
String get _goldctl => platform.environment[_kGoldctlKey];
|
|
|
|
/// The path to the local [Directory] where the service account key is
|
|
/// hosted.
|
|
///
|
|
/// Uses the [platform] environment in this implementation.
|
|
String get _serviceAccount => platform.environment[_kServiceAccountKey];
|
|
|
|
/// Prepares the local work space for golden file testing and calls the
|
|
/// goldctl `auth` command.
|
|
///
|
|
/// This ensures that the goldctl tool is authorized and ready for testing. It
|
|
/// will only be called once for each instance of
|
|
/// [FlutterSkiaGoldFileComparator].
|
|
///
|
|
/// The [workDirectory] parameter specifies the current directory that golden
|
|
/// tests are executing in, relative to the library of the given test. It is
|
|
/// informed by the basedir of the [FlutterSkiaGoldFileComparator].
|
|
Future<void> auth(Directory workDirectory) async {
|
|
assert(workDirectory != null);
|
|
_workDirectory = workDirectory;
|
|
if (_clientIsAuthorized())
|
|
return;
|
|
|
|
final File authorization = _workDirectory.childFile('serviceAccount.json');
|
|
await authorization.writeAsString(_serviceAccount);
|
|
|
|
final List<String> authArguments = <String>[
|
|
'auth',
|
|
'--service-account', authorization.path,
|
|
'--work-dir', _workDirectory.childDirectory('temp').path,
|
|
];
|
|
|
|
final io.ProcessResult authResults = await io.Process.run(
|
|
_goldctl,
|
|
authArguments,
|
|
);
|
|
|
|
if (authResults.exitCode != 0) {
|
|
final StringBuffer buf = StringBuffer()
|
|
..writeln('Flutter + Skia Gold auth failed.')
|
|
..writeln('stdout: ${authResults.stdout}')
|
|
..writeln('stderr: ${authResults.stderr}');
|
|
throw NonZeroExitCode(authResults.exitCode, buf.toString());
|
|
}
|
|
}
|
|
|
|
/// Executes the `imgtest init` command in the goldctl tool.
|
|
///
|
|
/// The `imgtest` command collects and uploads test results to the Skia Gold
|
|
/// backend, the `init` argument initializes the testing environment.
|
|
Future<void> imgtestInit() async {
|
|
final String commitHash = await _getCurrentCommit();
|
|
final File keys = _workDirectory.childFile('keys.json');
|
|
final File failures = _workDirectory.childFile('failures.json');
|
|
|
|
await keys.writeAsString(_getKeysJSON());
|
|
await failures.create();
|
|
|
|
final List<String> imgtestInitArguments = <String>[
|
|
'imgtest', 'init',
|
|
'--instance', 'flutter',
|
|
'--work-dir', _workDirectory.childDirectory('temp').path,
|
|
'--commit', commitHash,
|
|
'--keys-file', keys.path,
|
|
'--failure-file', failures.path,
|
|
'--passfail',
|
|
];
|
|
|
|
if (imgtestInitArguments.contains(null)) {
|
|
final StringBuffer buf = StringBuffer();
|
|
buf.writeln('Null argument for Skia Gold imgtest init:');
|
|
imgtestInitArguments.forEach(buf.writeln);
|
|
throw NonZeroExitCode(1, buf.toString());
|
|
}
|
|
|
|
final io.ProcessResult imgtestInitResult = await io.Process.run(
|
|
_goldctl,
|
|
imgtestInitArguments,
|
|
);
|
|
|
|
if (imgtestInitResult.exitCode != 0) {
|
|
final StringBuffer buf = StringBuffer()
|
|
..writeln('Flutter + Skia Gold imgtest init failed.')
|
|
..writeln('stdout: ${imgtestInitResult.stdout}')
|
|
..writeln('stderr: ${imgtestInitResult.stderr}');
|
|
throw NonZeroExitCode(imgtestInitResult.exitCode, buf.toString());
|
|
}
|
|
}
|
|
|
|
/// Executes the `imgtest add` command in the goldctl tool.
|
|
///
|
|
/// The `imgtest` command collects and uploads test results to the Skia Gold
|
|
/// backend, the `add` argument uploads the current image test. A response is
|
|
/// returned from the invocation of this command that indicates a pass or fail
|
|
/// result.
|
|
///
|
|
/// The testName and goldenFile parameters reference the current comparison
|
|
/// being evaluated by the [FlutterSkiaGoldFileComparator].
|
|
Future<bool> imgtestAdd(String testName, File goldenFile) async {
|
|
assert(testName != null);
|
|
assert(goldenFile != null);
|
|
|
|
final List<String> imgtestArguments = <String>[
|
|
'imgtest', 'add',
|
|
'--work-dir', _workDirectory.childDirectory('temp').path,
|
|
'--test-name', testName.split(path.extension(testName.toString()))[0],
|
|
'--png-file', goldenFile.path,
|
|
];
|
|
|
|
await io.Process.run(
|
|
_goldctl,
|
|
imgtestArguments,
|
|
);
|
|
|
|
// TODO(Piinks): Comment on PR if triage is needed, https://github.com/flutter/flutter/issues/34673
|
|
// So as not to turn the tree red in this initial implementation, this will
|
|
// return true for now.
|
|
// The ProcessResult that returns from line 157 contains the pass/fail
|
|
// result of the test & links to the dashboard and diffs.
|
|
return true;
|
|
}
|
|
|
|
/// Returns the current commit hash of the Flutter repository.
|
|
Future<String> _getCurrentCommit() async {
|
|
if (!flutterRoot.existsSync()) {
|
|
final StringBuffer buf = StringBuffer()
|
|
..writeln('Flutter root could not be found: $flutterRoot');
|
|
throw NonZeroExitCode(1, buf.toString());
|
|
} else {
|
|
final io.ProcessResult revParse = await process.run(
|
|
<String>['git', 'rev-parse', 'HEAD'],
|
|
workingDirectory: flutterRoot.path,
|
|
);
|
|
return revParse.exitCode == 0 ? revParse.stdout.trim() : null;
|
|
}
|
|
}
|
|
|
|
/// Returns a JSON String with keys value pairs used to uniquely identify the
|
|
/// configuration that generated the given golden file.
|
|
///
|
|
/// Currently, the only key value pair being tracked is the platform the image
|
|
/// was rendered on.
|
|
String _getKeysJSON() {
|
|
return json.encode(
|
|
<String, dynamic>{
|
|
'Platform' : platform.operatingSystem,
|
|
}
|
|
);
|
|
}
|
|
|
|
/// Returns a boolean value to prevent the client from re-authorizing itself
|
|
/// for multiple tests.
|
|
bool _clientIsAuthorized() {
|
|
final File authFile = _workDirectory.childFile(super.fs.path.join(
|
|
'temp',
|
|
'auth_opt.json',
|
|
));
|
|
return authFile.existsSync();
|
|
}
|
|
}
|