|  | // Copyright 2014 The Flutter Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | import 'dart:convert'; | 
|  | import 'dart:io'; | 
|  |  | 
|  | class TestSpecs { | 
|  |  | 
|  | TestSpecs({ | 
|  | required this.path, | 
|  | required this.startTime, | 
|  | }); | 
|  |  | 
|  | final String path; | 
|  | int startTime; | 
|  | int? _endTime; | 
|  |  | 
|  | int get milliseconds { | 
|  | return endTime - startTime; | 
|  | } | 
|  |  | 
|  | set endTime(int value) { | 
|  | _endTime = value; | 
|  | } | 
|  |  | 
|  | int get endTime { | 
|  | if (_endTime == null) { | 
|  | return 0; | 
|  | } | 
|  | return _endTime!; | 
|  | } | 
|  |  | 
|  | String toJson() { | 
|  | return json.encode( | 
|  | <String, String>{'path': path, 'runtime': milliseconds.toString()} | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | class TestFileReporterResults { | 
|  | TestFileReporterResults._({ | 
|  | required this.allTestSpecs, | 
|  | required this.hasFailedTests, | 
|  | required this.errors, | 
|  | }); | 
|  |  | 
|  | /// Intended to parse the output file of `dart test --file-reporter json:file_name | 
|  | factory TestFileReporterResults.fromFile(File metrics) { | 
|  | if (!metrics.existsSync()) { | 
|  | throw Exception('${metrics.path} does not exist'); | 
|  | } | 
|  |  | 
|  | final Map<int, TestSpecs> testSpecs = <int, TestSpecs>{}; | 
|  | bool hasFailedTests = true; | 
|  | final List<String> errors = <String>[]; | 
|  |  | 
|  | for (final String metric in metrics.readAsLinesSync()) { | 
|  | final Map<String, Object?> entry = json.decode(metric) as Map<String, Object?>; | 
|  | if (entry.containsKey('suite')) { | 
|  | final Map<String, Object?> suite = entry['suite']! as Map<String, Object?>; | 
|  | addTestSpec(suite, entry['time']! as int, testSpecs); | 
|  | } else if (isMetricDone(entry, testSpecs)) { | 
|  | final Map<String, Object?> group = entry['group']! as Map<String, Object?>; | 
|  | final int suiteID = group['suiteID']! as int; | 
|  | addMetricDone(suiteID, entry['time']! as int, testSpecs); | 
|  | } else if (entry.containsKey('error')) { | 
|  | final String stackTrace = entry.containsKey('stackTrace') ? entry['stackTrace']! as String : ''; | 
|  | errors.add('${entry['error']}\n $stackTrace'); | 
|  | } else if (entry.containsKey('success') && entry['success'] == true) { | 
|  | hasFailedTests = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return TestFileReporterResults._(allTestSpecs: testSpecs, hasFailedTests: hasFailedTests, errors: errors); | 
|  | } | 
|  |  | 
|  | final Map<int, TestSpecs> allTestSpecs; | 
|  | final bool hasFailedTests; | 
|  | final List<String> errors; | 
|  |  | 
|  |  | 
|  | static void addTestSpec(Map<String, Object?> suite, int time, Map<int, TestSpecs> allTestSpecs) { | 
|  | allTestSpecs[suite['id']! as int] = TestSpecs( | 
|  | path: suite['path']! as String, | 
|  | startTime: time, | 
|  | ); | 
|  | } | 
|  |  | 
|  | static void addMetricDone(int suiteID, int time, Map<int, TestSpecs> allTestSpecs) { | 
|  | final TestSpecs testSpec = allTestSpecs[suiteID]!; | 
|  | testSpec.endTime = time; | 
|  | } | 
|  |  | 
|  | static bool isMetricDone(Map<String, Object?> entry, Map<int, TestSpecs> allTestSpecs) { | 
|  | if (entry.containsKey('group') && entry['type']! as String == 'group') { | 
|  | final Map<String, Object?> group = entry['group']! as Map<String, Object?>; | 
|  | return allTestSpecs.containsKey(group['suiteID']! as int); | 
|  | } | 
|  | return false; | 
|  | } | 
|  | } |