Skip to content

Commit 59fd4f9

Browse files
authored
Fix SkiaException -> TestFailure, add tests. (flutter#163054)
Fixes flutter#163051, mitigates flutter#162362.
1 parent 761c162 commit 59fd4f9

File tree

3 files changed

+134
-6
lines changed

3 files changed

+134
-6
lines changed

dev/tools/android_driver_extensions/lib/skia_gold.dart

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import 'dart:typed_data';
1313

1414
import 'package:file/local.dart';
1515
import 'package:flutter_goldens/skia_client.dart';
16+
import 'package:meta/meta.dart';
1617
import 'package:path/path.dart' as path;
1718
import 'package:platform/platform.dart';
1819
import 'package:process/process.dart';
20+
import 'package:test_api/test_api.dart';
1921

2022
import 'native_driver.dart';
2123

@@ -69,16 +71,30 @@ Future<void> enableSkiaGoldComparator({String? namePrefix}) async {
6971
httpClient: io.HttpClient(),
7072
log: io.stderr.writeln,
7173
);
74+
await enableSkiaGoldComparatorForTesting(
75+
skiaGoldClient,
76+
namePrefix: namePrefix,
77+
presubmit: isPresubmit,
78+
);
79+
}
80+
81+
/// Configures [goldenFileComparator] to use Skia Gold (for unit testing).
82+
@visibleForTesting
83+
Future<void> enableSkiaGoldComparatorForTesting(
84+
SkiaGoldClient skiaGoldClient, {
85+
required bool presubmit,
86+
String? namePrefix,
87+
}) async {
7288
await skiaGoldClient.auth();
73-
goldenFileComparator = _GoldenFileComparator(
89+
goldenFileComparator = _SkiaGoldComparator(
7490
skiaGoldClient,
7591
namePrefix: namePrefix,
76-
isPresubmit: isPresubmit,
92+
isPresubmit: presubmit,
7793
);
7894
}
7995

80-
final class _GoldenFileComparator extends GoldenFileComparator {
81-
_GoldenFileComparator(this.skiaClient, {required this.isPresubmit, this.namePrefix, Uri? baseDir})
96+
final class _SkiaGoldComparator extends GoldenFileComparator {
97+
_SkiaGoldComparator(this.skiaClient, {required this.isPresubmit, this.namePrefix, Uri? baseDir})
8298
: baseDir = baseDir ?? Uri.parse(path.dirname(io.Platform.script.path));
8399

84100
final Uri baseDir;
@@ -108,8 +124,18 @@ final class _GoldenFileComparator extends GoldenFileComparator {
108124
io.stderr.writeln('Skia Gold comparison succeeded comparing "$golden".');
109125
}
110126
return true;
111-
} else {
112-
return skiaClient.imgtestAdd(golden.path, _localFs.file(goldenFile.path));
127+
}
128+
129+
try {
130+
return await skiaClient.imgtestAdd(golden.path, _localFs.file(goldenFile.path));
131+
} on SkiaException catch (e) {
132+
// Convert SkiaException -> TestFailure so that this class implements the
133+
// contract of GoldenFileComparator, and matchesGoldenFile() converts the
134+
// TestFailure into a standard reported test error (with a better stack
135+
// trace, for example).
136+
//
137+
// https://github.com/flutter/flutter/issues/162621
138+
throw TestFailure('$e');
113139
}
114140
}
115141

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:typed_data';
6+
7+
import 'package:android_driver_extensions/native_driver.dart';
8+
// Similar to `flutter_test`, we ignore the implementation import.
9+
// ignore: implementation_imports
10+
import 'package:matcher/src/expect/async_matcher.dart';
11+
import 'package:test/fake.dart';
12+
import 'package:test/test.dart';
13+
14+
void main() {
15+
test('passes when the comparator passes', () async {
16+
goldenFileComparator = _FakeGoldenFileComparator(_CannedComparisonMode.alwaysPass);
17+
final AsyncMatcher matcher = matchesGoldenFile('test');
18+
await expectLater(matcher.matchAsync(Uint8List(0)), completion(isNull));
19+
});
20+
21+
test('fails with a default message when the comparator fails', () async {
22+
goldenFileComparator = _FakeGoldenFileComparator(_CannedComparisonMode.alwaysFail);
23+
final AsyncMatcher matcher = matchesGoldenFile('test');
24+
await expectLater(matcher.matchAsync(Uint8List(0)), completion(contains('does not match')));
25+
});
26+
27+
test('fails when the comparator throws a TestFailure', () async {
28+
goldenFileComparator = _FakeGoldenFileComparator(_CannedComparisonMode.alwaysThrowTestFailure);
29+
final AsyncMatcher matcher = matchesGoldenFile('test');
30+
await expectLater(matcher.matchAsync(Uint8List(0)), completion(contains('An expected error')));
31+
});
32+
33+
test('unhandled exception when the comparator throws anything but TestFailure', () async {
34+
goldenFileComparator = _FakeGoldenFileComparator(_CannedComparisonMode.alwaysThrowStateError);
35+
final AsyncMatcher matcher = matchesGoldenFile('test');
36+
await expectLater(matcher.matchAsync(Uint8List(0)), throwsStateError);
37+
});
38+
}
39+
40+
final class _FakeGoldenFileComparator extends Fake implements GoldenFileComparator {
41+
_FakeGoldenFileComparator(this._mode);
42+
final _CannedComparisonMode _mode;
43+
44+
@override
45+
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
46+
return switch (_mode) {
47+
_CannedComparisonMode.alwaysPass => true,
48+
_CannedComparisonMode.alwaysFail => false,
49+
_CannedComparisonMode.alwaysThrowTestFailure => throw TestFailure('An expected error'),
50+
_CannedComparisonMode.alwaysThrowStateError => throw StateError('An unexpected error'),
51+
};
52+
}
53+
54+
@override
55+
Uri getTestUri(Uri key, int? version) => key;
56+
}
57+
58+
enum _CannedComparisonMode { alwaysPass, alwaysFail, alwaysThrowTestFailure, alwaysThrowStateError }
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:typed_data';
6+
7+
import 'package:android_driver_extensions/native_driver.dart';
8+
import 'package:android_driver_extensions/skia_gold.dart';
9+
import 'package:file/src/interface/file.dart';
10+
import 'package:flutter_goldens/skia_client.dart';
11+
import 'package:test/fake.dart';
12+
import 'package:test/test.dart';
13+
14+
void main() {
15+
// Regression test for https://github.com/flutter/flutter/issues/163051.
16+
test('converts SkiaException to TestFailure in postsubmit', () async {
17+
final SkiaGoldClient skiaGold = _ThrowsSkiaException();
18+
await enableSkiaGoldComparatorForTesting(skiaGold, presubmit: false);
19+
20+
await expectLater(
21+
goldenFileComparator.compare(Uint8List(0), Uri(path: 'test.png')),
22+
throwsA(
23+
isA<TestFailure>().having(
24+
(Object e) => '$e',
25+
'description',
26+
contains('Skia Gold received an unapproved image in post'),
27+
),
28+
),
29+
);
30+
});
31+
}
32+
33+
final class _ThrowsSkiaException extends Fake implements SkiaGoldClient {
34+
@override
35+
Future<void> auth() async {}
36+
37+
@override
38+
Future<void> imgtestInit() async {}
39+
40+
@override
41+
Future<bool> imgtestAdd(String testName, File goldenFile) async {
42+
throw const SkiaException('Skia Gold received an unapproved image in post');
43+
}
44+
}

0 commit comments

Comments
 (0)