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

Side by Side Diff: tools/gardening/bin/compare_failures.dart

Issue 2797253006: Add find_timeouts and caching to tools/gardening (Closed)
Patch Set: Updated cf. comments Created 3 years, 8 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 | « tools/gardening/README.md ('k') | tools/gardening/bin/create_shard_groups.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 import 'dart:io'; 10 import 'dart:io';
11 11
12 import 'package:args/args.dart';
12 import 'package:gardening/src/buildbot_structures.dart'; 13 import 'package:gardening/src/buildbot_structures.dart';
14 import 'package:gardening/src/buildbot_loading.dart';
13 import 'package:gardening/src/util.dart'; 15 import 'package:gardening/src/util.dart';
14 16
15 main(List<String> args) async { 17 main(List<String> args) async {
16 if (args.length != 1) { 18 ArgParser argParser = createArgParser();
17 print('Usage: compare_failures <log-uri>'); 19 ArgResults argResults = argParser.parse(args);
20 processArgResults(argResults);
21 if (argResults.rest.length != 1) {
22 print('Usage: compare_failures [options] <log-uri>');
23 print('where <log-uri> is the uri the stdio output of a failing test step');
24 print('and options are:');
25 print(argParser.usage);
18 exit(1); 26 exit(1);
19 } 27 }
20 String url = args.first; 28 String url = argResults.rest.first;
21 if (!url.endsWith('/text')) { 29 if (!url.endsWith('/text')) {
22 // Use the text version of the stdio log. 30 // Use the text version of the stdio log.
23 url += '/text'; 31 url += '/text';
24 } 32 }
25 Uri uri = Uri.parse(url); 33 Uri uri = Uri.parse(url);
26 HttpClient client = new HttpClient(); 34 HttpClient client = new HttpClient();
27 BuildUri buildUri = new BuildUri(uri); 35 BuildUri buildUri = new BuildUri(uri);
28 List<BuildResult> results = await readBuildResults(client, buildUri); 36 List<BuildResult> results = await readBuildResults(client, buildUri);
29 print(generateBuildResultsSummary(buildUri, results)); 37 print(generateBuildResultsSummary(buildUri, results));
30 client.close(); 38 client.close();
31 } 39 }
32 40
33 /// Creates a [BuildResult] for [buildUri] and, if it contains failures, the 41 /// Creates a [BuildResult] for [buildUri] and, if it contains failures, the
34 /// [BuildResult]s for the previous 5 builds. 42 /// [BuildResult]s for the previous 5 builds.
35 Future<List<BuildResult>> readBuildResults( 43 Future<List<BuildResult>> readBuildResults(
36 HttpClient client, BuildUri buildUri) async { 44 HttpClient client, BuildUri buildUri) async {
37 List<BuildResult> summaries = <BuildResult>[]; 45 List<BuildResult> summaries = <BuildResult>[];
38 BuildResult firstSummary = await readBuildResult(client, buildUri); 46 BuildResult firstSummary = await readBuildResult(client, buildUri);
39 summaries.add(firstSummary); 47 summaries.add(firstSummary);
40 if (firstSummary.hasFailures) { 48 if (firstSummary.hasFailures) {
41 for (int i = 0; i < 10; i++) { 49 for (int i = 0; i < 10; i++) {
42 buildUri = buildUri.prev(); 50 buildUri = buildUri.prev();
43 summaries.add(await readBuildResult(client, buildUri)); 51 summaries.add(await readBuildResult(client, buildUri));
44 } 52 }
45 } 53 }
46 return summaries; 54 return summaries;
47 } 55 }
48 56
49 /// Parses the [buildUri] test log and creates a [BuildResult] for it.
50 Future<BuildResult> readBuildResult(
51 HttpClient client, BuildUri buildUri) async {
52 Uri uri = buildUri.toUri();
53 log('Reading $uri');
54 String text = await readUriAsText(client, uri);
55
56 bool inFailure = false;
57 List<String> currentFailure;
58 bool parsingTimingBlock = false;
59
60 List<TestFailure> failures = <TestFailure>[];
61 List<Timing> timings = <Timing>[];
62 for (String line in text.split('\n')) {
63 if (currentFailure != null) {
64 if (line.startsWith('!@@@STEP_CLEAR@@@')) {
65 failures.add(new TestFailure(buildUri, currentFailure));
66 currentFailure = null;
67 } else {
68 currentFailure.add(line);
69 }
70 } else if (inFailure && line.startsWith('@@@STEP_FAILURE@@@')) {
71 inFailure = false;
72 } else if (line.startsWith('!@@@STEP_FAILURE@@@')) {
73 inFailure = true;
74 } else if (line.startsWith('FAILED:')) {
75 currentFailure = <String>[];
76 currentFailure.add(line);
77 }
78 if (line.startsWith('--- Total time:')) {
79 parsingTimingBlock = true;
80 } else if (parsingTimingBlock) {
81 if (line.startsWith('0:')) {
82 timings.addAll(parseTimings(buildUri, line));
83 } else {
84 parsingTimingBlock = false;
85 }
86 }
87 }
88 return new BuildResult(buildUri, failures, timings);
89 }
90
91 /// Generate a summary of the timeouts and other failures in [results]. 57 /// Generate a summary of the timeouts and other failures in [results].
92 String generateBuildResultsSummary( 58 String generateBuildResultsSummary(
93 BuildUri buildUri, List<BuildResult> results) { 59 BuildUri buildUri, List<BuildResult> results) {
94 StringBuffer sb = new StringBuffer(); 60 StringBuffer sb = new StringBuffer();
95 sb.write('Results for $buildUri:\n'); 61 sb.write('Results for $buildUri:\n');
96 Set<TestConfiguration> timeoutIds = new Set<TestConfiguration>(); 62 Set<TestConfiguration> timeoutIds = new Set<TestConfiguration>();
97 for (BuildResult result in results) { 63 for (BuildResult result in results) {
98 timeoutIds.addAll(result.timeouts.map((TestFailure failure) => failure.id)); 64 timeoutIds.addAll(result.timeouts.map((TestFailure failure) => failure.id));
99 } 65 }
100 if (timeoutIds.isNotEmpty) { 66 if (timeoutIds.isNotEmpty) {
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 sb.write(' / '); 136 sb.write(' / ');
171 sb.write(padRight('-- OK --', 10)); 137 sb.write(padRight('-- OK --', 10));
172 } 138 }
173 sb.write('\n'); 139 sb.write('\n');
174 } 140 }
175 sb.write('\n'); 141 sb.write('\n');
176 }); 142 });
177 } 143 }
178 return sb.toString(); 144 return sb.toString();
179 } 145 }
180
181 /// The results of a build step.
182 class BuildResult {
183 final BuildUri buildUri;
184 final List<TestFailure> _failures;
185 final List<Timing> _timings;
186
187 BuildResult(this.buildUri, this._failures, this._timings);
188
189 /// `true` of the build result has test failures.
190 bool get hasFailures => _failures.isNotEmpty;
191
192 /// Returns the top-20 timings found in the build log.
193 Iterable<Timing> get timings => _timings;
194
195 /// Returns the [TestFailure]s for tests that timed out.
196 Iterable<TestFailure> get timeouts {
197 return _failures
198 .where((TestFailure failure) => failure.actual == 'Timeout');
199 }
200
201 /// Returns the [TestFailure]s for failing tests that did not time out.
202 Iterable<TestFailure> get errors {
203 return _failures
204 .where((TestFailure failure) => failure.actual != 'Timeout');
205 }
206
207 String toString() {
208 StringBuffer sb = new StringBuffer();
209 sb.write('$buildUri\n');
210 sb.write('Failures:\n${_failures.join('\n-----\n')}\n');
211 sb.write('\nTimings:\n${_timings.join('\n')}');
212 return sb.toString();
213 }
214 }
215
216 /// Test failure data derived from the test failure summary in the build step
217 /// stdio log.
218 class TestFailure {
219 final BuildUri uri;
220 final TestConfiguration id;
221 final String expected;
222 final String actual;
223 final String text;
224
225 factory TestFailure(BuildUri uri, List<String> lines) {
226 List<String> parts = split(lines.first, ['FAILED: ', ' ', ' ']);
227 String configName = parts[1];
228 String archName = parts[2];
229 String testName = parts[3];
230 TestConfiguration id =
231 new TestConfiguration(configName, archName, testName);
232 String expected = split(lines[1], ['Expected: '])[1];
233 String actual = split(lines[2], ['Actual: '])[1];
234 return new TestFailure.internal(
235 uri, id, expected, actual, lines.skip(3).join('\n'));
236 }
237
238 TestFailure.internal(
239 this.uri, this.id, this.expected, this.actual, this.text);
240
241 String toString() {
242 StringBuffer sb = new StringBuffer();
243 sb.write('FAILED: $id\n');
244 sb.write('Expected: $expected\n');
245 sb.write('Actual: $actual\n');
246 sb.write(text);
247 return sb.toString();
248 }
249 }
250
251 /// Id for a single test step, for instance the compilation and run steps of
252 /// a test.
253 class TestStep {
254 final String stepName;
255 final TestConfiguration id;
256
257 TestStep(this.stepName, this.id);
258
259 String toString() {
260 return '$stepName - $id';
261 }
262
263 int get hashCode => stepName.hashCode * 13 + id.hashCode * 17;
264
265 bool operator ==(other) {
266 if (identical(this, other)) return true;
267 if (other is! TestStep) return false;
268 return stepName == other.stepName && id == other.id;
269 }
270 }
271
272 /// The timing result for a single test step.
273 class Timing {
274 final BuildUri uri;
275 final String time;
276 final TestStep step;
277
278 Timing(this.uri, this.time, this.step);
279
280 String toString() {
281 return '$time - $step';
282 }
283 }
284
285 /// Create the [Timing]s for the [line] as found in the top-20 timings of a
286 /// build step stdio log.
287 List<Timing> parseTimings(BuildUri uri, String line) {
288 List<String> parts = split(line, [' - ', ' - ', ' ']);
289 String time = parts[0];
290 String stepName = parts[1];
291 String configName = parts[2];
292 String testNames = parts[3];
293 List<Timing> timings = <Timing>[];
294 for (String name in testNames.split(',')) {
295 name = name.trim();
296 int slashPos = name.indexOf('/');
297 String archName = name.substring(0, slashPos);
298 String testName = name.substring(slashPos + 1);
299 timings.add(new Timing(
300 uri,
301 time,
302 new TestStep(
303 stepName, new TestConfiguration(configName, archName, testName))));
304 }
305 return timings;
306 }
OLDNEW
« no previous file with comments | « tools/gardening/README.md ('k') | tools/gardening/bin/create_shard_groups.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698