Make the Skia expectation parser more resilient and update to "master_str". (#62898)

diff --git a/packages/flutter_goldens/test/flutter_goldens_test.dart b/packages/flutter_goldens/test/flutter_goldens_test.dart
index 919aa93..5045a09 100644
--- a/packages/flutter_goldens/test/flutter_goldens_test.dart
+++ b/packages/flutter_goldens/test/flutter_goldens_test.dart
@@ -34,6 +34,17 @@
   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<void> testWithOutput(String name, Future<void> 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<Future<void>>(body, zoneSpecification: ZoneSpecification(print: _recordPrint));
+    expect(output.toString(), expectedOutput);
+  });
+}
+
 void main() {
   MemoryFileSystem fs;
   FakePlatform platform;
@@ -341,6 +352,24 @@
         );
       });
 
+      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<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()) as Map<String, dynamic>;
@@ -851,7 +880,7 @@
           );
         });
 
-        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 @@
             ),
             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 @@
         );
       });
 
-      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 @@
           ),
           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<bool> completer = Completer<bool>();
diff --git a/packages/flutter_goldens/test/json_templates.dart b/packages/flutter_goldens/test/json_templates.dart
index f5f96ad..65bb70a 100644
--- a/packages/flutter_goldens/test/json_templates.dart
+++ b/packages/flutter_goldens/test/json_templates.dart
@@ -58,6 +58,29 @@
   };
 }
 
+/// 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 912524d..0710eeb 100644
--- a/packages/flutter_goldens_client/lib/skia_client.dart
+++ b/packages/flutter_goldens_client/lib/skia_client.dart
@@ -438,20 +438,30 @@
       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<String, dynamic> skiaJson = json.decode(rawResponse)['master'] as Map<String, dynamic>;
-
+        final dynamic jsonResponse = json.decode(rawResponse);
+        if (jsonResponse is! Map<String, dynamic>)
+          throw const FormatException('Skia gold expectations do not match expected format.');
+        final Map<String, dynamic> skiaJson = (jsonResponse[mainKey] ?? jsonResponse[temporaryKey]) as Map<String, dynamic>;
+        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<String, dynamic> hashesMap = value as Map<String, dynamic>;
           _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;
       }
     },