Unverified Commit 65079ad5 authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Add Flutter-repo-specific golden comparator (#17205)

In order to avoid checking binaries into flutter/flutter,
this comparator can be used to retrieve golden files from
a sibling flutter/goldens repository.

https://github.com/flutter/flutter/issues/16859
parent 50afda01
0ea80e6a0147f1a3a59ff57f460a3f038a0d2748
......@@ -64,4 +64,4 @@ dev_dependencies:
web_socket_channel: 1.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
yaml: 2.1.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
# PUBSPEC CHECKSUM: 2b15
# PUBSPEC CHECKSUM: f3fb
......@@ -29,6 +29,8 @@ dev_dependencies:
sdk: flutter
flutter_driver:
sdk: flutter
flutter_goldens:
sdk: flutter
args: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
async: 2.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
......@@ -209,4 +211,4 @@ flutter:
- asset: packages/flutter_gallery_assets/fonts/GoogleSansDisplay-Regular.ttf
weight: 400
# PUBSPEC CHECKSUM: 50c7
# PUBSPEC CHECKSUM: 7b1f
// 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.
export 'package:flutter_goldens/flutter_goldens.dart' show main;
......@@ -16,6 +16,8 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
flutter_goldens:
sdk: flutter
mockito: 3.0.0-alpha+3
args: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
......@@ -67,4 +69,4 @@ dev_dependencies:
environment:
sdk: '>=1.19.0 <2.0.0'
# PUBSPEC CHECKSUM: a5bf
# PUBSPEC CHECKSUM: 8817
// 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.
export 'package:flutter_goldens/flutter_goldens.dart' show main;
// 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:io' as io;
import 'dart:typed_data';
import 'package:collection/collection.dart';
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:process/process.dart';
const String _kFlutterRootKey = 'FLUTTER_ROOT';
/// 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.
Future<void> main(FutureOr<void> testMain()) async {
goldenFileComparator = await FlutterGoldenFileComparator.fromDefaultComparator();
await testMain();
}
/// A golden file comparator specific to the `flutter/flutter` 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, then perform the comparison against
/// the files therein.
class FlutterGoldenFileComparator implements GoldenFileComparator {
@visibleForTesting
FlutterGoldenFileComparator(
this.goldens,
this.basedir, {
this.fs: const LocalFileSystem(),
});
final GoldensClient goldens;
final Uri basedir;
final FileSystem fs;
/// Creates a new [FlutterGoldenFileComparator] 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 use.
static Future<FlutterGoldenFileComparator> fromDefaultComparator() async {
final LocalFileComparator defaultComparator = goldenFileComparator;
final GoldensClient goldens = new GoldensClient();
await goldens.prepare();
return new FlutterGoldenFileComparator(goldens, defaultComparator.basedir);
}
@override
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
final File goldenFile = _getGoldenFile(golden);
if (!goldenFile.existsSync()) {
throw new TestFailure('Could not be compared against non-existent file: "$golden"');
}
final List<int> goldenBytes = await goldenFile.readAsBytes();
// TODO(tvolkert): Improve the intelligence of this comparison.
return const ListEquality<int>().equals(goldenBytes, imageBytes);
}
@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);
}
File _getGoldenFile(Uri uri) {
final File relativeFile = fs.file(uri);
final Directory testDirectory = fs.directory(basedir);
final String relativeBase = fs.path.relative(testDirectory.path, from: goldens.flutterRoot.path);
return goldens.repositoryRoot.childDirectory(relativeBase).childFile(relativeFile.path);
}
}
/// A class that represents a clone of the https://github.com/flutter/goldens
/// repository, nested within the `bin/cache` directory of the caller's Flutter
/// repository.
@visibleForTesting
class GoldensClient {
GoldensClient({
this.fs: const LocalFileSystem(),
this.platform: const LocalPlatform(),
this.process: const LocalProcessManager(),
});
final FileSystem fs;
final Platform platform;
final ProcessManager process;
RandomAccessFile _lock;
Directory get flutterRoot => fs.directory(platform.environment[_kFlutterRootKey]);
Directory get repositoryRoot => flutterRoot.childDirectory(fs.path.join('bin', 'cache', 'pkg', 'goldens'));
/// Prepares the local clone of the `flutter/goldens` repository for golden
/// file testing.
///
/// This ensures that the goldens repository has been cloned into its
/// expected location within `bin/cache` and that it is synced to the Git
/// revision specified in `bin/internal/goldens.version`.
///
/// While this is preparing the repository, it obtains a file lock such that
/// [GoldensClient] instances in other processes or isolates will not
/// duplicate the work that this is doing.
Future<void> prepare() async {
final String goldensCommit = await _getGoldensCommit();
String currentCommit = await _getCurrentCommit();
if (currentCommit != goldensCommit) {
await _obtainLock();
try {
// Check the current commit again now that we have the lock.
currentCommit = await _getCurrentCommit();
if (currentCommit != goldensCommit) {
if (currentCommit == null) {
await _initRepository();
}
await _syncTo(goldensCommit);
}
} finally {
await _releaseLock();
}
}
}
Future<String> _getGoldensCommit() async {
final File versionFile = flutterRoot.childFile(fs.path.join('bin', 'internal', 'goldens.version'));
return (await versionFile.readAsString()).trim();
}
Future<String> _getCurrentCommit() async {
if (!repositoryRoot.existsSync()) {
return null;
} else {
final io.ProcessResult revParse = await process.run(
<String>['git', 'rev-parse', 'HEAD'],
workingDirectory: repositoryRoot.path,
);
return revParse.exitCode == 0 ? revParse.stdout.trim() : null;
}
}
Future<void> _initRepository() async {
await repositoryRoot.create(recursive: true);
await _runCommands(
<String>[
'git init',
'git remote add upstream https://github.com/flutter/goldens.git',
],
workingDirectory: repositoryRoot,
);
}
Future<void> _syncTo(String commit) async {
await _runCommands(
<String>[
'git pull upstream master',
'git fetch upstream $commit',
'git reset --hard FETCH_HEAD',
],
workingDirectory: repositoryRoot,
);
}
Future<void> _runCommands(
List<String> commands, {
Directory workingDirectory,
}) async {
for (String command in commands) {
final List<String> parts = command.split(' ');
final io.ProcessResult result = await process.run(
parts,
workingDirectory: workingDirectory?.path,
);
if (result.exitCode != 0) {
throw new NonZeroExitCode(result.exitCode, result.stderr);
}
}
}
Future<void> _obtainLock() async {
final File lockFile = flutterRoot.childFile(fs.path.join('bin', 'cache', 'goldens.lockfile'));
await lockFile.create(recursive: true);
_lock = await lockFile.open(mode: io.FileMode.WRITE);
await _lock.lock(io.FileLock.BLOCKING_EXCLUSIVE);
}
Future<void> _releaseLock() async {
await _lock.close();
_lock = null;
}
}
/// Exception that signals a process' exit with a non-zero exit code.
class NonZeroExitCode implements Exception {
const NonZeroExitCode(this.exitCode, this.stderr) : assert(exitCode != 0);
final int exitCode;
final String stderr;
@override
String toString() {
return 'Exit code $exitCode: $stderr';
}
}
name: flutter_goldens
dependencies:
# To update these, use "flutter update-packages --force-upgrade".
flutter_test:
sdk: flutter
collection: 1.14.6
file: 5.0.0
meta: 1.1.2
platform: 2.1.2
process: 3.0.1
args: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
async: 2.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
barback: 0.15.2+15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
boolean_selector: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
charcode: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
convert: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
crypto: 2.0.2+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
csslib: 0.14.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
glob: 1.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
html: 0.13.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
http: 0.11.3+16 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
http_multi_server: 2.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
http_parser: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
intl: 0.15.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
io: 0.3.2+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
js: 0.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
logging: 0.11.3+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
matcher: 0.12.1+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
mime: 0.9.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
multi_server_socket: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
node_preamble: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
package_config: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
package_resolver: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
path: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
plugin: 0.2.0+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
pool: 1.3.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
pub_semver: 1.3.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
quiver: 0.29.0+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf: 0.7.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_packages_handler: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_static: 0.2.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_web_socket: 0.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_map_stack_trace: 1.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_maps: 0.10.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_span: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
stack_trace: 1.9.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
stream_channel: 1.6.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
string_scanner: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
term_glyph: 1.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
test: 0.12.34 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
typed_data: 1.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
utf: 0.9.0+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
vector_math: 2.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
watcher: 0.9.7+7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
web_socket_channel: 1.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
yaml: 2.1.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
dev_dependencies:
mockito: 3.0.0-alpha+3
environment:
sdk: '>=1.19.0 <2.0.0'
# PUBSPEC CHECKSUM: 4bf9
// 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:io' as io;
import 'dart:typed_data';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_goldens/flutter_goldens.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
const String _kFlutterRoot = '/flutter';
const String _kRepositoryRoot = '$_kFlutterRoot/bin/cache/pkg/goldens';
const String _kVersionFile = '$_kFlutterRoot/bin/internal/goldens.version';
const String _kGoldensVersion = '123456abcdef';
void main() {
MemoryFileSystem fs;
FakePlatform platform;
MockProcessManager process;
setUp(() {
fs = new MemoryFileSystem();
platform = new FakePlatform(environment: <String, String>{'FLUTTER_ROOT': _kFlutterRoot});
process = new MockProcessManager();
fs.directory(_kFlutterRoot).createSync(recursive: true);
fs.directory(_kRepositoryRoot).createSync(recursive: true);
fs.file(_kVersionFile).createSync(recursive: true);
fs.file(_kVersionFile).writeAsStringSync(_kGoldensVersion);
});
group('GoldensClient', () {
GoldensClient goldens;
setUp(() {
goldens = new GoldensClient(
fs: fs,
platform: platform,
process: process,
);
});
group('prepare', () {
test('performs minimal work if versions match', () async {
when(process.run(typed(captureAny), workingDirectory: typed(captureAny, named: 'workingDirectory')))
.thenAnswer((_) => new Future<io.ProcessResult>.value(io.ProcessResult(123, 0, _kGoldensVersion, '')));
await goldens.prepare();
// Verify that we only spawned `git rev-parse HEAD`
final VerificationResult verifyProcessRun =
verify(process.run(typed(captureAny), workingDirectory: typed(captureAny, named: 'workingDirectory')));
verifyProcessRun.called(1);
expect(verifyProcessRun.captured.first, <String>['git', 'rev-parse', 'HEAD']);
expect(verifyProcessRun.captured.last, _kRepositoryRoot);
});
});
});
group('FlutterGoldenFileComparator', () {
GoldensClient goldens;
MemoryFileSystem fs;
FlutterGoldenFileComparator comparator;
setUp(() {
goldens = new MockGoldensClient();
fs = new MemoryFileSystem();
final Directory flutterRoot = fs.directory('/path/to/flutter')..createSync(recursive: true);
final Directory goldensRoot = flutterRoot.childDirectory('bin/cache/goldens')..createSync(recursive: true);
final Directory testDirectory = flutterRoot.childDirectory('test/foo/bar')..createSync(recursive: true);
comparator = new FlutterGoldenFileComparator(goldens, new Uri.directory(testDirectory.path), fs: fs);
when(goldens.flutterRoot).thenReturn(flutterRoot);
when(goldens.repositoryRoot).thenReturn(goldensRoot);
});
group('compare', () {
test('throws if golden file is not found', () async {
try {
await comparator.compare(new Uint8List.fromList(<int>[1, 2, 3]), Uri.parse('test.png'));
fail('TestFailure expected but not thrown');
} on TestFailure catch (error) {
expect(error.message, contains('Could not be compared against non-existent file'));
}
});
test('returns false if golden bytes do not match', () async {
final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png')
..createSync(recursive: true);
goldenFile.writeAsBytesSync(<int>[4, 5, 6], flush: true);
final bool result = await comparator.compare(new Uint8List.fromList(<int>[1, 2, 3]), Uri.parse('test.png'));
expect(result, isFalse);
});
test('returns true if golden bytes match', () async {
final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png')
..createSync(recursive: true);
goldenFile.writeAsBytesSync(<int>[1, 2, 3], flush: true);
final bool result = await comparator.compare(new Uint8List.fromList(<int>[1, 2, 3]), Uri.parse('test.png'));
expect(result, isTrue);
});
});
group('update', () {
test('creates golden file if it does not already exist', () async {
final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png');
expect(goldenFile.existsSync(), isFalse);
await comparator.update(Uri.parse('test.png'), new Uint8List.fromList(<int>[1, 2, 3]));
expect(goldenFile.existsSync(), isTrue);
expect(goldenFile.readAsBytesSync(), <int>[1, 2, 3]);
});
test('overwrites golden bytes if golden file already exist', () async {
final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png')
..createSync(recursive: true);
goldenFile.writeAsBytesSync(<int>[4, 5, 6], flush: true);
await comparator.update(Uri.parse('test.png'), new Uint8List.fromList(<int>[1, 2, 3]));
expect(goldenFile.readAsBytesSync(), <int>[1, 2, 3]);
});
});
});
}
class MockProcessManager extends Mock implements ProcessManager {}
class MockGoldensClient extends Mock implements GoldensClient {}
......@@ -66,7 +66,11 @@ abstract class GoldenFileComparator {
///
/// * [flutter_test] for more information about how to configure tests at the
/// directory-level.
GoldenFileComparator goldenFileComparator = const _UninitializedComparator();
GoldenFileComparator _goldenFileComparator = const _UninitializedComparator();
GoldenFileComparator get goldenFileComparator => _goldenFileComparator;
set goldenFileComparator(GoldenFileComparator comparator) {
_goldenFileComparator = comparator ?? const _UninitializedComparator();
}
/// Whether golden files should be automatically updated during tests rather
/// than compared to the image bytes recorded by the tests.
......@@ -120,15 +124,19 @@ class LocalFileComparator implements GoldenFileComparator {
/// directory in which [testFile] resides.
///
/// The [testFile] URI must represent a file.
LocalFileComparator(Uri testFile)
LocalFileComparator(Uri testFile, {path.Style pathStyle})
: assert(testFile.scheme == 'file'),
basedir = new Uri.directory(_path.dirname(_path.fromUri(testFile)));
basedir = _getBasedir(testFile, pathStyle),
_path = _getPath(pathStyle);
// Due to https://github.com/flutter/flutter/issues/17118, we need to
// explicitly set the path style.
static final path.Context _path = new path.Context(style: Platform.isWindows
? path.Style.windows
: path.Style.posix);
static path.Context _getPath(path.Style style) {
return new path.Context(style: style ?? path.Style.platform);
}
static Uri _getBasedir(Uri testFile, path.Style pathStyle) {
final path.Context context = _getPath(pathStyle);
return context.toUri(context.dirname(context.fromUri(testFile)) + context.separator);
}
/// The directory in which the test was loaded.
///
......@@ -136,6 +144,11 @@ class LocalFileComparator implements GoldenFileComparator {
/// directory.
final Uri basedir;
/// Path context exists as an instance variable rather than just using the
/// system path context in order to support testing, where we can spoof the
/// platform to test behaviors with arbitrary path styles.
final path.Context _path;
@override
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
final File goldenFile = _getFile(golden);
......@@ -149,11 +162,12 @@ class LocalFileComparator implements GoldenFileComparator {
@override
Future<void> update(Uri golden, Uint8List imageBytes) async {
final File goldenFile = _getFile(golden);
await goldenFile.parent.create(recursive: true);
await goldenFile.writeAsBytes(imageBytes, flush: true);
}
File _getFile(Uri golden) {
return new File(_path.join(_path.fromUri(basedir), golden.path));
return new File(_path.join(_path.fromUri(basedir), _path.fromUri(golden.path)));
}
static bool _areListsEqual<T>(List<T> list1, List<T> list2) {
......
......@@ -24,6 +24,16 @@ void main() {
fs = new MemoryFileSystem(style: style);
});
/// Converts posix-style paths to the style associated with [fs].
///
/// This allows us to deal in posix-style paths in the tests.
String fix(String path) {
if (path.startsWith('/')) {
path = '${fs.style.drive}$path';
}
return path.replaceAll('/', fs.path.separator);
}
void test(String description, FutureOr<void> body()) {
test_package.test(description, () {
return io.IOOverrides.runZoned(
......@@ -59,18 +69,18 @@ void main() {
LocalFileComparator comparator;
setUp(() {
comparator = new LocalFileComparator(new Uri.file('/golden_test.dart'));
comparator = new LocalFileComparator(fs.file(fix('/golden_test.dart')).uri, pathStyle: fs.path.style);
});
test('calculates basedir correctly', () {
expect(comparator.basedir, new Uri.file('/'));
comparator = new LocalFileComparator(new Uri.file('/foo/bar/golden_test.dart'));
expect(comparator.basedir, new Uri.file('/foo/bar/'));
expect(comparator.basedir, fs.file(fix('/')).uri);
comparator = new LocalFileComparator(fs.file(fix('/foo/bar/golden_test.dart')).uri, pathStyle: fs.path.style);
expect(comparator.basedir, fs.directory(fix('/foo/bar/')).uri);
});
group('compare', () {
Future<bool> doComparison([String golden = 'golden.png']) {
final Uri uri = new Uri.file(golden);
final Uri uri = fs.file(fix(golden)).uri;
return comparator.compare(
new Uint8List.fromList(_kExpectedBytes),
uri,
......@@ -79,13 +89,13 @@ void main() {
group('succeeds', () {
test('when golden file is in same folder as test', () async {
fs.file('/golden.png').writeAsBytesSync(_kExpectedBytes);
fs.file(fix('/golden.png')).writeAsBytesSync(_kExpectedBytes);
final bool success = await doComparison();
expect(success, isTrue);
});
test('when golden file is in subfolder of test', () async {
fs.file('/sub/foo.png')
fs.file(fix('/sub/foo.png'))
..createSync(recursive: true)
..writeAsBytesSync(_kExpectedBytes);
final bool success = await doComparison('sub/foo.png');
......@@ -100,32 +110,32 @@ void main() {
});
test('when golden bytes are leading subset of image bytes', () async {
fs.file('/golden.png').writeAsBytesSync(<int>[1, 2]);
fs.file(fix('/golden.png')).writeAsBytesSync(<int>[1, 2]);
expect(await doComparison(), isFalse);
});
test('when golden bytes are leading superset of image bytes', () async {
fs.file('/golden.png').writeAsBytesSync(<int>[1, 2, 3, 4]);
fs.file(fix('/golden.png')).writeAsBytesSync(<int>[1, 2, 3, 4]);
expect(await doComparison(), isFalse);
});
test('when golden bytes are trailing subset of image bytes', () async {
fs.file('/golden.png').writeAsBytesSync(<int>[2, 3]);
fs.file(fix('/golden.png')).writeAsBytesSync(<int>[2, 3]);
expect(await doComparison(), isFalse);
});
test('when golden bytes are trailing superset of image bytes', () async {
fs.file('/golden.png').writeAsBytesSync(<int>[0, 1, 2, 3]);
fs.file(fix('/golden.png')).writeAsBytesSync(<int>[0, 1, 2, 3]);
expect(await doComparison(), isFalse);
});
test('when golden bytes are disjoint from image bytes', () async {
fs.file('/golden.png').writeAsBytesSync(<int>[4, 5, 6]);
fs.file(fix('/golden.png')).writeAsBytesSync(<int>[4, 5, 6]);
expect(await doComparison(), isFalse);
});
test('when golden bytes are empty', () async {
fs.file('/golden.png').writeAsBytesSync(<int>[]);
fs.file(fix('/golden.png')).writeAsBytesSync(<int>[]);
expect(await doComparison(), isFalse);
});
});
......@@ -133,18 +143,18 @@ void main() {
group('update', () {
test('updates existing file', () async {
fs.file('/golden.png').writeAsBytesSync(_kExpectedBytes);
fs.file(fix('/golden.png')).writeAsBytesSync(_kExpectedBytes);
const List<int> newBytes = const <int>[11, 12, 13];
await comparator.update(new Uri.file('golden.png'), new Uint8List.fromList(newBytes));
expect(fs.file('/golden.png').readAsBytesSync(), newBytes);
await comparator.update(fs.file('golden.png').uri, new Uint8List.fromList(newBytes));
expect(fs.file(fix('/golden.png')).readAsBytesSync(), newBytes);
});
test('creates non-existent file', () async {
expect(fs.file('/foo.png').existsSync(), isFalse);
expect(fs.file(fix('/foo.png')).existsSync(), isFalse);
const List<int> newBytes = const <int>[11, 12, 13];
await comparator.update(new Uri.file('foo.png'), new Uint8List.fromList(newBytes));
expect(fs.file('/foo.png').existsSync(), isTrue);
expect(fs.file('/foo.png').readAsBytesSync(), newBytes);
await comparator.update(fs.file('foo.png').uri, new Uint8List.fromList(newBytes));
expect(fs.file(fix('/foo.png')).existsSync(), isTrue);
expect(fs.file(fix('/foo.png')).readAsBytesSync(), newBytes);
});
});
});
......
......@@ -685,9 +685,7 @@ void main() {
);
if (testConfigFile != null) {
buffer.write('''
return () {
test_config.main(test.main);
};
return () => test_config.main(test.main);
''');
} else {
buffer.write('''
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment