Simplify Test summary creation
- return a TestSummary rather than a Builder
- only call build() once
- remove some dead code
- avoid creating some unnecessary intermediate lists
This code is performance critical in that the thread executing it is
holding a lock on AggregatingTestListener. On builds which have a lot of
executing tests (e.g., high values of --runs_per_test) this can lead to
significant lock contention.
It might be better to refactor this code to just store the received test
attempt results and only aggregate when all attempts for a specific test
are done, rather than trying to do incremental work. That should make
the code simpler, although it might be a bit slower, but allows multiple
test reports to process in parallel.
PiperOrigin-RevId: 241903073
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/TestResultAnalyzer.java b/src/main/java/com/google/devtools/build/lib/runtime/TestResultAnalyzer.java
index 871f2dd..88c40b8 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/TestResultAnalyzer.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/TestResultAnalyzer.java
@@ -100,7 +100,7 @@
int passCount = 0;
for (ConfiguredTarget testTarget : testTargets) {
- TestSummary summary = aggregateAndReportSummary(testTarget, listener).build();
+ TestSummary summary = aggregateAndReportSummary(testTarget, listener);
summaries.add(summary);
// Finished aggregating; build the final console output.
@@ -135,12 +135,11 @@
}
/**
- * Helper for differential analysis which aggregates the TestSummary
- * for an individual target, reporting runs on the EventBus if necessary.
+ * Helper for differential analysis which aggregates the TestSummary for an individual target,
+ * reporting runs on the EventBus if necessary.
*/
- private TestSummary.Builder aggregateAndReportSummary(
- ConfiguredTarget testTarget,
- AggregatingTestListener listener) {
+ private TestSummary aggregateAndReportSummary(
+ ConfiguredTarget testTarget, AggregatingTestListener listener) {
// If already reported by the listener, no work remains for this target.
TestSummary.Builder summary = listener.getCurrentSummary(testTarget);
@@ -148,7 +147,7 @@
Preconditions.checkNotNull(summary,
"%s did not complete test filtering, but has a test result", testLabel);
if (listener.targetReported(testTarget)) {
- return summary;
+ return summary.build();
}
Collection<Artifact> incompleteRuns = listener.getIncompleteRuns(testTarget);
@@ -182,8 +181,9 @@
}
// The target was not posted by the listener and must be posted now.
- eventBus.post(summary.build());
- return summary;
+ TestSummary result = summary.build();
+ eventBus.post(result);
+ return result;
}
/**
@@ -250,19 +250,16 @@
}
}
- List<Path> passed = new ArrayList<>();
if (result.getData().hasPassedLog()) {
- passed.add(result.getTestLogPath().getRelative(result.getData().getPassedLog()));
+ summaryBuilder.addPassedLog(
+ result.getTestLogPath().getRelative(result.getData().getPassedLog()));
}
- List<Path> failed = new ArrayList<>();
for (String path : result.getData().getFailedLogsList()) {
- failed.add(result.getTestLogPath().getRelative(path));
+ summaryBuilder.addFailedLog(result.getTestLogPath().getRelative(path));
}
summaryBuilder
.addTestTimes(result.getData().getTestTimesList())
- .addPassedLogs(passed)
- .addFailedLogs(failed)
.addWarnings(result.getData().getWarningList())
.collectFailedTests(result.getData().getTestCase())
.countTotalTestCases(result.getData().getTestCase())
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/TestSummary.java b/src/main/java/com/google/devtools/build/lib/runtime/TestSummary.java
index 486c833..a5cd20d 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/TestSummary.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/TestSummary.java
@@ -139,12 +139,24 @@
return this;
}
+ public Builder addPassedLog(Path passedLog) {
+ checkMutation(passedLog);
+ summary.passedLogs.add(passedLog);
+ return this;
+ }
+
public Builder addFailedLogs(List<Path> failedLogs) {
checkMutation(failedLogs);
summary.failedLogs.addAll(failedLogs);
return this;
}
+ public Builder addFailedLog(Path failedLog) {
+ checkMutation(failedLog);
+ summary.failedLogs.add(failedLog);
+ return this;
+ }
+
public Builder addTotalTestCases(int totalTestCases) {
checkMutation(totalTestCases);
summary.totalTestCases += totalTestCases;
@@ -356,15 +368,6 @@
return new Builder();
}
- /**
- * Creates a new Builder initialized with a copy of the existing object's values.
- */
- public static Builder newBuilderFromExisting(TestSummary existing) {
- Builder builder = new Builder();
- builder.mergeFrom(existing);
- return builder;
- }
-
public Label getLabel() {
return AliasProvider.getDependencyLabel(target);
}
diff --git a/src/test/java/com/google/devtools/build/lib/runtime/TestSummaryTest.java b/src/test/java/com/google/devtools/build/lib/runtime/TestSummaryTest.java
index 3a885d2..8bf021d 100644
--- a/src/test/java/com/google/devtools/build/lib/runtime/TestSummaryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/runtime/TestSummaryTest.java
@@ -222,13 +222,6 @@
TestSummary sixtyCached = basicBuilder.setNumCached(60).build();
assertThat(sixtyCached.numCached()).isEqualTo(60);
assertThat(fiftyCached.numCached()).isEqualTo(50);
-
- TestSummary failedCacheTemplate = TestSummary.newBuilderFromExisting(fiftyCached)
- .setStatus(BlazeTestStatus.FAILED)
- .build();
- assertThat(failedCacheTemplate.numCached()).isEqualTo(50);
- assertThat(failedCacheTemplate.getStatus()).isEqualTo(BlazeTestStatus.FAILED);
- assertThat(failedCacheTemplate.getTotalTestCases()).isEqualTo(fiftyCached.getTotalTestCases());
}
@Test