blob: bdbbabe7fb9e151760b2d4b28c8278e31d2a4cb0 [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:async';
import 'dart:io' as io;
import 'package:golden_tests_harvester/golden_tests_harvester.dart';
import 'package:litetest/litetest.dart';
import 'package:path/path.dart' as p;
void main() async {
Future<void> withTempDirectory(
FutureOr<void> Function(io.Directory) callback) async {
final io.Directory tempDirectory = await io.Directory.systemTemp
.createTemp('golden_tests_harvester_test.');
try {
await callback(tempDirectory);
} finally {
await tempDirectory.delete(recursive: true);
}
}
test('should fail on a missing directory', () async {
await withTempDirectory((io.Directory tempDirectory) async {
final StringSink stderr = StringBuffer();
final ArgumentError error = await _expectThrow<ArgumentError>(() async {
await Harvester.create(
io.Directory(p.join(tempDirectory.path, 'non_existent')),
stderr,
addImageToSkiaGold: _alwaysThrowsAddImg);
});
expect(error.message, contains('non_existent'));
expect(stderr.toString(), isEmpty);
});
});
test('should require a file named "digest.json" in the working directory',
() async {
await withTempDirectory((io.Directory tempDirectory) async {
final StringSink stderr = StringBuffer();
final StateError error = await _expectThrow<StateError>(() async {
await Harvester.create(
tempDirectory,
stderr,
addImageToSkiaGold: _alwaysThrowsAddImg);
});
expect(error.toString(), contains('digest.json'));
expect(stderr.toString(), isEmpty);
});
});
test('should throw if "digest.json" is in an unexpected format', () async {
await withTempDirectory((io.Directory tempDirectory) async {
final StringSink stderr = StringBuffer();
final io.File digestsFile =
io.File(p.join(tempDirectory.path, 'digest.json'));
await digestsFile
.writeAsString('{"dimensions": "not a map", "entries": []}');
final FormatException error =
await _expectThrow<FormatException>(() async {
await Harvester.create(
tempDirectory,
stderr,
addImageToSkiaGold: _alwaysThrowsAddImg);
});
expect(error.message, contains('dimensions'));
expect(stderr.toString(), isEmpty);
});
});
test('should fail eagerly if addImg fails', () async {
await withTempDirectory((io.Directory tempDirectory) async {
final io.File digestsFile =
io.File(p.join(tempDirectory.path, 'digest.json'));
final StringSink stderr = StringBuffer();
await digestsFile.writeAsString('''
{
"dimensions": {},
"entries": [
{
"filename": "test_name_1.png",
"width": 100,
"height": 100,
"maxDiffPixelsPercent": 0.01,
"maxColorDelta": 0
}
]
}
''');
final FailedComparisonException error =
await _expectThrow<FailedComparisonException>(() async {
final Harvester harvester = await Harvester.create(
tempDirectory,
stderr,
addImageToSkiaGold: _alwaysThrowsAddImg);
await harvest(harvester);
});
expect(error.testName, 'test_name_1.png');
expect(stderr.toString(), contains('IntentionalError'));
});
});
test('should invoke addImg per test', () async {
await withTempDirectory((io.Directory tempDirectory) async {
final io.File digestsFile =
io.File(p.join(tempDirectory.path, 'digest.json'));
await digestsFile.writeAsString('''
{
"dimensions": {},
"entries": [
{
"filename": "test_name_1.png",
"width": 100,
"height": 100,
"maxDiffPixelsPercent": 0.01,
"maxColorDelta": 0
},
{
"filename": "test_name_2.png",
"width": 200,
"height": 200,
"maxDiffPixelsPercent": 0.02,
"maxColorDelta": 1
}
]
}
''');
final List<String> addImgCalls = <String>[];
final StringSink stderr = StringBuffer();
final Harvester harvester =
await Harvester.create(tempDirectory, stderr, addImageToSkiaGold: (
String testName,
io.File goldenFile, {
required int screenshotSize,
double differentPixelsRate = 0.01,
int pixelColorDelta = 0,
}) async {
addImgCalls.add(
'$testName $screenshotSize $differentPixelsRate $pixelColorDelta');
});
await harvest(harvester);
expect(addImgCalls, <String>[
'test_name_1.png 10000 0.01 0',
'test_name_2.png 40000 0.02 1',
]);
});
});
test('client has dimensions', () async {
await withTempDirectory((io.Directory tempDirectory) async {
final StringSink stderr = StringBuffer();
final io.File digestsFile =
io.File(p.join(tempDirectory.path, 'digest.json'));
await digestsFile
.writeAsString('{"dimensions": {"key":"value"}, "entries": []}');
final Harvester harvester = await Harvester.create(tempDirectory, stderr);
expect(harvester is SkiaGoldHarvester, true);
final SkiaGoldHarvester skiaGoldHarvester =
harvester as SkiaGoldHarvester;
expect(skiaGoldHarvester.client.dimensions,
<String, String>{'key': 'value'});
});
});
test('throws without GOLDCTL', () async {
await withTempDirectory((io.Directory tempDirectory) async {
final StringSink stderr = StringBuffer();
final io.File digestsFile =
io.File(p.join(tempDirectory.path, 'digest.json'));
await digestsFile.writeAsString('''
{
"dimensions": {"key":"value"},
"entries": [
{
"filename": "foo.png",
"width": 100,
"height": 100,
"maxDiffPixelsPercent": 0.01,
"maxColorDelta": 0
}
]}
''');
final Harvester harvester = await Harvester.create(tempDirectory, stderr);
final StateError error = await _expectThrow<StateError>(() async {
await harvest(harvester);
});
expect(error.message, contains('GOLDCTL'));
expect(stderr.toString(), isEmpty);
});
});
}
FutureOr<T> _expectThrow<T extends Object>(
FutureOr<void> Function() callback) async {
try {
await callback();
fail('Expected an exception of type $T');
} on T catch (e) {
return e;
} catch (e) {
fail('Expected an exception of type $T, but got $e');
}
// fail(...) unfortunately does not return Never, but it does always throw.
throw UnsupportedError('Unreachable');
}
final class _IntentionalError extends Error {}
Future<void> _alwaysThrowsAddImg(
String testName,
io.File goldenFile, {
required int screenshotSize,
double differentPixelsRate = 0.01,
int pixelColorDelta = 0,
}) async {
throw _IntentionalError();
}