| OLD | NEW |
| 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'; | |
| 11 | 10 |
| 12 import 'package:args/args.dart'; | |
| 13 import 'package:gardening/src/buildbot_structures.dart'; | 11 import 'package:gardening/src/buildbot_structures.dart'; |
| 12 import 'package:gardening/src/buildbot_data.dart'; |
| 14 import 'package:gardening/src/client.dart'; | 13 import 'package:gardening/src/client.dart'; |
| 15 import 'package:gardening/src/util.dart'; | 14 import 'package:gardening/src/util.dart'; |
| 16 | 15 |
| 17 void help(ArgParser argParser) { | 16 Future mainInternal(BuildbotClient client, List<String> args, |
| 18 print('Given a <log-uri> finds all failing tests in that stdout. Then '); | 17 {int runCount: 10}) async { |
| 19 print('fetches earlier runs of the same bot and compares the results.'); | 18 printBuildResultsSummary( |
| 20 print('This tool is particularly useful to detect flakes and their '); | 19 await loadBuildResults(client, args, runCount: runCount)); |
| 21 print('frequency.'); | |
| 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); | |
| 26 } | 20 } |
| 27 | 21 |
| 28 main(List<String> args) async { | 22 /// Loads [BuildResult]s for the [runCount] last builds for the build(s) in |
| 29 ArgParser argParser = createArgParser(); | 23 /// [args]. [args] can be a list of [BuildGroup] names or a list of log uris. |
| 30 argParser.addOption("run-count", | 24 Future<Map<BuildUri, List<BuildResult>>> loadBuildResults( |
| 31 defaultsTo: "10", help: "How many previous runs should be fetched"); | 25 BuildbotClient client, List<String> args, |
| 32 ArgResults argResults = argParser.parse(args); | 26 {int runCount: 10}) async { |
| 33 processArgResults(argResults); | 27 List<BuildUri> buildUriList = <BuildUri>[]; |
| 28 for (BuildGroup buildGroup in buildGroups) { |
| 29 if (args.contains(buildGroup.groupName)) { |
| 30 buildUriList.addAll(buildGroup.createUris(client.mostRecentBuildNumber)); |
| 31 } |
| 32 } |
| 33 if (buildUriList.isEmpty) { |
| 34 for (String url in args) { |
| 35 if (!url.endsWith('/text')) { |
| 36 // Use the text version of the stdio log. |
| 37 url += '/text'; |
| 38 } |
| 39 Uri uri = Uri.parse(url); |
| 40 BuildUri buildUri = new BuildUri(uri); |
| 41 buildUriList.add(buildUri); |
| 42 } |
| 43 } |
| 44 Map<BuildUri, List<BuildResult>> buildResults = |
| 45 <BuildUri, List<BuildResult>>{}; |
| 46 for (BuildUri buildUri in buildUriList) { |
| 47 List<BuildResult> results = |
| 48 await readBuildResults(client, buildUri, runCount); |
| 49 buildResults[buildUri] = results; |
| 50 } |
| 51 return buildResults; |
| 52 } |
| 34 | 53 |
| 35 BuildbotClient client = argResults['logdog'] | 54 /// Prints summaries for the [buildResults]. |
| 36 ? new LogdogBuildbotClient() | 55 // TODO(johnniwinther): Improve printing of multiple [BuildUri] results. |
| 37 : new HttpBuildbotClient(); | 56 void printBuildResultsSummary(Map<BuildUri, List<BuildResult>> buildResults) { |
| 38 | 57 buildResults.forEach((BuildUri buildUri, List<BuildResult> results) { |
| 39 var runCount = int.parse(argResults['run-count'], onError: (_) => null); | 58 print(generateBuildResultsSummary(buildUri, results)); |
| 40 | 59 }); |
| 41 if (argResults.rest.length != 1 || argResults['help'] || runCount == null) { | |
| 42 help(argParser); | |
| 43 if (argResults['help']) return; | |
| 44 exit(1); | |
| 45 } | |
| 46 String url = argResults.rest.first; | |
| 47 if (!url.endsWith('/text')) { | |
| 48 // Use the text version of the stdio log. | |
| 49 url += '/text'; | |
| 50 } | |
| 51 Uri uri = Uri.parse(url); | |
| 52 BuildUri buildUri = new BuildUri(uri); | |
| 53 List<BuildResult> results = | |
| 54 await readBuildResults(client, buildUri, runCount); | |
| 55 print(generateBuildResultsSummary(buildUri, results)); | |
| 56 client.close(); | |
| 57 } | 60 } |
| 58 | 61 |
| 59 /// Creates a [BuildResult] for [buildUri] and, if it contains failures, the | 62 /// Creates a [BuildResult] for [buildUri] and, if it contains failures, the |
| 60 /// [BuildResult]s for the previous [runCount] builds. | 63 /// [BuildResult]s for the previous [runCount] builds. |
| 61 Future<List<BuildResult>> readBuildResults( | 64 Future<List<BuildResult>> readBuildResults( |
| 62 BuildbotClient client, BuildUri buildUri, int runCount) async { | 65 BuildbotClient client, BuildUri buildUri, int runCount) async { |
| 63 List<BuildResult> summaries = <BuildResult>[]; | 66 List<BuildResult> summaries = <BuildResult>[]; |
| 64 BuildResult summary = await client.readResult(buildUri); | 67 BuildResult summary = await client.readResult(buildUri); |
| 65 summaries.add(summary); | 68 summaries.add(summary); |
| 66 if (summary.hasFailures) { | 69 if (summary.hasFailures) { |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 } else { | 150 } else { |
| 148 sb.write(' ' * 10); | 151 sb.write(' ' * 10); |
| 149 sb.write(' / '); | 152 sb.write(' / '); |
| 150 sb.write(padRight('-- OK --', 10)); | 153 sb.write(padRight('-- OK --', 10)); |
| 151 } | 154 } |
| 152 sb.write('\n'); | 155 sb.write('\n'); |
| 153 } | 156 } |
| 154 sb.write('\n'); | 157 sb.write('\n'); |
| 155 }); | 158 }); |
| 156 } | 159 } |
| 160 if (timeoutIds.isEmpty && errorIds.isEmpty) { |
| 161 sb.write('No errors found.'); |
| 162 } |
| 157 return sb.toString(); | 163 return sb.toString(); |
| 158 } | 164 } |
| OLD | NEW |