1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:convert';
6import 'dart:io';
7
8/// A result of running a single task.
9class TaskResult {
10 TaskResult.buildOnly()
11 : succeeded = true,
12 data = null,
13 detailFiles = null,
14 benchmarkScoreKeys = null,
15 message = 'No tests run';
16
17 /// Constructs a successful result.
18 TaskResult.success(
19 this.data, {
20 this.benchmarkScoreKeys = const <String>[],
21 this.detailFiles = const <String>[],
22 this.message = 'success',
23 }) : succeeded = true {
24 const JsonEncoder prettyJson = JsonEncoder.withIndent(' ');
25 if (benchmarkScoreKeys != null) {
26 for (final String key in benchmarkScoreKeys!) {
27 if (!data!.containsKey(key)) {
28 throw 'Invalid benchmark score key "$key". It does not exist in task '
29 'result data ${prettyJson.convert(data)}';
30 } else if (data![key] is! num) {
31 throw 'Invalid benchmark score for key "$key". It is expected to be a num '
32 'but was ${(data![key] as Object).runtimeType}: ${prettyJson.convert(data![key])}';
33 }
34 }
35 }
36 }
37
38 /// Constructs a successful result using JSON data stored in a file.
39 factory TaskResult.successFromFile(
40 File file, {
41 List<String> benchmarkScoreKeys = const <String>[],
42 List<String> detailFiles = const <String>[],
43 }) {
44 return TaskResult.success(
45 json.decode(file.readAsStringSync()) as Map<String, dynamic>?,
46 benchmarkScoreKeys: benchmarkScoreKeys,
47 detailFiles: detailFiles,
48 );
49 }
50
51 /// Constructs a [TaskResult] from JSON.
52 factory TaskResult.fromJson(Map<String, dynamic> json) {
53 final bool success = json['success'] as bool;
54 if (success) {
55 final List<String> benchmarkScoreKeys =
56 (json['benchmarkScoreKeys'] as List<dynamic>? ?? <String>[]).cast<String>();
57 final List<String> detailFiles = (json['detailFiles'] as List<dynamic>? ?? <String>[])
58 .cast<String>();
59 return TaskResult.success(
60 json['data'] as Map<String, dynamic>?,
61 benchmarkScoreKeys: benchmarkScoreKeys,
62 detailFiles: detailFiles,
63 message: json['reason'] as String?,
64 );
65 }
66
67 return TaskResult.failure(json['reason'] as String?);
68 }
69
70 /// Constructs an unsuccessful result.
71 TaskResult.failure(this.message)
72 : succeeded = false,
73 data = null,
74 detailFiles = null,
75 benchmarkScoreKeys = null;
76
77 /// Whether the task succeeded.
78 final bool succeeded;
79
80 /// Task-specific JSON data
81 final Map<String, dynamic>? data;
82
83 /// Files containing detail on the run (e.g. timeline trace files)
84 final List<String>? detailFiles;
85
86 /// Keys in [data] that store scores that will be submitted to Cocoon.
87 ///
88 /// Each key is also part of a benchmark's name tracked by Cocoon.
89 final List<String>? benchmarkScoreKeys;
90
91 /// Whether the task failed.
92 bool get failed => !succeeded;
93
94 /// Explains the result in a human-readable format.
95 final String? message;
96
97 /// Serializes this task result to JSON format.
98 ///
99 /// The JSON format is as follows:
100 ///
101 /// {
102 /// "success": true|false,
103 /// "data": arbitrary JSON data valid only for successful results,
104 /// "detailFiles": list of filenames containing detail on the run
105 /// "benchmarkScoreKeys": [
106 /// contains keys into "data" that represent benchmarks scores, which
107 /// can be uploaded, for example. to golem, valid only for successful
108 /// results
109 /// ],
110 /// "reason": failure reason string valid only for unsuccessful results
111 /// }
112 Map<String, dynamic> toJson() {
113 final Map<String, dynamic> json = <String, dynamic>{'success': succeeded};
114
115 if (succeeded) {
116 json['data'] = data;
117 json['detailFiles'] = detailFiles;
118 json['benchmarkScoreKeys'] = benchmarkScoreKeys;
119 }
120
121 if (message != null || !succeeded) {
122 json['reason'] = message;
123 }
124
125 return json;
126 }
127
128 @override
129 String toString() => message ?? '';
130}
131
132class TaskResultCheckProcesses extends TaskResult {
133 TaskResultCheckProcesses() : super.success(null);
134}
135