Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(815)

Side by Side Diff: tools/gardening/lib/src/compare_failures_impl.dart

Issue 2999623002: Improve compare-failures output for multiple [BuildUri] results (Closed)
Patch Set: Updated cf. comments Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/gardening/test/compare_failures_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// Compares the test log of a build step with previous builds. 5 /// Compares the test log of a build step with previous builds.
6 /// 6 ///
7 /// Use this to detect flakiness of failures, especially timeouts. 7 /// Use this to detect flakiness of failures, especially timeouts.
8 8
9 import 'dart:async'; 9 import 'dart:async';
10 10
11 import 'package:gardening/src/buildbot_structures.dart'; 11 import 'package:gardening/src/buildbot_structures.dart';
12 import 'package:gardening/src/buildbot_data.dart'; 12 import 'package:gardening/src/buildbot_data.dart';
13 import 'package:gardening/src/client.dart'; 13 import 'package:gardening/src/client.dart';
14 import 'package:gardening/src/util.dart'; 14 import 'package:gardening/src/util.dart';
15 15
16 Future mainInternal(BuildbotClient client, List<String> args, 16 Future mainInternal(BuildbotClient client, List<String> args,
17 {int runCount: 10}) async { 17 {int runCount: 10}) async {
18 printBuildResultsSummary( 18 printBuildResultsSummary(
19 await loadBuildResults(client, args, runCount: runCount)); 19 await loadBuildResults(client, args, runCount: runCount), args);
20 } 20 }
21 21
22 /// Loads [BuildResult]s for the [runCount] last builds for the build(s) in 22 /// Loads [BuildResult]s for the [runCount] last builds for the build(s) in
23 /// [args]. [args] can be a list of [BuildGroup] names or a list of log uris. 23 /// [args]. [args] can be a list of [BuildGroup] names or a list of log uris.
24 Future<Map<BuildUri, List<BuildResult>>> loadBuildResults( 24 Future<Map<BuildUri, List<BuildResult>>> loadBuildResults(
25 BuildbotClient client, List<String> args, 25 BuildbotClient client, List<String> args,
26 {int runCount: 10}) async { 26 {int runCount: 10}) async {
27 List<BuildUri> buildUriList = <BuildUri>[]; 27 List<BuildUri> buildUriList = <BuildUri>[];
28 for (BuildGroup buildGroup in buildGroups) { 28 for (BuildGroup buildGroup in buildGroups) {
29 if (args.contains(buildGroup.groupName)) { 29 if (args.contains(buildGroup.groupName)) {
(...skipping 15 matching lines...) Expand all
45 <BuildUri, List<BuildResult>>{}; 45 <BuildUri, List<BuildResult>>{};
46 for (BuildUri buildUri in buildUriList) { 46 for (BuildUri buildUri in buildUriList) {
47 List<BuildResult> results = 47 List<BuildResult> results =
48 await readBuildResults(client, buildUri, runCount); 48 await readBuildResults(client, buildUri, runCount);
49 buildResults[buildUri] = results; 49 buildResults[buildUri] = results;
50 } 50 }
51 return buildResults; 51 return buildResults;
52 } 52 }
53 53
54 /// Prints summaries for the [buildResults]. 54 /// Prints summaries for the [buildResults].
55 // TODO(johnniwinther): Improve printing of multiple [BuildUri] results. 55 void printBuildResultsSummary(
56 void printBuildResultsSummary(Map<BuildUri, List<BuildResult>> buildResults) { 56 Map<BuildUri, List<BuildResult>> buildResults, List<String> args) {
57 List<Summary> emptySummaries = <Summary>[];
58 List<Summary> nonEmptySummaries = <Summary>[];
57 buildResults.forEach((BuildUri buildUri, List<BuildResult> results) { 59 buildResults.forEach((BuildUri buildUri, List<BuildResult> results) {
58 print(generateBuildResultsSummary(buildUri, results)); 60 Summary summary = new Summary(buildUri, results);
61 if (summary.isEmpty) {
62 emptySummaries.add(summary);
63 } else {
64 nonEmptySummaries.add(summary);
65 }
59 }); 66 });
67 StringBuffer sb = new StringBuffer();
68 if (nonEmptySummaries.isEmpty) {
69 if (emptySummaries.isNotEmpty) {
70 if (LOG || emptySummaries.length < 3) {
71 if (emptySummaries.length == 1) {
72 sb.writeln('No errors found for build bot:');
73 sb.write(emptySummaries.single.buildUri);
74 } else {
75 sb.writeln('No errors found for any of these build bots:');
76 for (Summary summary in emptySummaries) {
77 sb.writeln('${summary.buildUri}');
78 }
79 }
80 } else {
81 sb.write('No errors found for any of the '
82 '${emptySummaries.length} bots.');
83 }
84 } else {
85 sb.write('No build bot results found for args: ${args}');
86 }
87 } else {
88 for (Summary summary in nonEmptySummaries) {
89 summary.printOn(sb);
90 }
91 if (emptySummaries.isNotEmpty) {
92 if (LOG || emptySummaries.length < 3) {
93 sb.writeln('No errors found for the remaining build bots:');
94 for (Summary summary in emptySummaries) {
95 sb.writeln('${summary.buildUri}');
96 }
97 } else {
98 sb.write(
99 'No errors found for the ${emptySummaries.length} remaining bots.');
100 }
101 }
102 }
103 print(sb);
60 } 104 }
61 105
62 /// Creates a [BuildResult] for [buildUri] and, if it contains failures, the 106 /// Creates a [BuildResult] for [buildUri] and, if it contains failures, the
63 /// [BuildResult]s for the previous [runCount] builds. 107 /// [BuildResult]s for the previous [runCount] builds.
64 Future<List<BuildResult>> readBuildResults( 108 Future<List<BuildResult>> readBuildResults(
65 BuildbotClient client, BuildUri buildUri, int runCount) async { 109 BuildbotClient client, BuildUri buildUri, int runCount) async {
66 List<BuildResult> summaries = <BuildResult>[]; 110 List<BuildResult> summaries = <BuildResult>[];
67 BuildResult summary = await client.readResult(buildUri); 111 BuildResult summary = await client.readResult(buildUri);
68 summaries.add(summary); 112 summaries.add(summary);
69 if (summary.hasFailures) { 113 if (summary.hasFailures) {
70 for (int i = 0; i < runCount; i++) { 114 for (int i = 0; i < runCount; i++) {
71 buildUri = summary.buildUri.prev(); 115 buildUri = summary.buildUri.prev();
72 summary = await client.readResult(buildUri); 116 summary = await client.readResult(buildUri);
73 summaries.add(summary); 117 summaries.add(summary);
74 } 118 }
75 } 119 }
76 return summaries; 120 return summaries;
77 } 121 }
78 122
79 /// Generate a summary of the timeouts and other failures in [results]. 123 class Summary {
80 String generateBuildResultsSummary( 124 final BuildUri buildUri;
81 BuildUri buildUri, List<BuildResult> results) { 125 final List<BuildResult> results;
82 StringBuffer sb = new StringBuffer(); 126 final Set<TestConfiguration> timeoutIds = new Set<TestConfiguration>();
83 sb.write('Results for $buildUri:\n'); 127 final Set<TestConfiguration> errorIds = new Set<TestConfiguration>();
84 Set<TestConfiguration> timeoutIds = new Set<TestConfiguration>(); 128
85 for (BuildResult result in results) { 129 Summary(this.buildUri, this.results) {
86 timeoutIds.addAll(result.timeouts.map((TestFailure failure) => failure.id)); 130 for (BuildResult result in results) {
131 timeoutIds
132 .addAll(result.timeouts.map((TestFailure failure) => failure.id));
133 errorIds.addAll(result.errors.map((TestFailure failure) => failure.id));
134 }
87 } 135 }
88 if (timeoutIds.isNotEmpty) { 136
89 Map<TestConfiguration, Map<int, Map<String, Timing>>> map = 137 bool get isEmpty => timeoutIds.isEmpty && errorIds.isEmpty;
90 <TestConfiguration, Map<int, Map<String, Timing>>>{}; 138
91 Set<String> stepNames = new Set<String>(); 139 /// Generate a summary of the timeouts and other failures in [results].
92 for (BuildResult result in results) { 140 void printOn(StringBuffer sb) {
93 for (Timing timing in result.timings) { 141 if (timeoutIds.isNotEmpty) {
94 Map<int, Map<String, Timing>> builds = 142 Map<TestConfiguration, Map<int, Map<String, Timing>>> map =
95 map.putIfAbsent(timing.step.id, () => <int, Map<String, Timing>>{}); 143 <TestConfiguration, Map<int, Map<String, Timing>>>{};
96 stepNames.add(timing.step.stepName); 144 Set<String> stepNames = new Set<String>();
97 builds.putIfAbsent(timing.uri.buildNumber, () => <String, Timing>{})[ 145 for (BuildResult result in results) {
98 timing.step.stepName] = timing; 146 for (Timing timing in result.timings) {
147 Map<int, Map<String, Timing>> builds = map.putIfAbsent(
148 timing.step.id, () => <int, Map<String, Timing>>{});
149 stepNames.add(timing.step.stepName);
150 builds.putIfAbsent(timing.uri.buildNumber, () => <String, Timing>{})[
151 timing.step.stepName] = timing;
152 }
99 } 153 }
100 } 154 sb.write('Timeouts for ${buildUri} :\n');
101 sb.write('Timeouts for ${buildUri} :\n'); 155 map.forEach(
102 map.forEach((TestConfiguration id, Map<int, Map<String, Timing>> timings) { 156 (TestConfiguration id, Map<int, Map<String, Timing>> timings) {
103 if (!timeoutIds.contains(id)) return; 157 if (!timeoutIds.contains(id)) return;
104 sb.write('$id\n'); 158 sb.write('$id\n');
105 sb.write( 159 sb.write(
106 '${' ' * 8} ${stepNames.map((t) => padRight(t, 14)).join(' ')}\n'); 160 '${' ' * 8} ${stepNames.map((t) => padRight(t, 14)).join(' ')}\n');
107 for (BuildResult result in results) { 161 for (BuildResult result in results) {
108 int buildNumber = result.buildUri.buildNumber; 162 int buildNumber = result.buildUri.buildNumber;
109 Map<String, Timing> steps = timings[buildNumber] ?? const {}; 163 Map<String, Timing> steps = timings[buildNumber] ?? const {};
110 sb.write(padRight(' ${buildNumber}: ', 8)); 164 sb.write(padRight(' ${buildNumber}: ', 8));
111 for (String stepName in stepNames) { 165 for (String stepName in stepNames) {
112 Timing timing = steps[stepName]; 166 Timing timing = steps[stepName];
113 if (timing != null) { 167 if (timing != null) {
114 sb.write(' ${timing.time}'); 168 sb.write(' ${timing.time}');
115 } else { 169 } else {
116 sb.write(' --------------'); 170 sb.write(' --------------');
171 }
117 } 172 }
173 sb.write('\n');
118 } 174 }
119 sb.write('\n'); 175 sb.write('\n');
176 });
177 }
178 if (errorIds.isNotEmpty) {
179 Map<TestConfiguration, Map<int, TestFailure>> map =
180 <TestConfiguration, Map<int, TestFailure>>{};
181 for (BuildResult result in results) {
182 for (TestFailure failure in result.errors) {
183 map.putIfAbsent(failure.id, () => <int, TestFailure>{})[
184 failure.uri.buildNumber] = failure;
185 }
120 } 186 }
121 sb.write('\n'); 187 sb.write('Errors for ${buildUri} :\n');
122 }); 188 // TODO(johnniwinther): Improve comparison of non-timeouts.
123 } 189 map.forEach((TestConfiguration id, Map<int, TestFailure> failures) {
124 Set<TestConfiguration> errorIds = new Set<TestConfiguration>(); 190 if (!errorIds.contains(id)) return;
125 for (BuildResult result in results) { 191 sb.write('$id\n');
126 errorIds.addAll(result.errors.map((TestFailure failure) => failure.id)); 192 for (BuildResult result in results) {
127 } 193 int buildNumber = result.buildUri.buildNumber;
128 if (errorIds.isNotEmpty) { 194 TestFailure failure = failures[buildNumber];
129 Map<TestConfiguration, Map<int, TestFailure>> map = 195 sb.write(padRight(' ${buildNumber}: ', 8));
130 <TestConfiguration, Map<int, TestFailure>>{}; 196 if (failure != null) {
131 for (BuildResult result in results) { 197 sb.write(padRight(failure.expected, 10));
132 for (TestFailure failure in result.errors) { 198 sb.write(' / ');
133 map.putIfAbsent(failure.id, () => <int, TestFailure>{})[ 199 sb.write(padRight(failure.actual, 10));
134 failure.uri.buildNumber] = failure; 200 } else {
135 } 201 sb.write(' ' * 10);
136 } 202 sb.write(' / ');
137 sb.write('Errors for ${buildUri} :\n'); 203 sb.write(padRight('-- OK --', 10));
138 // TODO(johnniwinther): Improve comparison of non-timeouts. 204 }
139 map.forEach((TestConfiguration id, Map<int, TestFailure> failures) { 205 sb.write('\n');
140 if (!errorIds.contains(id)) return;
141 sb.write('$id\n');
142 for (BuildResult result in results) {
143 int buildNumber = result.buildUri.buildNumber;
144 TestFailure failure = failures[buildNumber];
145 sb.write(padRight(' ${buildNumber}: ', 8));
146 if (failure != null) {
147 sb.write(padRight(failure.expected, 10));
148 sb.write(' / ');
149 sb.write(padRight(failure.actual, 10));
150 } else {
151 sb.write(' ' * 10);
152 sb.write(' / ');
153 sb.write(padRight('-- OK --', 10));
154 } 206 }
155 sb.write('\n'); 207 sb.write('\n');
156 } 208 });
157 sb.write('\n'); 209 }
158 }); 210 if (timeoutIds.isEmpty && errorIds.isEmpty) {
211 sb.write('No errors found for ${buildUri}');
212 }
159 } 213 }
160 if (timeoutIds.isEmpty && errorIds.isEmpty) {
161 sb.write('No errors found.');
162 }
163 return sb.toString();
164 } 214 }
OLDNEW
« no previous file with comments | « no previous file | tools/gardening/test/compare_failures_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698