Prevent NPE on missing TEST_LOG due to cancel Tests added that exhibit this failure reported in #11468 without the correction. Closes #11494. PiperOrigin-RevId: 314687670
diff --git a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java index 4881a46..75d7e41 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java +++ b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
@@ -14,6 +14,7 @@ package com.google.devtools.build.lib.exec; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -244,11 +245,17 @@ Path renamedTestLog = null; for (Pair<String, Path> pair : testOutputs) { if (TestFileNameConstants.TEST_LOG.equals(pair.getFirst())) { + Preconditions.checkState(renamedTestLog == null, "multiple test_log matches"); renamedTestLog = pair.getSecond(); } } TestResultData.Builder dataBuilder = result.testResultDataBuilder(); + // If the test log path does not exist, mark the test as incomplete + if (renamedTestLog == null) { + dataBuilder.setStatus(BlazeTestStatus.INCOMPLETE); + } + if (dataBuilder.getStatus() == BlazeTestStatus.PASSED) { dataBuilder.setPassedLog(renamedTestLog.toString()); } else if (dataBuilder.getStatus() == BlazeTestStatus.INCOMPLETE) { @@ -400,12 +407,17 @@ return new TestResult(action, data, /*cached*/ true, execRoot); } - private static final class StandaloneFailedAttemptResult implements FailedAttemptResult { + @VisibleForTesting + static final class StandaloneFailedAttemptResult implements FailedAttemptResult { private final TestResultData testResultData; StandaloneFailedAttemptResult(TestResultData testResultData) { this.testResultData = testResultData; } + + TestResultData testResultData() { + return testResultData; + } } private final class StandaloneTestRunnerSpawn implements TestRunnerSpawn {
diff --git a/src/test/java/com/google/devtools/build/lib/exec/BUILD b/src/test/java/com/google/devtools/build/lib/exec/BUILD index 1d9f628..89cef69 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/BUILD +++ b/src/test/java/com/google/devtools/build/lib/exec/BUILD
@@ -45,6 +45,7 @@ "//src/main/java/com/google/devtools/build/lib/exec:spawn_log_context", "//src/main/java/com/google/devtools/build/lib/exec:spawn_runner", "//src/main/java/com/google/devtools/build/lib/exec:spawn_strategy_registry", + "//src/main/java/com/google/devtools/build/lib/exec:standalone_test_result", "//src/main/java/com/google/devtools/build/lib/exec:standalone_test_strategy", "//src/main/java/com/google/devtools/build/lib/exec:streamed_test_output", "//src/main/java/com/google/devtools/build/lib/exec:symlink_tree_helper",
diff --git a/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java b/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java index 028d5cf..b8cb751 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java +++ b/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java
@@ -40,12 +40,15 @@ import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.test.TestActionContext; +import com.google.devtools.build.lib.analysis.test.TestActionContext.FailedAttemptResult; +import com.google.devtools.build.lib.analysis.test.TestActionContext.TestRunnerSpawn; import com.google.devtools.build.lib.analysis.test.TestAttempt; import com.google.devtools.build.lib.analysis.test.TestProvider; import com.google.devtools.build.lib.analysis.test.TestResult; import com.google.devtools.build.lib.analysis.test.TestRunnerAction; import com.google.devtools.build.lib.analysis.test.TestStrategy; import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TestResult.ExecutionInfo; import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TestStatus; import com.google.devtools.build.lib.clock.BlazeClock; import com.google.devtools.build.lib.clock.Clock; @@ -54,6 +57,7 @@ import com.google.devtools.build.lib.events.EventKind; import com.google.devtools.build.lib.events.ExtendedEventHandler; import com.google.devtools.build.lib.events.StoredEventHandler; +import com.google.devtools.build.lib.exec.StandaloneTestStrategy.StandaloneFailedAttemptResult; import com.google.devtools.build.lib.exec.util.TestExecutorBuilder; import com.google.devtools.build.lib.util.AbruptExitException; import com.google.devtools.build.lib.util.OS; @@ -62,6 +66,7 @@ import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.view.test.TestStatus.BlazeTestStatus; +import com.google.devtools.build.lib.view.test.TestStatus.TestResultData; import com.google.devtools.common.options.Options; import java.io.IOException; import java.io.OutputStream; @@ -1022,4 +1027,42 @@ storedEvents.getEvents(), Event.of(EventKind.FAIL, null, "//standalone:empty_test (run 2 of 2)")); } + + @Test + public void missingTestLogSpawnTestResultIsIncomplete() throws Exception { + ExecutionOptions executionOptions = ExecutionOptions.DEFAULTS; + Path tmpDirRoot = TestStrategy.getTmpRoot(rootDirectory, outputBase, executionOptions); + BinTools binTools = BinTools.forUnitTesting(directories, analysisMock.getEmbeddedTools()); + TestedStandaloneTestStrategy standaloneTestStrategy = + new TestedStandaloneTestStrategy(executionOptions, binTools, tmpDirRoot); + ActionExecutionContext actionExecutionContext = + new FakeActionExecutionContext(createTempOutErr(tmpDirRoot), spawnStrategy, binTools); + + // setup a test action + scratch.file("standalone/simple_test.sh", "this does not get executed, it is mocked out"); + scratch.file( + "standalone/BUILD", + "sh_test(", + " name = \"simple_test\",", + " size = \"small\",", + " srcs = [\"simple_test.sh\"],", + ")"); + TestRunnerAction testRunnerAction = getTestAction("//standalone:simple_test"); + TestRunnerSpawn spawn = + standaloneTestStrategy.createTestRunnerSpawn(testRunnerAction, actionExecutionContext); + + TestResultData.Builder builder = + TestResultData.newBuilder().setTestPassed(true).setStatus(BlazeTestStatus.PASSED); + StandaloneTestResult result = + StandaloneTestResult.builder() + .setSpawnResults(ImmutableList.of()) + .setTestResultDataBuilder(builder) + .setExecutionInfo(ExecutionInfo.getDefaultInstance()) + .build(); + FailedAttemptResult failedResult = spawn.finalizeFailedTestAttempt(result, 0); + + assertThat(failedResult).isInstanceOf(StandaloneFailedAttemptResult.class); + TestResultData data = ((StandaloneFailedAttemptResult) failedResult).testResultData(); + assertThat(data.getStatus()).isEqualTo(BlazeTestStatus.INCOMPLETE); + } }