Unverified Commit 895beb04 authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Initialize the Skia client more efficiently (#95055)

parent 37200a0b
...@@ -227,12 +227,12 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -227,12 +227,12 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator {
goldens ??= SkiaGoldClient(baseDirectory); goldens ??= SkiaGoldClient(baseDirectory);
await goldens.auth(); await goldens.auth();
await goldens.imgtestInit();
return FlutterPostSubmitFileComparator(baseDirectory.uri, goldens); return FlutterPostSubmitFileComparator(baseDirectory.uri, goldens);
} }
@override @override
Future<bool> compare(Uint8List imageBytes, Uri golden) async { Future<bool> compare(Uint8List imageBytes, Uri golden) async {
await skiaClient.imgtestInit();
golden = _addPrefix(golden); golden = _addPrefix(golden);
await update(golden, imageBytes); await update(golden, imageBytes);
final File goldenFile = getGoldenFile(golden); final File goldenFile = getGoldenFile(golden);
...@@ -309,16 +309,15 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -309,16 +309,15 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator {
goldens ??= SkiaGoldClient(baseDirectory); goldens ??= SkiaGoldClient(baseDirectory);
await goldens.auth(); await goldens.auth();
await goldens.tryjobInit();
return FlutterPreSubmitFileComparator( return FlutterPreSubmitFileComparator(
baseDirectory.uri, baseDirectory.uri,
goldens, goldens, platform: platform,
platform: platform,
); );
} }
@override @override
Future<bool> compare(Uint8List imageBytes, Uri golden) async { Future<bool> compare(Uint8List imageBytes, Uri golden) async {
await skiaClient.tryjobInit();
golden = _addPrefix(golden); golden = _addPrefix(golden);
await update(golden, imageBytes); await update(golden, imageBytes);
final File goldenFile = getGoldenFile(golden); final File goldenFile = getGoldenFile(golden);
......
...@@ -100,7 +100,7 @@ void main() { ...@@ -100,7 +100,7 @@ void main() {
httpClient: fakeHttpClient, httpClient: fakeHttpClient,
); );
process.fallbackProcessResult = ProcessResult(123, 1, 'fail', 'fail'); process.fallbackProcessResult = ProcessResult(123, 1, 'Fallback failure', 'Fallback failure');
expect( expect(
skiaClient.auth(), skiaClient.auth(),
...@@ -125,8 +125,6 @@ void main() { ...@@ -125,8 +125,6 @@ void main() {
httpClient: fakeHttpClient, httpClient: fakeHttpClient,
); );
process.fallbackProcessResult = ProcessResult(123, 1, 'fail', 'fail');
const RunInvocation gitInvocation = RunInvocation( const RunInvocation gitInvocation = RunInvocation(
<String>['git', 'rev-parse', 'HEAD'], <String>['git', 'rev-parse', 'HEAD'],
'/flutter', '/flutter',
...@@ -145,7 +143,8 @@ void main() { ...@@ -145,7 +143,8 @@ void main() {
null, null,
); );
process.processResults[gitInvocation] = ProcessResult(12345678, 0, '12345678', ''); process.processResults[gitInvocation] = ProcessResult(12345678, 0, '12345678', '');
process.processResults[goldctlInvocation] = ProcessResult(123, 1, 'fail', 'fail'); process.processResults[goldctlInvocation] = ProcessResult(123, 1, 'Expected failure', 'Expected failure');
process.fallbackProcessResult = ProcessResult(123, 1, 'Fallback failure', 'Fallback failure');
expect( expect(
skiaClient.imgtestInit(), skiaClient.imgtestInit(),
...@@ -153,6 +152,112 @@ void main() { ...@@ -153,6 +152,112 @@ void main() {
); );
}); });
test('Only calls init once', () async {
platform = FakePlatform(
environment: <String, String>{
'FLUTTER_ROOT': _kFlutterRoot,
'GOLDCTL' : 'goldctl',
},
operatingSystem: 'macos'
);
skiaClient = SkiaGoldClient(
workDirectory,
fs: fs,
process: process,
platform: platform,
httpClient: fakeHttpClient,
);
const RunInvocation gitInvocation = RunInvocation(
<String>['git', 'rev-parse', 'HEAD'],
'/flutter',
);
const RunInvocation goldctlInvocation = RunInvocation(
<String>[
'goldctl',
'imgtest', 'init',
'--instance', 'flutter',
'--work-dir', '/workDirectory/temp',
'--commit', '1234',
'--keys-file', '/workDirectory/keys.json',
'--failure-file', '/workDirectory/failures.json',
'--passfail',
],
null,
);
process.processResults[gitInvocation] = ProcessResult(1234, 0, '1234', '');
process.processResults[goldctlInvocation] = ProcessResult(5678, 0, '5678', '');
process.fallbackProcessResult = ProcessResult(123, 1, 'Fallback failure', 'Fallback failure');
// First call
await skiaClient.imgtestInit();
// Remove fake process result.
// If the init call is executed again, the fallback process will throw.
process.processResults.remove(goldctlInvocation);
// Second call
await skiaClient.imgtestInit();
});
test('Only calls tryjob init once', () async {
platform = FakePlatform(
environment: <String, String>{
'FLUTTER_ROOT': _kFlutterRoot,
'GOLDCTL' : 'goldctl',
'SWARMING_TASK_ID' : '4ae997b50dfd4d11',
'LOGDOG_STREAM_PREFIX' : 'buildbucket/cr-buildbucket.appspot.com/8885996262141582672',
'GOLD_TRYJOB' : 'refs/pull/49815/head',
},
operatingSystem: 'macos'
);
skiaClient = SkiaGoldClient(
workDirectory,
fs: fs,
process: process,
platform: platform,
httpClient: fakeHttpClient,
);
const RunInvocation gitInvocation = RunInvocation(
<String>['git', 'rev-parse', 'HEAD'],
'/flutter',
);
const RunInvocation goldctlInvocation = RunInvocation(
<String>[
'goldctl',
'imgtest', 'init',
'--instance', 'flutter',
'--work-dir', '/workDirectory/temp',
'--commit', '1234',
'--keys-file', '/workDirectory/keys.json',
'--failure-file', '/workDirectory/failures.json',
'--passfail',
'--crs', 'github',
'--patchset_id', '1234',
'--changelist', '49815',
'--cis', 'buildbucket',
'--jobid', '8885996262141582672',
],
null,
);
process.processResults[gitInvocation] = ProcessResult(1234, 0, '1234', '');
process.processResults[goldctlInvocation] = ProcessResult(5678, 0, '5678', '');
process.fallbackProcessResult = ProcessResult(123, 1, 'Fallback failure', 'Fallback failure');
// First call
await skiaClient.tryjobInit();
// Remove fake process result.
// If the init call is executed again, the fallback process will throw.
process.processResults.remove(goldctlInvocation);
// Second call
await skiaClient.tryjobInit();
});
test('throws for error state from imgtestAdd', () { test('throws for error state from imgtestAdd', () {
final File goldenFile = fs.file('/workDirectory/temp/golden_file_test.png') final File goldenFile = fs.file('/workDirectory/temp/golden_file_test.png')
..createSync(recursive: true); ..createSync(recursive: true);
...@@ -172,7 +277,6 @@ void main() { ...@@ -172,7 +277,6 @@ void main() {
httpClient: fakeHttpClient, httpClient: fakeHttpClient,
); );
process.fallbackProcessResult = ProcessResult(123, 1, 'fail', 'fail');
const RunInvocation goldctlInvocation = RunInvocation( const RunInvocation goldctlInvocation = RunInvocation(
<String>[ <String>[
'goldctl', 'goldctl',
...@@ -184,7 +288,8 @@ void main() { ...@@ -184,7 +288,8 @@ void main() {
], ],
null, null,
); );
process.processResults[goldctlInvocation] = ProcessResult(123, 1, 'fail', 'fail'); process.processResults[goldctlInvocation] = ProcessResult(123, 1, 'Expected failure', 'Expected failure');
process.fallbackProcessResult = ProcessResult(123, 1, 'Fallback failure', 'Fallback failure');
expect( expect(
skiaClient.imgtestAdd('golden_file_test', goldenFile), skiaClient.imgtestAdd('golden_file_test', goldenFile),
...@@ -327,7 +432,7 @@ void main() { ...@@ -327,7 +432,7 @@ void main() {
}); });
group('FlutterGoldenFileComparator', () { group('FlutterGoldenFileComparator', () {
late FlutterPostSubmitFileComparator comparator; late FlutterGoldenFileComparator comparator;
setUp(() { setUp(() {
final Directory basedir = fs.directory('flutter/test/library/') final Directory basedir = fs.directory('flutter/test/library/')
...@@ -362,9 +467,10 @@ void main() { ...@@ -362,9 +467,10 @@ void main() {
}); });
group('Post-Submit', () { group('Post-Submit', () {
final FakeSkiaGoldClient fakeSkiaClient = FakeSkiaGoldClient(); late FakeSkiaGoldClient fakeSkiaClient;
setUp(() { setUp(() {
fakeSkiaClient = FakeSkiaGoldClient();
final Directory basedir = fs.directory('flutter/test/library/') final Directory basedir = fs.directory('flutter/test/library/')
..createSync(recursive: true); ..createSync(recursive: true);
comparator = FlutterPostSubmitFileComparator( comparator = FlutterPostSubmitFileComparator(
...@@ -375,6 +481,24 @@ void main() { ...@@ -375,6 +481,24 @@ void main() {
); );
}); });
test('calls init during compare', () {
expect(fakeSkiaClient.initCalls, 0);
comparator.compare(
Uint8List.fromList(_kTestPngBytes),
Uri.parse('flutter.golden_test.1.png'),
);
expect(fakeSkiaClient.initCalls, 1);
});
test('does not call init in during construction', () {
expect(fakeSkiaClient.initCalls, 0);
FlutterPostSubmitFileComparator.fromDefaultComparator(
platform,
goldens: fakeSkiaClient,
);
expect(fakeSkiaClient.initCalls, 0);
});
group('correctly determines testing environment', () { group('correctly determines testing environment', () {
test('returns true for configured Luci', () { test('returns true for configured Luci', () {
platform = FakePlatform( platform = FakePlatform(
...@@ -441,6 +565,38 @@ void main() { ...@@ -441,6 +565,38 @@ void main() {
}); });
group('Pre-Submit', () { group('Pre-Submit', () {
late FakeSkiaGoldClient fakeSkiaClient;
setUp(() {
fakeSkiaClient = FakeSkiaGoldClient();
final Directory basedir = fs.directory('flutter/test/library/')
..createSync(recursive: true);
comparator = FlutterPreSubmitFileComparator(
basedir.uri,
fakeSkiaClient,
fs: fs,
platform: platform,
);
});
test('calls init during compare', () {
expect(fakeSkiaClient.tryInitCalls, 0);
comparator.compare(
Uint8List.fromList(_kTestPngBytes),
Uri.parse('flutter.golden_test.1.png'),
);
expect(fakeSkiaClient.tryInitCalls, 1);
});
test('does not call init in during construction', () {
expect(fakeSkiaClient.tryInitCalls, 0);
FlutterPostSubmitFileComparator.fromDefaultComparator(
platform,
goldens: fakeSkiaClient,
);
expect(fakeSkiaClient.tryInitCalls, 0);
});
group('correctly determines testing environment', () { group('correctly determines testing environment', () {
test('returns true for Luci', () { test('returns true for Luci', () {
platform = FakePlatform( platform = FakePlatform(
...@@ -707,6 +863,21 @@ class FakeSkiaGoldClient extends Fake implements SkiaGoldClient { ...@@ -707,6 +863,21 @@ class FakeSkiaGoldClient extends Fake implements SkiaGoldClient {
return expectationForTestValues[testName] ?? ''; return expectationForTestValues[testName] ?? '';
} }
@override
Future<void> auth() async {}
int initCalls = 0;
@override
Future<void> imgtestInit() async => initCalls += 1;
@override
Future<bool> imgtestAdd(String testName, File goldenFile) async => true;
int tryInitCalls = 0;
@override
Future<void> tryjobInit() async => tryInitCalls += 1;
@override
Future<bool> tryjobAdd(String testName, File goldenFile) async => true;
Map<String, List<int>> imageBytesValues = <String, List<int>>{}; Map<String, List<int>> imageBytesValues = <String, List<int>>{};
@override @override
Future<List<int>> getImageBytes(String imageHash) async => imageBytesValues[imageHash]!; Future<List<int>> getImageBytes(String imageHash) async => imageBytesValues[imageHash]!;
......
...@@ -109,12 +109,24 @@ class SkiaGoldClient { ...@@ -109,12 +109,24 @@ class SkiaGoldClient {
} }
} }
/// Signals if this client is initialized for uploading images to the Gold
/// service.
///
/// Since Flutter framework tests are executed in parallel, and in random
/// order, this will signal is this instance of the Gold client has been
/// initialized.
bool _initialized = false;
/// Executes the `imgtest init` command in the goldctl tool. /// Executes the `imgtest init` command in the goldctl tool.
/// ///
/// The `imgtest` command collects and uploads test results to the Skia Gold /// The `imgtest` command collects and uploads test results to the Skia Gold
/// backend, the `init` argument initializes the current test. Used by the /// backend, the `init` argument initializes the current test. Used by the
/// [FlutterPostSubmitFileComparator]. /// [FlutterPostSubmitFileComparator].
Future<void> imgtestInit() async { Future<void> imgtestInit() async {
// This client has already been intialized
if (_initialized)
return;
final File keys = workDirectory.childFile('keys.json'); final File keys = workDirectory.childFile('keys.json');
final File failures = workDirectory.childFile('failures.json'); final File failures = workDirectory.childFile('failures.json');
...@@ -147,6 +159,7 @@ class SkiaGoldClient { ...@@ -147,6 +159,7 @@ class SkiaGoldClient {
final io.ProcessResult result = await process.run(imgtestInitCommand); final io.ProcessResult result = await process.run(imgtestInitCommand);
if (result.exitCode != 0) { if (result.exitCode != 0) {
_initialized = false;
final StringBuffer buf = StringBuffer() final StringBuffer buf = StringBuffer()
..writeln('Skia Gold imgtest init failed.') ..writeln('Skia Gold imgtest init failed.')
..writeln('An error occurred when initializing golden file test with ') ..writeln('An error occurred when initializing golden file test with ')
...@@ -157,6 +170,7 @@ class SkiaGoldClient { ...@@ -157,6 +170,7 @@ class SkiaGoldClient {
..writeln('stderr: ${result.stderr}'); ..writeln('stderr: ${result.stderr}');
throw Exception(buf.toString()); throw Exception(buf.toString());
} }
_initialized = true;
} }
/// Executes the `imgtest add` command in the goldctl tool. /// Executes the `imgtest add` command in the goldctl tool.
...@@ -204,12 +218,24 @@ class SkiaGoldClient { ...@@ -204,12 +218,24 @@ class SkiaGoldClient {
return true; return true;
} }
/// Signals if this client is initialized for uploading tryjobs to the Gold
/// service.
///
/// Since Flutter framework tests are executed in parallel, and in random
/// order, this will signal is this instance of the Gold client has been
/// initialized for tryjobs.
bool _tryjobInitialized = false;
/// Executes the `imgtest init` command in the goldctl tool for tryjobs. /// Executes the `imgtest init` command in the goldctl tool for tryjobs.
/// ///
/// The `imgtest` command collects and uploads test results to the Skia Gold /// The `imgtest` command collects and uploads test results to the Skia Gold
/// backend, the `init` argument initializes the current tryjob. Used by the /// backend, the `init` argument initializes the current tryjob. Used by the
/// [FlutterPreSubmitFileComparator]. /// [FlutterPreSubmitFileComparator].
Future<void> tryjobInit() async { Future<void> tryjobInit() async {
// This client has already been initialized
if (_tryjobInitialized)
return;
final File keys = workDirectory.childFile('keys.json'); final File keys = workDirectory.childFile('keys.json');
final File failures = workDirectory.childFile('failures.json'); final File failures = workDirectory.childFile('failures.json');
...@@ -245,6 +271,7 @@ class SkiaGoldClient { ...@@ -245,6 +271,7 @@ class SkiaGoldClient {
final io.ProcessResult result = await process.run(imgtestInitCommand); final io.ProcessResult result = await process.run(imgtestInitCommand);
if (result.exitCode != 0) { if (result.exitCode != 0) {
_tryjobInitialized = false;
final StringBuffer buf = StringBuffer() final StringBuffer buf = StringBuffer()
..writeln('Skia Gold tryjobInit failure.') ..writeln('Skia Gold tryjobInit failure.')
..writeln('An error occurred when initializing golden file tryjob with ') ..writeln('An error occurred when initializing golden file tryjob with ')
...@@ -255,6 +282,7 @@ class SkiaGoldClient { ...@@ -255,6 +282,7 @@ class SkiaGoldClient {
..writeln('stderr: ${result.stderr}'); ..writeln('stderr: ${result.stderr}');
throw Exception(buf.toString()); throw Exception(buf.toString());
} }
_tryjobInitialized = true;
} }
/// Executes the `imgtest add` command in the goldctl tool for tryjobs. /// Executes the `imgtest add` command in the goldctl tool for tryjobs.
......
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