diff --git a/packages/flutter_goldens/test/flutter_goldens_test.dart b/packages/flutter_goldens/test/flutter_goldens_test.dart index 919aa93eb38..5045a095b3d 100644 --- a/packages/flutter_goldens/test/flutter_goldens_test.dart +++ b/packages/flutter_goldens/test/flutter_goldens_test.dart @@ -34,6 +34,17 @@ const List _kFailPngBytes = 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]; +Future testWithOutput(String name, Future body(), String expectedOutput) async { + test(name, () async { + final StringBuffer output = StringBuffer(); + void _recordPrint(Zone self, ZoneDelegate parent, Zone zone, String line) { + output.write(line); + } + await runZoned>(body, zoneSpecification: ZoneSpecification(print: _recordPrint)); + expect(output.toString(), expectedOutput); + }); +} + void main() { MemoryFileSystem fs; FakePlatform platform; @@ -341,6 +352,24 @@ void main() { ); }); + test('sets up expectations with temporary key', () async { + url = Uri.parse('https://flutter-gold.skia.org/json/expectations/commit/HEAD'); + final MockHttpClientResponse mockHttpResponse = MockHttpClientResponse( + utf8.encode(rawExpectationsTemplateWithTemporaryKey()) + ); + when(mockHttpClient.getUrl(url)) + .thenAnswer((_) => Future.value(mockHttpRequest)); + when(mockHttpRequest.close()) + .thenAnswer((_) => Future.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 skiaJson = json.decode(digestResponseTemplate()) as Map; @@ -851,7 +880,7 @@ void main() { ); }); - test('passes non-existent baseline for new test', () async { + testWithOutput('passes non-existent baseline for new test', () async { when(mockSkiaClient.cleanTestName('library.flutter.new_golden_test.1.png')) .thenReturn('flutter.new_golden_test.1'); expect( @@ -861,7 +890,8 @@ void main() { ), isTrue, ); - }); + }, 'No expectations provided by Skia Gold for test: library.flutter.new_golden_test.1.png. ' + 'This may be a new test. If this is an unexpected result, check https://flutter-gold.skia.org.\n'); }); }); @@ -937,7 +967,7 @@ void main() { ); }); - test('passes non-existent baseline for new test', () async { + testWithOutput('passes non-existent baseline for new test', () async { expect( await comparator.compare( Uint8List.fromList(_kFailPngBytes), @@ -945,7 +975,10 @@ void main() { ), isTrue, ); - }); + }, 'No expectations provided by Skia Gold for test: library.flutter.new_golden_test.1. ' + 'This may be a new test. If this is an unexpected result, check https://flutter-gold.skia.org.\n' + 'Validate image output found at flutter/test/library/' + ); test('compare properly awaits validation & output before failing.', () async { final Completer completer = Completer(); diff --git a/packages/flutter_goldens/test/json_templates.dart b/packages/flutter_goldens/test/json_templates.dart index f5f96ad71a1..65bb70adb14 100644 --- a/packages/flutter_goldens/test/json_templates.dart +++ b/packages/flutter_goldens/test/json_templates.dart @@ -58,6 +58,29 @@ Map> expectationsTemplate() { }; } +/// Same as [rawExpectationsTemplate] but with the temporary key. +String rawExpectationsTemplateWithTemporaryKey() { + return ''' + { + "md5": "a7489b00e03a1846e43500b7c14dd7b0", + "master_str": { + "flutter.golden_test.1": { + "55109a4bed52acc780530f7a9aeff6c0": 1 + }, + "flutter.golden_test.3": { + "87cb35131e6ad4b57d4d09d59ae743c3": 1, + "dc94eb2c39c0c8ae11a4efd090b72f94": 1, + "f2583c9003978a06b7888878bdc089e2": 1 + }, + "flutter.golden_test.2": { + "eb03a5e3114c9ecad5e4f1178f285a49": 1, + "f14631979de24fca6e14ad247d5f2bd6": 1 + } + } + } + '''; +} + /// Json response template for Skia Gold digest request: /// https://flutter-gold.skia.org/json/details?test=[testName]&digest=[expectation] String digestResponseTemplate({ diff --git a/packages/flutter_goldens_client/lib/skia_client.dart b/packages/flutter_goldens_client/lib/skia_client.dart index 912524d4a42..0710eeb7e57 100644 --- a/packages/flutter_goldens_client/lib/skia_client.dart +++ b/packages/flutter_goldens_client/lib/skia_client.dart @@ -438,20 +438,30 @@ class SkiaGoldClient { final Uri requestForExpectations = Uri.parse( 'https://flutter-gold.skia.org/json/expectations/commit/HEAD' ); + const String mainKey = 'master'; + const String temporaryKey = 'master_str'; String rawResponse; try { final io.HttpClientRequest request = await httpClient.getUrl(requestForExpectations); final io.HttpClientResponse response = await request.close(); rawResponse = await utf8.decodeStream(response); - final Map skiaJson = json.decode(rawResponse)['master'] as Map; - + final dynamic jsonResponse = json.decode(rawResponse); + if (jsonResponse is! Map) + throw const FormatException('Skia gold expectations do not match expected format.'); + final Map skiaJson = (jsonResponse[mainKey] ?? jsonResponse[temporaryKey]) as Map; + if (skiaJson == null) + throw FormatException('Skia gold expectations are missing the "$mainKey" key (and also doesn\'t have "$temporaryKey")! Available keys: ${jsonResponse.keys.join(", ")}'); skiaJson.forEach((String key, dynamic value) { final Map hashesMap = value as Map; _expectations[key] = hashesMap.keys.toList(); }); - } on FormatException catch(_) { - print('Formatting error detected requesting expectations from Flutter Gold.\n' - 'rawResponse: $rawResponse'); + } on FormatException catch (error) { + print( + 'Formatting error detected requesting expectations from Flutter Gold.\n' + 'error: $error\n' + 'url: $requestForExpectations\n' + 'response: $rawResponse' + ); rethrow; } },