Persistent tests: fix NPE for most test rules
Only the java_test rule currently supports the persistent test runner, and
all other test rules throw NPE. Instead, automatically fall back to the
non-persistent test runner for rules that don't support it, avoiding the NPE.
This requires updating the infrastructure to *not* just check the test config,
but also whether an individual test *action* has the persistent runner enabled.
This allows enabling this feature by default, but having it only apply to rules
that support it.
Note that I am not sure if the persistent test runner works in Bazel since
there are no public tests for it.
Fixes #11519.
Change-Id: I321e7497267e77e668209a541600908b38a32464
Closes #11527.
Change-Id: I321e7497267e77e668209a541600908b38a32464
PiperOrigin-RevId: 319499062
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
index 9c4d5b1..7b35bb8 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestActionBuilder.java
@@ -158,6 +158,14 @@
return this;
}
+ private boolean isPersistentTestRunner() {
+ return ruleContext
+ .getConfiguration()
+ .getFragment(TestConfiguration.class)
+ .isPersistentTestRunner()
+ && persistentTestRunnerRunfiles != null;
+ }
+
/**
* Creates a test action and artifacts for the given rule. The test action will
* use the specified executable and runfiles.
@@ -188,8 +196,8 @@
PrerequisiteArtifacts.nestedSet(ruleContext, "$test_runtime", TransitionMode.HOST);
inputsBuilder.addTransitive(testRuntime);
}
- TestTargetProperties testProperties = new TestTargetProperties(
- ruleContext, executionRequirements);
+ TestTargetProperties testProperties =
+ new TestTargetProperties(ruleContext, executionRequirements, isPersistentTestRunner());
// If the test rule does not provide InstrumentedFilesProvider, there's not much that we can do.
final boolean collectCodeCoverage = config.isCodeCoverageEnabled()
@@ -301,7 +309,7 @@
} else {
Artifact flagFile = null;
// The worker spawn runner expects a flag file containg the worker's flags.
- if (testConfiguration.isPersistentTestRunner()) {
+ if (isPersistentTestRunner()) {
flagFile = ruleContext.getBinArtifact(ruleContext.getLabel().getName() + "_flag_file.txt");
inputsBuilder.add(flagFile);
}
@@ -366,7 +374,7 @@
testConfiguration.runsPerTestDetectsFlakes()
&& testConfiguration.cancelConcurrentTests();
RunfilesSupplier testRunfilesSupplier;
- if (testConfiguration.isPersistentTestRunner()) {
+ if (isPersistentTestRunner()) {
// Create a RunfilesSupplier from the persistent test runner's runfiles. Pass only the
// test runner's runfiles to avoid using a different worker for every test run.
testRunfilesSupplier =
@@ -380,7 +388,7 @@
}
ImmutableList.Builder<Artifact> tools = new ImmutableList.Builder<>();
- if (testConfiguration.isPersistentTestRunner()) {
+ if (isPersistentTestRunner()) {
tools.add(testActionExecutable);
tools.add(executionSettings.getExecutable());
tools.addAll(additionalTools.build());
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestConfiguration.java
index bf59452..f476e82 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestConfiguration.java
@@ -327,6 +327,12 @@
return options.testShardingStrategy;
}
+ /**
+ * Whether the persistent test runner is enabled. Note that not all test rules support this
+ * feature, in which case Bazel should fall back to the normal test runner. Therefore, this method
+ * must only be called by test rules, and never for test actions. For actions, use {@code
+ * TestTargetProperties.isPersistentTestRunner} instead.
+ */
public boolean isPersistentTestRunner() {
return options.persistentTestRunner;
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
index e79d8d3..2fb04db 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
@@ -584,7 +584,7 @@
env.put("RUNFILES_MANIFEST_ONLY", "1");
}
- if (testConfiguration.isPersistentTestRunner()) {
+ if (testProperties.isPersistentTestRunner()) {
// Let the test runner know it runs persistently within a worker.
env.put("PERSISTENT_TEST_RUNNER", "true");
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetProperties.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetProperties.java
index 15a422f..0493dc6 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetProperties.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestTargetProperties.java
@@ -70,13 +70,16 @@
private final boolean isExternal;
private final String language;
private final ImmutableMap<String, String> executionInfo;
+ private final boolean isPersistentTestRunner;
/**
- * Creates test target properties instance. Constructor expects that it
- * will be called only for test configured targets.
+ * Creates test target properties instance. Constructor expects that it will be called only for
+ * test configured targets.
*/
- TestTargetProperties(RuleContext ruleContext,
- ExecutionInfo executionRequirements) {
+ TestTargetProperties(
+ RuleContext ruleContext,
+ ExecutionInfo executionRequirements,
+ boolean isPersistentTestRunner) {
Rule rule = ruleContext.getRule();
Preconditions.checkState(TargetUtils.isTestRule(rule));
@@ -87,6 +90,7 @@
// We need to use method on ruleConfiguredTarget to perform validation.
isFlaky = ruleContext.attributes().get("flaky", Type.BOOLEAN);
isExternal = TargetUtils.isExternalTestRule(rule);
+ this.isPersistentTestRunner = isPersistentTestRunner;
Map<String, String> executionInfo = Maps.newLinkedHashMap();
executionInfo.putAll(TargetUtils.getExecutionInfo(rule));
@@ -133,6 +137,10 @@
return isExternal;
}
+ public boolean isPersistentTestRunner() {
+ return isPersistentTestRunner;
+ }
+
public ResourceSet getLocalResourceUsage(Label label, boolean usingLocalTestJobs)
throws UserExecException {
if (usingLocalTestJobs) {
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 e0d24d1..f8c7d1d 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
@@ -35,7 +35,6 @@
import com.google.devtools.build.lib.actions.TestExecException;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.test.TestAttempt;
-import com.google.devtools.build.lib.analysis.test.TestConfiguration;
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.TestRunnerAction.ResolvedPaths;
@@ -117,7 +116,7 @@
executionInfo.put(ExecutionRequirements.NO_CACHE, "");
}
executionInfo.put(ExecutionRequirements.TIMEOUT, "" + getTimeout(action).getSeconds());
- if (action.getConfiguration().getFragment(TestConfiguration.class).isPersistentTestRunner()) {
+ if (action.getTestProperties().isPersistentTestRunner()) {
executionInfo.put(ExecutionRequirements.SUPPORTS_WORKERS, "1");
}
@@ -136,7 +135,7 @@
action.getRunfilesSupplier(),
ImmutableMap.of(),
/*inputs=*/ action.getInputs(),
- action.getConfiguration().getFragment(TestConfiguration.class).isPersistentTestRunner()
+ action.getTestProperties().isPersistentTestRunner()
? action.getTools()
: NestedSetBuilder.emptySet(Order.STABLE_ORDER),
ImmutableSet.copyOf(action.getSpawnOutputs()),
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index f8d0843..ca9ceb9 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -911,6 +911,18 @@
)
sh_test(
+ name = "persistent_test_runner_test",
+ size = "large",
+ srcs = ["persistent_test_runner_test.sh"],
+ data = [
+ ":test-deps",
+ ],
+ tags = [
+ "no_windows",
+ ],
+)
+
+sh_test(
name = "bazel_workspace_status_test",
size = "large",
srcs = ["bazel_workspace_status_test.sh"],
diff --git a/src/test/shell/bazel/persistent_test_runner_test.sh b/src/test/shell/bazel/persistent_test_runner_test.sh
new file mode 100755
index 0000000..67f7028
--- /dev/null
+++ b/src/test/shell/bazel/persistent_test_runner_test.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Copyright 2020 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Tests the persistent test runner
+#
+
+# Load test environment
+# Load the test setup defined in the parent directory
+CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "${CURRENT_DIR}/../integration_test_setup.sh" \
+ || { echo "integration_test_setup.sh not found!" >&2; exit 1; }
+
+cat >>$TEST_TMPDIR/bazelrc <<'EOF'
+build --strategy=TestRunner=worker,local
+build --experimental_persistent_test_runner
+EOF
+
+function test_simple_sh_test() {
+ mkdir -p examples/tests/
+ cat << 'EOF' > examples/tests/BUILD
+sh_test(
+ name = "shell",
+ srcs = [ "shell.sh" ],
+)
+EOF
+ cat << 'EOF' > examples/tests/shell.sh
+EOF
+ chmod +x examples/tests/shell.sh
+ bazel test examples/tests:shell &> $TEST_log || fail "Shell test failed"
+}
+
+run_suite "persistent_test_runner"