| Index: tools/gardening/lib/src/compare_failures_impl.dart
|
| diff --git a/tools/gardening/lib/src/compare_failures_impl.dart b/tools/gardening/lib/src/compare_failures_impl.dart
|
| index d3d77198061be4d9de8b975fb5ff855a0099b324..8f6412c5696465ff53d1134fee354298cb1baf91 100644
|
| --- a/tools/gardening/lib/src/compare_failures_impl.dart
|
| +++ b/tools/gardening/lib/src/compare_failures_impl.dart
|
| @@ -16,7 +16,7 @@ import 'package:gardening/src/util.dart';
|
| Future mainInternal(BuildbotClient client, List<String> args,
|
| {int runCount: 10}) async {
|
| printBuildResultsSummary(
|
| - await loadBuildResults(client, args, runCount: runCount));
|
| + await loadBuildResults(client, args, runCount: runCount), args);
|
| }
|
|
|
| /// Loads [BuildResult]s for the [runCount] last builds for the build(s) in
|
| @@ -52,11 +52,55 @@ Future<Map<BuildUri, List<BuildResult>>> loadBuildResults(
|
| }
|
|
|
| /// Prints summaries for the [buildResults].
|
| -// TODO(johnniwinther): Improve printing of multiple [BuildUri] results.
|
| -void printBuildResultsSummary(Map<BuildUri, List<BuildResult>> buildResults) {
|
| +void printBuildResultsSummary(
|
| + Map<BuildUri, List<BuildResult>> buildResults, List<String> args) {
|
| + List<Summary> emptySummaries = <Summary>[];
|
| + List<Summary> nonEmptySummaries = <Summary>[];
|
| buildResults.forEach((BuildUri buildUri, List<BuildResult> results) {
|
| - print(generateBuildResultsSummary(buildUri, results));
|
| + Summary summary = new Summary(buildUri, results);
|
| + if (summary.isEmpty) {
|
| + emptySummaries.add(summary);
|
| + } else {
|
| + nonEmptySummaries.add(summary);
|
| + }
|
| });
|
| + StringBuffer sb = new StringBuffer();
|
| + if (nonEmptySummaries.isEmpty) {
|
| + if (emptySummaries.isNotEmpty) {
|
| + if (LOG || emptySummaries.length < 3) {
|
| + if (emptySummaries.length == 1) {
|
| + sb.writeln('No errors found for build bot:');
|
| + sb.write(emptySummaries.single.buildUri);
|
| + } else {
|
| + sb.writeln('No errors found for any of these build bots:');
|
| + for (Summary summary in emptySummaries) {
|
| + sb.writeln('${summary.buildUri}');
|
| + }
|
| + }
|
| + } else {
|
| + sb.write('No errors found for any of the '
|
| + '${emptySummaries.length} bots.');
|
| + }
|
| + } else {
|
| + sb.write('No build bot results found for args: ${args}');
|
| + }
|
| + } else {
|
| + for (Summary summary in nonEmptySummaries) {
|
| + summary.printOn(sb);
|
| + }
|
| + if (emptySummaries.isNotEmpty) {
|
| + if (LOG || emptySummaries.length < 3) {
|
| + sb.writeln('No errors found for the remaining build bots:');
|
| + for (Summary summary in emptySummaries) {
|
| + sb.writeln('${summary.buildUri}');
|
| + }
|
| + } else {
|
| + sb.write(
|
| + 'No errors found for the ${emptySummaries.length} remaining bots.');
|
| + }
|
| + }
|
| + }
|
| + print(sb);
|
| }
|
|
|
| /// Creates a [BuildResult] for [buildUri] and, if it contains failures, the
|
| @@ -76,89 +120,95 @@ Future<List<BuildResult>> readBuildResults(
|
| return summaries;
|
| }
|
|
|
| -/// Generate a summary of the timeouts and other failures in [results].
|
| -String generateBuildResultsSummary(
|
| - BuildUri buildUri, List<BuildResult> results) {
|
| - StringBuffer sb = new StringBuffer();
|
| - sb.write('Results for $buildUri:\n');
|
| - Set<TestConfiguration> timeoutIds = new Set<TestConfiguration>();
|
| - for (BuildResult result in results) {
|
| - timeoutIds.addAll(result.timeouts.map((TestFailure failure) => failure.id));
|
| - }
|
| - if (timeoutIds.isNotEmpty) {
|
| - Map<TestConfiguration, Map<int, Map<String, Timing>>> map =
|
| - <TestConfiguration, Map<int, Map<String, Timing>>>{};
|
| - Set<String> stepNames = new Set<String>();
|
| +class Summary {
|
| + final BuildUri buildUri;
|
| + final List<BuildResult> results;
|
| + final Set<TestConfiguration> timeoutIds = new Set<TestConfiguration>();
|
| + final Set<TestConfiguration> errorIds = new Set<TestConfiguration>();
|
| +
|
| + Summary(this.buildUri, this.results) {
|
| for (BuildResult result in results) {
|
| - for (Timing timing in result.timings) {
|
| - Map<int, Map<String, Timing>> builds =
|
| - map.putIfAbsent(timing.step.id, () => <int, Map<String, Timing>>{});
|
| - stepNames.add(timing.step.stepName);
|
| - builds.putIfAbsent(timing.uri.buildNumber, () => <String, Timing>{})[
|
| - timing.step.stepName] = timing;
|
| - }
|
| + timeoutIds
|
| + .addAll(result.timeouts.map((TestFailure failure) => failure.id));
|
| + errorIds.addAll(result.errors.map((TestFailure failure) => failure.id));
|
| }
|
| - sb.write('Timeouts for ${buildUri} :\n');
|
| - map.forEach((TestConfiguration id, Map<int, Map<String, Timing>> timings) {
|
| - if (!timeoutIds.contains(id)) return;
|
| - sb.write('$id\n');
|
| - sb.write(
|
| - '${' ' * 8} ${stepNames.map((t) => padRight(t, 14)).join(' ')}\n');
|
| + }
|
| +
|
| + bool get isEmpty => timeoutIds.isEmpty && errorIds.isEmpty;
|
| +
|
| + /// Generate a summary of the timeouts and other failures in [results].
|
| + void printOn(StringBuffer sb) {
|
| + if (timeoutIds.isNotEmpty) {
|
| + Map<TestConfiguration, Map<int, Map<String, Timing>>> map =
|
| + <TestConfiguration, Map<int, Map<String, Timing>>>{};
|
| + Set<String> stepNames = new Set<String>();
|
| for (BuildResult result in results) {
|
| - int buildNumber = result.buildUri.buildNumber;
|
| - Map<String, Timing> steps = timings[buildNumber] ?? const {};
|
| - sb.write(padRight(' ${buildNumber}: ', 8));
|
| - for (String stepName in stepNames) {
|
| - Timing timing = steps[stepName];
|
| - if (timing != null) {
|
| - sb.write(' ${timing.time}');
|
| - } else {
|
| - sb.write(' --------------');
|
| + for (Timing timing in result.timings) {
|
| + Map<int, Map<String, Timing>> builds = map.putIfAbsent(
|
| + timing.step.id, () => <int, Map<String, Timing>>{});
|
| + stepNames.add(timing.step.stepName);
|
| + builds.putIfAbsent(timing.uri.buildNumber, () => <String, Timing>{})[
|
| + timing.step.stepName] = timing;
|
| + }
|
| + }
|
| + sb.write('Timeouts for ${buildUri} :\n');
|
| + map.forEach(
|
| + (TestConfiguration id, Map<int, Map<String, Timing>> timings) {
|
| + if (!timeoutIds.contains(id)) return;
|
| + sb.write('$id\n');
|
| + sb.write(
|
| + '${' ' * 8} ${stepNames.map((t) => padRight(t, 14)).join(' ')}\n');
|
| + for (BuildResult result in results) {
|
| + int buildNumber = result.buildUri.buildNumber;
|
| + Map<String, Timing> steps = timings[buildNumber] ?? const {};
|
| + sb.write(padRight(' ${buildNumber}: ', 8));
|
| + for (String stepName in stepNames) {
|
| + Timing timing = steps[stepName];
|
| + if (timing != null) {
|
| + sb.write(' ${timing.time}');
|
| + } else {
|
| + sb.write(' --------------');
|
| + }
|
| }
|
| + sb.write('\n');
|
| }
|
| sb.write('\n');
|
| - }
|
| - sb.write('\n');
|
| - });
|
| - }
|
| - Set<TestConfiguration> errorIds = new Set<TestConfiguration>();
|
| - for (BuildResult result in results) {
|
| - errorIds.addAll(result.errors.map((TestFailure failure) => failure.id));
|
| - }
|
| - if (errorIds.isNotEmpty) {
|
| - Map<TestConfiguration, Map<int, TestFailure>> map =
|
| - <TestConfiguration, Map<int, TestFailure>>{};
|
| - for (BuildResult result in results) {
|
| - for (TestFailure failure in result.errors) {
|
| - map.putIfAbsent(failure.id, () => <int, TestFailure>{})[
|
| - failure.uri.buildNumber] = failure;
|
| - }
|
| + });
|
| }
|
| - sb.write('Errors for ${buildUri} :\n');
|
| - // TODO(johnniwinther): Improve comparison of non-timeouts.
|
| - map.forEach((TestConfiguration id, Map<int, TestFailure> failures) {
|
| - if (!errorIds.contains(id)) return;
|
| - sb.write('$id\n');
|
| + if (errorIds.isNotEmpty) {
|
| + Map<TestConfiguration, Map<int, TestFailure>> map =
|
| + <TestConfiguration, Map<int, TestFailure>>{};
|
| for (BuildResult result in results) {
|
| - int buildNumber = result.buildUri.buildNumber;
|
| - TestFailure failure = failures[buildNumber];
|
| - sb.write(padRight(' ${buildNumber}: ', 8));
|
| - if (failure != null) {
|
| - sb.write(padRight(failure.expected, 10));
|
| - sb.write(' / ');
|
| - sb.write(padRight(failure.actual, 10));
|
| - } else {
|
| - sb.write(' ' * 10);
|
| - sb.write(' / ');
|
| - sb.write(padRight('-- OK --', 10));
|
| + for (TestFailure failure in result.errors) {
|
| + map.putIfAbsent(failure.id, () => <int, TestFailure>{})[
|
| + failure.uri.buildNumber] = failure;
|
| }
|
| - sb.write('\n');
|
| }
|
| - sb.write('\n');
|
| - });
|
| - }
|
| - if (timeoutIds.isEmpty && errorIds.isEmpty) {
|
| - sb.write('No errors found.');
|
| + sb.write('Errors for ${buildUri} :\n');
|
| + // TODO(johnniwinther): Improve comparison of non-timeouts.
|
| + map.forEach((TestConfiguration id, Map<int, TestFailure> failures) {
|
| + if (!errorIds.contains(id)) return;
|
| + sb.write('$id\n');
|
| + for (BuildResult result in results) {
|
| + int buildNumber = result.buildUri.buildNumber;
|
| + TestFailure failure = failures[buildNumber];
|
| + sb.write(padRight(' ${buildNumber}: ', 8));
|
| + if (failure != null) {
|
| + sb.write(padRight(failure.expected, 10));
|
| + sb.write(' / ');
|
| + sb.write(padRight(failure.actual, 10));
|
| + } else {
|
| + sb.write(' ' * 10);
|
| + sb.write(' / ');
|
| + sb.write(padRight('-- OK --', 10));
|
| + }
|
| + sb.write('\n');
|
| + }
|
| + sb.write('\n');
|
| + });
|
| + }
|
| + if (timeoutIds.isEmpty && errorIds.isEmpty) {
|
| + sb.write('No errors found for ${buildUri}');
|
| + }
|
| }
|
| - return sb.toString();
|
| }
|
|
|