Be more resilient to bad data from VM (#18132)

Fixes https://github.com/flutter/flutter/issues/14517
Or at least, fixes the crash part of that.
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index dcb16ae..6c98710 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -412,7 +412,24 @@
   /// Prints errors if [printErrors] is [true].
   static bool validateReloadReport(Map<String, dynamic> reloadReport,
       { bool printErrors: true }) {
-    if (reloadReport['type'] != 'ReloadReport') {
+    if (reloadReport == null) {
+      if (printErrors)
+        printError('Hot reload did not receive reload report.');
+      return false;
+    }
+    if (!(reloadReport['type'] == 'ReloadReport' &&
+          (reloadReport['success'] == true ||
+           (reloadReport['success'] == false &&
+            (reloadReport['details'] is Map<String, dynamic> &&
+             reloadReport['details']['notices'] is List<dynamic> &&
+             reloadReport['details']['notices'].isNotEmpty &&
+             reloadReport['details']['notices'].every(
+               (dynamic item) => item is Map<String, dynamic> && item['message'] is String
+             )
+            )
+           )
+          )
+         )) {
       if (printErrors)
         printError('Hot reload received invalid response: $reloadReport');
       return false;
diff --git a/packages/flutter_tools/test/hot_test.dart b/packages/flutter_tools/test/hot_test.dart
index a09d174..73f7fd8 100644
--- a/packages/flutter_tools/test/hot_test.dart
+++ b/packages/flutter_tools/test/hot_test.dart
@@ -11,9 +11,76 @@
   group('validateReloadReport', () {
     testUsingContext('invalid', () async {
       expect(HotRunner.validateReloadReport(<String, dynamic>{}), false);
-      expect(HotRunner.validateReloadReport(
-          <String, dynamic>{'type': 'ReloadReport', 'success': true, 'details': <String, dynamic>{}}),
-          true);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': false,
+        'details': <String, dynamic>{},
+      }), false);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': false,
+        'details': <String, dynamic>{
+          'notices': <Map<String, dynamic>>[
+          ],
+        },
+      }), false);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': false,
+        'details': <String, dynamic>{
+          'notices': <String, dynamic>{
+            'message': 'error',
+          },
+        },
+      }), false);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': false,
+        'details': <String, dynamic>{
+          'notices': <Map<String, dynamic>>[],
+        },
+      }), false);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': false,
+        'details': <String, dynamic>{
+          'notices': <Map<String, dynamic>>[
+            <String, dynamic>{ 'message': false, }
+          ],
+        },
+      }), false);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': false,
+        'details': <String, dynamic>{
+          'notices': <Map<String, dynamic>>[
+            <String, dynamic>{ 'message': <String>['error'], },
+          ],
+        },
+      }), false);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': false,
+        'details': <String, dynamic>{
+          'notices': <Map<String, dynamic>>[
+            <String, dynamic>{ 'message': 'error', },
+            <String, dynamic>{ 'message': <String>['error'], },
+          ],
+        },
+      }), false);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': false,
+        'details': <String, dynamic>{
+          'notices': <Map<String, dynamic>>[
+            <String, dynamic>{ 'message': 'error', },
+          ],
+        },
+      }), false);
+      expect(HotRunner.validateReloadReport(<String, dynamic>{
+        'type': 'ReloadReport',
+        'success': true,
+      }), true);
     });
   });
 }