mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
806 lines
26 KiB
Dart
806 lines
26 KiB
Dart
// 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:convert';
|
|
import 'dart:core';
|
|
import 'dart: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';
|
|
|
|
import 'json_templates.dart';
|
|
|
|
const String _kFlutterRoot = '/flutter';
|
|
|
|
// 1x1 transparent pixel
|
|
const List<int> _kTestPngBytes =
|
|
<int>[137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
|
|
1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 11, 73, 68, 65, 84,
|
|
120, 1, 99, 97, 0, 2, 0, 0, 25, 0, 5, 144, 240, 54, 245, 0, 0, 0, 0, 73, 69,
|
|
78, 68, 174, 66, 96, 130];
|
|
|
|
// 1x1 colored pixel
|
|
const List<int> _kFailPngBytes =
|
|
<int>[137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
|
|
1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65, 84,
|
|
120, 1, 99, 249, 207, 240, 255, 63, 0, 7, 18, 3, 2, 164, 147, 160, 197, 0,
|
|
0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130];
|
|
|
|
void main() {
|
|
MemoryFileSystem fs;
|
|
FakePlatform platform;
|
|
MockProcessManager process;
|
|
MockHttpClient mockHttpClient;
|
|
|
|
setUp(() {
|
|
fs = MemoryFileSystem();
|
|
platform = FakePlatform(
|
|
environment: <String, String>{'FLUTTER_ROOT': _kFlutterRoot},
|
|
operatingSystem: 'macos'
|
|
);
|
|
process = MockProcessManager();
|
|
mockHttpClient = MockHttpClient();
|
|
fs.directory(_kFlutterRoot).createSync(recursive: true);
|
|
});
|
|
|
|
group('SkiaGoldClient', () {
|
|
SkiaGoldClient skiaClient;
|
|
|
|
setUp(() {
|
|
final Directory workDirectory = fs.directory('/workDirectory')
|
|
..createSync(recursive: true);
|
|
skiaClient = SkiaGoldClient(
|
|
workDirectory,
|
|
fs: fs,
|
|
process: process,
|
|
platform: platform,
|
|
httpClient: mockHttpClient,
|
|
);
|
|
});
|
|
|
|
group('auth', () {
|
|
test('performs minimal work if already authorized', () async {
|
|
fs.file('/workDirectory/temp/auth_opt.json')
|
|
..createSync(recursive: true);
|
|
when(process.run(any))
|
|
.thenAnswer((_) => Future<ProcessResult>
|
|
.value(ProcessResult(123, 0, '', '')));
|
|
await skiaClient.auth();
|
|
|
|
verifyNever(process.run(
|
|
captureAny,
|
|
workingDirectory: captureAnyNamed('workingDirectory'),
|
|
));
|
|
});
|
|
});
|
|
|
|
group('Request Handling', () {
|
|
String testName;
|
|
String pullRequestNumber;
|
|
String expectation;
|
|
Uri url;
|
|
MockHttpClientRequest mockHttpRequest;
|
|
|
|
setUp(() {
|
|
testName = 'flutter.golden_test.1.png';
|
|
pullRequestNumber = '1234';
|
|
expectation = '55109a4bed52acc780530f7a9aeff6c0';
|
|
mockHttpRequest = MockHttpClientRequest();
|
|
});
|
|
|
|
test('validates SkiaDigest', () {
|
|
final Map<String, dynamic> skiaJson = json.decode(digestResponseTemplate());
|
|
final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
|
|
expect(
|
|
digest.isValid(
|
|
platform,
|
|
'flutter.golden_test.1',
|
|
expectation,
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('invalidates bad SkiaDigest - platform', () {
|
|
final Map<String, dynamic> skiaJson = json.decode(
|
|
digestResponseTemplate(platform: 'linux')
|
|
);
|
|
final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
|
|
expect(
|
|
digest.isValid(
|
|
platform,
|
|
'flutter.golden_test.1',
|
|
expectation,
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('invalidates bad SkiaDigest - test name', () {
|
|
final Map<String, dynamic> skiaJson = json.decode(
|
|
digestResponseTemplate(testName: 'flutter.golden_test.2')
|
|
);
|
|
final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
|
|
expect(
|
|
digest.isValid(
|
|
platform,
|
|
'flutter.golden_test.1',
|
|
expectation,
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('invalidates bad SkiaDigest - expectation', () {
|
|
final Map<String, dynamic> skiaJson = json.decode(
|
|
digestResponseTemplate(expectation: '1deg543sf645erg44awqcc78')
|
|
);
|
|
final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
|
|
expect(
|
|
digest.isValid(
|
|
platform,
|
|
'flutter.golden_test.1',
|
|
expectation,
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('invalidates bad SkiaDigest - status', () {
|
|
final Map<String, dynamic> skiaJson = json.decode(
|
|
digestResponseTemplate(status: 'negative')
|
|
);
|
|
final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
|
|
expect(
|
|
digest.isValid(
|
|
platform,
|
|
'flutter.golden_test.1',
|
|
expectation,
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('sets up expectations', () async {
|
|
url = Uri.parse('https://flutter-gold.skia.org/json/expectations/commit/HEAD');
|
|
final MockHttpClientResponse mockHttpResponse = MockHttpClientResponse(
|
|
utf8.encode(rawExpectationsTemplate())
|
|
);
|
|
when(mockHttpClient.getUrl(url))
|
|
.thenAnswer((_) => Future<MockHttpClientRequest>.value(mockHttpRequest));
|
|
when(mockHttpRequest.close())
|
|
.thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
|
|
|
|
await skiaClient.getExpectations();
|
|
expect(skiaClient.expectations, isNotNull);
|
|
expect(
|
|
skiaClient.expectations['flutter.golden_test.1'],
|
|
contains(expectation),
|
|
);
|
|
});
|
|
|
|
test('detects invalid digests SkiaDigest', () {
|
|
const String testName = 'flutter.golden_test.2';
|
|
final Map<String, dynamic> skiaJson = json.decode(digestResponseTemplate());
|
|
final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
|
|
expect(digest.isValid(platform, testName, expectation), isFalse);
|
|
});
|
|
|
|
test('image bytes are processed properly', () async {
|
|
final Uri imageUrl = Uri.parse(
|
|
'https://flutter-gold.skia.org/img/images/$expectation.png'
|
|
);
|
|
final MockHttpClientRequest mockImageRequest = MockHttpClientRequest();
|
|
final MockHttpImageResponse mockImageResponse = MockHttpImageResponse(
|
|
imageResponseTemplate()
|
|
);
|
|
when(mockHttpClient.getUrl(imageUrl))
|
|
.thenAnswer((_) => Future<MockHttpClientRequest>.value(mockImageRequest));
|
|
when(mockImageRequest.close())
|
|
.thenAnswer((_) => Future<MockHttpImageResponse>.value(mockImageResponse));
|
|
|
|
final List<int> masterBytes = await skiaClient.getImageBytes(expectation);
|
|
|
|
expect(masterBytes, equals(_kTestPngBytes));
|
|
});
|
|
|
|
group('ignores', () {
|
|
Uri url;
|
|
MockHttpClientRequest mockHttpRequest;
|
|
MockHttpClientResponse mockHttpResponse;
|
|
|
|
setUp(() {
|
|
url = Uri.parse('https://flutter-gold.skia.org/json/ignores');
|
|
mockHttpRequest = MockHttpClientRequest();
|
|
mockHttpResponse = MockHttpClientResponse(utf8.encode(
|
|
ignoreResponseTemplate(
|
|
pullRequestNumber: pullRequestNumber,
|
|
expires: DateTime.now()
|
|
.add(const Duration(days: 1))
|
|
.toString(),
|
|
otherTestName: 'unrelatedTest.1'
|
|
)
|
|
));
|
|
when(mockHttpClient.getUrl(url))
|
|
.thenAnswer((_) => Future<MockHttpClientRequest>.value(mockHttpRequest));
|
|
when(mockHttpRequest.close())
|
|
.thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
|
|
});
|
|
|
|
test('returns true for ignored test and ignored pull request number', () async {
|
|
expect(
|
|
await skiaClient.testIsIgnoredForPullRequest(
|
|
pullRequestNumber,
|
|
testName,
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('returns true for ignored test and not ignored pull request number', () async {
|
|
expect(
|
|
await skiaClient.testIsIgnoredForPullRequest(
|
|
'5678',
|
|
testName,
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('returns false for not ignored test and ignored pull request number', () async {
|
|
expect(
|
|
await skiaClient.testIsIgnoredForPullRequest(
|
|
pullRequestNumber,
|
|
'failure.png',
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('throws exception for expired ignore', () async {
|
|
mockHttpResponse = MockHttpClientResponse(utf8.encode(
|
|
ignoreResponseTemplate(
|
|
pullRequestNumber: pullRequestNumber,
|
|
)
|
|
));
|
|
when(mockHttpRequest.close())
|
|
.thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
|
|
final Future<bool> test = skiaClient.testIsIgnoredForPullRequest(
|
|
pullRequestNumber,
|
|
testName,
|
|
);
|
|
expect(
|
|
test,
|
|
throwsException,
|
|
);
|
|
});
|
|
|
|
test('throws exception for first expired ignore among multiple', () async {
|
|
mockHttpResponse = MockHttpClientResponse(utf8.encode(
|
|
ignoreResponseTemplate(
|
|
pullRequestNumber: pullRequestNumber,
|
|
otherExpires: DateTime.now()
|
|
.add(const Duration(days: 1))
|
|
.toString(),
|
|
)
|
|
));
|
|
when(mockHttpRequest.close())
|
|
.thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
|
|
final Future<bool> test = skiaClient.testIsIgnoredForPullRequest(
|
|
pullRequestNumber,
|
|
testName,
|
|
);
|
|
expect(
|
|
test,
|
|
throwsException,
|
|
);
|
|
});
|
|
|
|
test('throws exception for later expired ignore among multiple', () async {
|
|
mockHttpResponse = MockHttpClientResponse(utf8.encode(
|
|
ignoreResponseTemplate(
|
|
pullRequestNumber: pullRequestNumber,
|
|
expires: DateTime.now()
|
|
.add(const Duration(days: 1))
|
|
.toString(),
|
|
)
|
|
));
|
|
when(mockHttpRequest.close())
|
|
.thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
|
|
final Future<bool> test = skiaClient.testIsIgnoredForPullRequest(
|
|
pullRequestNumber,
|
|
testName,
|
|
);
|
|
expect(
|
|
test,
|
|
throwsException,
|
|
);
|
|
});
|
|
});
|
|
|
|
group('digest parsing', () {
|
|
Uri url;
|
|
MockHttpClientRequest mockHttpRequest;
|
|
MockHttpClientResponse mockHttpResponse;
|
|
|
|
setUp(() {
|
|
url = Uri.parse(
|
|
'https://flutter-gold.skia.org/json/details?'
|
|
'test=flutter.golden_test.1&digest=$expectation'
|
|
);
|
|
mockHttpRequest = MockHttpClientRequest();
|
|
when(mockHttpClient.getUrl(url))
|
|
.thenAnswer((_) => Future<MockHttpClientRequest>.value(mockHttpRequest));
|
|
});
|
|
|
|
test('succeeds when valid', () async {
|
|
mockHttpResponse = MockHttpClientResponse(utf8.encode(digestResponseTemplate()));
|
|
when(mockHttpRequest.close())
|
|
.thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
|
|
expect(
|
|
await skiaClient.isValidDigestForExpectation(
|
|
expectation,
|
|
testName,
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('fails when invalid', () async {
|
|
mockHttpResponse = MockHttpClientResponse(utf8.encode(
|
|
digestResponseTemplate(platform: 'linux')
|
|
));
|
|
when(mockHttpRequest.close())
|
|
.thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
|
|
expect(
|
|
await skiaClient.isValidDigestForExpectation(
|
|
expectation,
|
|
testName,
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
group('FlutterGoldenFileComparator', () {
|
|
FlutterSkiaGoldFileComparator comparator;
|
|
|
|
setUp(() {
|
|
final Directory basedir = fs.directory('flutter/test/library/')
|
|
..createSync(recursive: true);
|
|
comparator = FlutterSkiaGoldFileComparator(
|
|
basedir.uri,
|
|
MockSkiaGoldClient(),
|
|
fs: fs,
|
|
platform: platform,
|
|
);
|
|
});
|
|
|
|
test('calculates the basedir correctly from defaultComparator', () async {
|
|
final MockLocalFileComparator defaultComparator = MockLocalFileComparator();
|
|
final Directory flutterRoot = fs.directory(platform.environment['FLUTTER_ROOT'])
|
|
..createSync(recursive: true);
|
|
when(defaultComparator.basedir).thenReturn(flutterRoot.childDirectory('baz').uri);
|
|
|
|
final Directory basedir = FlutterGoldenFileComparator.getBaseDirectory(
|
|
defaultComparator,
|
|
platform,
|
|
);
|
|
expect(
|
|
basedir.uri,
|
|
fs.directory('/flutter/bin/cache/pkg/skia_goldens/baz').uri,
|
|
);
|
|
});
|
|
|
|
test('ignores version number', () {
|
|
final Uri key = comparator.getTestUri(Uri.parse('foo.png'), 1);
|
|
expect(key, Uri.parse('foo.png'));
|
|
});
|
|
|
|
group('Post-Submit', () {
|
|
final MockSkiaGoldClient mockSkiaClient = MockSkiaGoldClient();
|
|
|
|
setUp(() {
|
|
final Directory basedir = fs.directory('flutter/test/library/')
|
|
..createSync(recursive: true);
|
|
comparator = FlutterSkiaGoldFileComparator(
|
|
basedir.uri,
|
|
mockSkiaClient,
|
|
fs: fs,
|
|
platform: platform,
|
|
);
|
|
});
|
|
|
|
group('correctly determines testing environment', () {
|
|
test('returns true', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI': 'true',
|
|
'CIRRUS_PR': '',
|
|
'CIRRUS_BRANCH': 'master',
|
|
'GOLD_SERVICE_ACCOUNT': 'service account...',
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterSkiaGoldFileComparator.isAvailableForEnvironment(platform),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('returns false - PR active', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI': 'true',
|
|
'CIRRUS_PR': '1234',
|
|
'CIRRUS_BRANCH': 'master',
|
|
'GOLD_SERVICE_ACCOUNT': 'service account...',
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterSkiaGoldFileComparator.isAvailableForEnvironment(platform),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('returns false - no service account', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI': 'true',
|
|
'CIRRUS_PR': '',
|
|
'CIRRUS_BRANCH': 'master',
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterSkiaGoldFileComparator.isAvailableForEnvironment(platform),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('returns false - not on cirrus', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'SWARMING_ID' : '1234567890',
|
|
'GOLD_SERVICE_ACCOUNT': 'service account...'
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterSkiaGoldFileComparator.isAvailableForEnvironment(platform),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('returns false - not on master', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI': 'true',
|
|
'CIRRUS_PR': '',
|
|
'CIRRUS_BRANCH': 'hotfix',
|
|
'GOLD_SERVICE_ACCOUNT': 'service account...'
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterSkiaGoldFileComparator.isAvailableForEnvironment(platform),
|
|
isFalse,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
group('Pre-Submit', () {
|
|
FlutterPreSubmitFileComparator comparator;
|
|
final MockSkiaGoldClient mockSkiaClient = MockSkiaGoldClient();
|
|
|
|
setUp(() {
|
|
final Directory basedir = fs.directory('flutter/test/library/')
|
|
..createSync(recursive: true);
|
|
comparator = FlutterPreSubmitFileComparator(
|
|
basedir.uri,
|
|
mockSkiaClient,
|
|
fs: fs,
|
|
platform: FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI' : 'true',
|
|
'CIRRUS_PR' : '1234',
|
|
'GOLD_SERVICE_ACCOUNT' : 'service account...'
|
|
},
|
|
operatingSystem: 'macos'
|
|
),
|
|
);
|
|
|
|
when(mockSkiaClient.getImageBytes('55109a4bed52acc780530f7a9aeff6c0'))
|
|
.thenAnswer((_) => Future<List<int>>.value(_kTestPngBytes));
|
|
when(mockSkiaClient.expectations)
|
|
.thenReturn(expectationsTemplate());
|
|
when(mockSkiaClient.cleanTestName('library.flutter.golden_test.1.png'))
|
|
.thenReturn('flutter.golden_test.1');
|
|
when(mockSkiaClient.isValidDigestForExpectation(
|
|
'55109a4bed52acc780530f7a9aeff6c0',
|
|
'library.flutter.golden_test.1.png',
|
|
))
|
|
.thenAnswer((_) => Future<bool>.value(false));
|
|
});
|
|
group('correctly determines testing environment', () {
|
|
test('returns true', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI': 'true',
|
|
'CIRRUS_PR': '1234',
|
|
'GOLD_SERVICE_ACCOUNT' : 'service account...',
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('returns false - no PR', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI': 'true',
|
|
'CIRRUS_PR': '',
|
|
'GOLD_SERVICE_ACCOUNT' : 'service account...',
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('returns false - no service account', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI': 'true',
|
|
'CIRRUS_PR': '1234',
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('returns false - not on Cirrus', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform),
|
|
isFalse,
|
|
);
|
|
});
|
|
});
|
|
|
|
test('comparison passes test that is ignored for this PR', () async {
|
|
when(mockSkiaClient.getImageBytes('55109a4bed52acc780530f7a9aeff6c0'))
|
|
.thenAnswer((_) => Future<List<int>>.value(_kTestPngBytes));
|
|
when(mockSkiaClient.testIsIgnoredForPullRequest(
|
|
'1234',
|
|
'library.flutter.golden_test.1.png',
|
|
))
|
|
.thenAnswer((_) => Future<bool>.value(true));
|
|
expect(
|
|
await comparator.compare(
|
|
Uint8List.fromList(_kFailPngBytes),
|
|
Uri.parse('flutter.golden_test.1.png'),
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('fails test that is not ignored', () async {
|
|
when(mockSkiaClient.getImageBytes('55109a4bed52acc780530f7a9aeff6c0'))
|
|
.thenAnswer((_) => Future<List<int>>.value(_kTestPngBytes));
|
|
when(mockSkiaClient.testIsIgnoredForPullRequest(
|
|
'1234',
|
|
'library.flutter.golden_test.1.png',
|
|
))
|
|
.thenAnswer((_) => Future<bool>.value(false));
|
|
expect(
|
|
await comparator.compare(
|
|
Uint8List.fromList(_kFailPngBytes),
|
|
Uri.parse('flutter.golden_test.1.png'),
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('passes non-existent baseline for new test', () async {
|
|
expect(
|
|
await comparator.compare(
|
|
Uint8List.fromList(_kFailPngBytes),
|
|
Uri.parse('flutter.new_golden_test.1.png'),
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
});
|
|
|
|
group('Skipping', () {
|
|
group('correctly determines testing environment', () {
|
|
test('returns true on LUCI', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'SWARMING_TASK_ID' : '1234567890',
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterSkippingGoldenFileComparator.isAvailableForEnvironment(platform),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('returns true on Cirrus', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
'CIRRUS_CI' : 'yep',
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterSkippingGoldenFileComparator.isAvailableForEnvironment(platform),
|
|
isTrue,
|
|
);
|
|
});
|
|
test('returns false', () {
|
|
platform = FakePlatform(
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': _kFlutterRoot,
|
|
},
|
|
operatingSystem: 'macos'
|
|
);
|
|
expect(
|
|
FlutterSkippingGoldenFileComparator.isAvailableForEnvironment(
|
|
platform),
|
|
isFalse,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
group('Local', () {
|
|
FlutterLocalFileComparator comparator;
|
|
final MockSkiaGoldClient mockSkiaClient = MockSkiaGoldClient();
|
|
|
|
setUp(() async {
|
|
final Directory basedir = fs.directory('flutter/test/library/')
|
|
..createSync(recursive: true);
|
|
comparator = FlutterLocalFileComparator(
|
|
basedir.uri,
|
|
mockSkiaClient,
|
|
fs: fs,
|
|
platform: FakePlatform(
|
|
environment: <String, String>{'FLUTTER_ROOT': _kFlutterRoot},
|
|
operatingSystem: 'macos'
|
|
),
|
|
);
|
|
|
|
when(mockSkiaClient.getImageBytes('55109a4bed52acc780530f7a9aeff6c0'))
|
|
.thenAnswer((_) => Future<List<int>>.value(_kTestPngBytes));
|
|
when(mockSkiaClient.expectations)
|
|
.thenReturn(expectationsTemplate());
|
|
when(mockSkiaClient.cleanTestName('library.flutter.golden_test.1.png'))
|
|
.thenReturn('flutter.golden_test.1');
|
|
when(mockSkiaClient.isValidDigestForExpectation(
|
|
'55109a4bed52acc780530f7a9aeff6c0',
|
|
'library.flutter.golden_test.1.png',
|
|
))
|
|
.thenAnswer((_) => Future<bool>.value(false));
|
|
});
|
|
|
|
test('passes when bytes match', () async {
|
|
expect(
|
|
await comparator.compare(
|
|
Uint8List.fromList(_kTestPngBytes),
|
|
Uri.parse('flutter.golden_test.1.png'),
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('passes non-existent baseline for new test', () async {
|
|
expect(
|
|
await comparator.compare(
|
|
Uint8List.fromList(_kFailPngBytes),
|
|
Uri.parse('flutter.new_golden_test.1'),
|
|
),
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('compare properly awaits validation & output before failing.', () async {
|
|
final Completer<bool> completer = Completer<bool>();
|
|
when(mockSkiaClient.isValidDigestForExpectation(
|
|
'55109a4bed52acc780530f7a9aeff6c0',
|
|
'library.flutter.golden_test.1.png',
|
|
))
|
|
.thenAnswer((_) => completer.future);
|
|
final Future<bool> result = comparator.compare(
|
|
Uint8List.fromList(_kFailPngBytes),
|
|
Uri.parse('flutter.golden_test.1.png'),
|
|
);
|
|
bool shouldThrow = true;
|
|
result.then((_) {
|
|
if (shouldThrow)
|
|
fail('Compare completed before validation completed!');
|
|
});
|
|
await Future<void>.value();
|
|
shouldThrow = false;
|
|
completer.complete(Future<bool>.value(false));
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
class MockProcessManager extends Mock implements ProcessManager {}
|
|
|
|
class MockSkiaGoldClient extends Mock implements SkiaGoldClient {}
|
|
|
|
class MockLocalFileComparator extends Mock implements LocalFileComparator {}
|
|
|
|
class MockHttpClient extends Mock implements HttpClient {}
|
|
|
|
class MockHttpClientRequest extends Mock implements HttpClientRequest {}
|
|
|
|
class MockHttpClientResponse extends Mock implements HttpClientResponse {
|
|
MockHttpClientResponse(this.response);
|
|
|
|
final Uint8List response;
|
|
|
|
@override
|
|
StreamSubscription<Uint8List> listen(
|
|
void onData(Uint8List event), {
|
|
Function onError,
|
|
void onDone(),
|
|
bool cancelOnError,
|
|
}) {
|
|
return Stream<Uint8List>.fromFuture(Future<Uint8List>.value(response))
|
|
.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
|
|
}
|
|
}
|
|
|
|
class MockHttpImageResponse extends Mock implements HttpClientResponse {
|
|
MockHttpImageResponse(this.response);
|
|
|
|
final List<List<int>> response;
|
|
|
|
@override
|
|
Future<void> forEach(void action(List<int> element)) async {
|
|
response.forEach(action);
|
|
}
|
|
}
|