未验证 提交 c3bfbec9 编写于 作者: Y Yegor 提交者: GitHub

delete golden files; switch to flutter/goldens (#12434)

Delete golden files from flutter/engine; switch to flutter/goldens
上级 48ad8a08
......@@ -20,3 +20,15 @@ or:
```
felt build --watch
```
## Configuration files
`chrome_lock.yaml` contains the version of Chrome we use to test Flutter for
web. Chrome is not automatically updated whenever a new release is available.
Instead, we update this file manually once in a while.
`goldens_lock.yaml` refers to a revision in the https://github.com/flutter/goldens
repo. Screenshot tests are compared with the golden files at that revision.
When making engine changes that affect screenshots, first submit a PR to
flutter/goldens updating the screenshots. Then update this file pointing to
the new revision.
......@@ -103,4 +103,17 @@ class Environment {
webUiRootDir.path,
'.dart_tool',
));
/// Path to the "dev" directory containing engine developer tools and
/// configuration files.
io.Directory get webUiDevDir => io.Directory(pathlib.join(
webUiRootDir.path,
'dev',
));
/// Path to the clone of the flutter/goldens repository.
io.Directory get webUiGoldensRepositoryDirectory => io.Directory(pathlib.join(
webUiDartToolDir.path,
'goldens',
));
}
......@@ -3,6 +3,11 @@
// found in the LICENSE file.
import 'dart:io' as io;
import 'package:image/image.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
import 'environment.dart';
import 'utils.dart';
void main(List<String> args) {
final io.File fileA = io.File(args[0]);
......@@ -10,7 +15,7 @@ void main(List<String> args) {
final Image imageA = decodeNamedImage(fileA.readAsBytesSync(), 'a.png');
final Image imageB = decodeNamedImage(fileB.readAsBytesSync(), 'b.png');
final ImageDiff diff = ImageDiff(golden: imageA, other: imageB);
print('Diff: ${(diff.rate * 100).toStringAsFixed(4)}');
print('Diff: ${(diff.rate * 100).toStringAsFixed(4)}%');
}
/// This class encapsulates visually diffing an Image with any other.
......@@ -140,3 +145,72 @@ class ImageDiff {
String getPrintableDiffFilesInfo(double diffRate, double maxRate) =>
'(${((diffRate) * 100).toStringAsFixed(4)}% of pixels were different. '
'Maximum allowed rate is: ${(maxRate * 100).toStringAsFixed(4)}%).';
/// Fetches golden files from github.com/flutter/goldens, cloning the repository if necessary.
///
/// The repository is cloned into web_ui/.dart_tool.
Future<void> fetchGoldens() async {
await _GoldensRepoFetcher().fetch();
}
class _GoldensRepoFetcher {
String _repository;
String _revision;
Future<void> fetch() async {
final io.File lockFile = io.File(
path.join(environment.webUiDevDir.path, 'goldens_lock.yaml')
);
final YamlMap lock = loadYaml(lockFile.readAsStringSync());
_repository = lock['repository'];
_revision = lock['revision'];
final String localRevision = await _getLocalRevision();
if (localRevision == _revision) {
return;
}
print('Fetching $_repository@$_revision');
if (!environment.webUiGoldensRepositoryDirectory.existsSync()) {
environment.webUiGoldensRepositoryDirectory.createSync(recursive: true);
await runProcess(
'git',
<String>['init'],
workingDirectory: environment.webUiGoldensRepositoryDirectory.path,
mustSucceed: true,
);
await runProcess(
'git',
<String>['remote', 'add', 'origin', _repository],
workingDirectory: environment.webUiGoldensRepositoryDirectory.path,
mustSucceed: true,
);
}
await runProcess(
'git',
<String>['fetch', 'origin', 'master'],
workingDirectory: environment.webUiGoldensRepositoryDirectory.path,
mustSucceed: true,
);
await runProcess(
'git',
<String>['checkout', _revision],
workingDirectory: environment.webUiGoldensRepositoryDirectory.path,
mustSucceed: true,
);
}
Future<String> _getLocalRevision() async {
final io.File head = io.File(path.join(
environment.webUiGoldensRepositoryDirectory.path, '.git', 'HEAD'
));
if (!head.existsSync()) {
return null;
}
return head.readAsStringSync().trim();
}
}
repository: https://github.com/flutter/goldens.git
revision: dd993a32c23c5c542f083134467e7cda09cac975
......@@ -147,10 +147,28 @@ class BrowserPlatform extends PlatformPlugin {
}
Future<String> _diffScreenshot(String filename, bool write, [ Map<String, dynamic> region ]) async {
const String _kGoldensDirectory = 'test/golden_files';
String goldensDirectory;
if (filename.startsWith('__local__')) {
filename = filename.substring('__local__/'.length);
goldensDirectory = p.join(
env.environment.webUiRootDir.path,
'test',
'golden_files',
);
} else {
await fetchGoldens();
goldensDirectory = p.join(
env.environment.webUiGoldensRepositoryDirectory.path,
'engine',
'web',
);
}
// Bail out fast if golden doesn't exist, and user doesn't want to create it.
final File file = File(p.join(_kGoldensDirectory, filename));
final File file = File(p.join(
goldensDirectory,
filename,
));
if (!file.existsSync() && !write) {
return '''
Golden file $filename does not exist.
......
......@@ -5,6 +5,7 @@
import 'dart:async';
import 'dart:io' as io;
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
import 'environment.dart';
......@@ -31,17 +32,54 @@ class FilePath {
String toString() => _absolutePath;
}
/// Runs [executable] merging its output into the current process' standard out and standard error.
Future<int> runProcess(
String executable,
List<String> arguments, {
String workingDirectory,
bool mustSucceed: false,
}) async {
final io.Process process = await io.Process.start(
executable,
arguments,
workingDirectory: workingDirectory,
);
return _forwardIOAndWait(process);
final int exitCode = await _forwardIOAndWait(process);
if (mustSucceed && exitCode != 0) {
throw ProcessException(
description: 'Sub-process failed.',
executable: executable,
arguments: arguments,
workingDirectory: workingDirectory,
exitCode: exitCode,
);
}
return exitCode;
}
/// Runs [executable] and returns its standard output as a string.
///
/// If the process fails, throws a [ProcessException].
Future<String> evalProcess(
String executable,
List<String> arguments, {
String workingDirectory,
}) async {
final io.ProcessResult result = await io.Process.run(
executable,
arguments,
workingDirectory: workingDirectory,
);
if (result.exitCode != 0) {
throw ProcessException(
description: result.stderr,
executable: executable,
arguments: arguments,
workingDirectory: workingDirectory,
exitCode: result.exitCode,
);
}
return result.stdout;
}
Future<int> _forwardIOAndWait(io.Process process) {
......@@ -53,3 +91,31 @@ Future<int> _forwardIOAndWait(io.Process process) {
return exitCode;
});
}
@immutable
class ProcessException implements Exception {
ProcessException({
@required this.description,
@required this.executable,
@required this.arguments,
@required this.workingDirectory,
@required this.exitCode,
});
final String description;
final String executable;
final List<String> arguments;
final String workingDirectory;
final int exitCode;
@override
String toString() {
final StringBuffer message = StringBuffer();
message
..writeln(description)
..writeln('Command: $executable ${arguments.join(' ')}')
..writeln('Working directory: ${workingDirectory ?? io.Directory.current.path}')
..writeln('Exit code: $exitCode');
return '$message';
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册