From d56c7dbbe914cee2019a2a658a3147b3a91b3732 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Fri, 21 Aug 2026 12:00:10 -0700 Subject: [PATCH 1/2] Only supervise tests that require XML generation --- .../templates/attributes/common/tags.html | 8 +++++ .../lib/actions/ExecutionRequirements.java | 3 ++ .../lib/analysis/test/TestActionBuilder.java | 4 ++- .../analysis/test/TestActionBuilderTest.java | 31 +++++++++++++++++-- .../lib/exec/StandaloneTestStrategyTest.java | 2 ++ 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/google/devtools/build/docgen/templates/attributes/common/tags.html b/src/main/java/com/google/devtools/build/docgen/templates/attributes/common/tags.html index c4877759a029c8..47bfb59e7a4714 100644 --- a/src/main/java/com/google/devtools/build/docgen/templates/attributes/common/tags.html +++ b/src/main/java/com/google/devtools/build/docgen/templates/attributes/common/tags.html @@ -62,6 +62,14 @@ is enabled. +
  • requires-test-xml-generation marks test actions as requiring + Bazel to ensure that they produce test XML. With + --experimental_test_xml_missing_behavior=workaround and split + XML generation enabled, Bazel supervises these test actions and generates + missing XML in the test spawn, as it does when + --noexperimental_split_xml_generation is set. +
  • +
  • block-network keyword blocks access to the external network from inside the sandbox. In this case, only communication with localhost is allowed. This tag only has an effect if sandboxing is diff --git a/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java b/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java index 94576c64fa41c6..b13462b7e1a276 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java +++ b/src/main/java/com/google/devtools/build/lib/actions/ExecutionRequirements.java @@ -261,6 +261,9 @@ public enum WorkerProtocolFormat { */ public static final String REQUIRES_NETWORK = "requires-network"; + /** Indicates that test actions need Bazel to ensure that they produce test XML. */ + public static final String REQUIRES_TEST_XML_GENERATION = "requires-test-xml-generation"; + /** * Disables networking for a spawn if possible (only if sandboxing is enabled and if the sandbox * supports it). 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 c027d232af66a7..fd9e153e7eabac 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 @@ -27,6 +27,7 @@ import com.google.devtools.build.lib.actions.ActionOwner; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.ArtifactRoot; +import com.google.devtools.build.lib.actions.ExecutionRequirements; import com.google.devtools.build.lib.analysis.Allowlist; import com.google.devtools.build.lib.analysis.AnalysisEnvironment; import com.google.devtools.build.lib.analysis.FilesToRunProvider; @@ -305,7 +306,8 @@ private TestParams createTestAction() inputsBuilder.add(testXmlGeneratorExecutable); Artifact ensureXmlExecutable = null; if (testConfiguration.getTestXmlMissingBehavior() - == TestConfiguration.TestXmlMissingBehavior.WORKAROUND) { + == TestConfiguration.TestXmlMissingBehavior.WORKAROUND + && testProperties.getTags().contains(ExecutionRequirements.REQUIRES_TEST_XML_GENERATION)) { ensureXmlExecutable = getEnsureXmlExecutable(actionOwner); if (ensureXmlExecutable != null) { inputsBuilder.add(ensureXmlExecutable); diff --git a/src/test/java/com/google/devtools/build/lib/analysis/test/TestActionBuilderTest.java b/src/test/java/com/google/devtools/build/lib/analysis/test/TestActionBuilderTest.java index dfdf56476004f9..242a42c9494c10 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/test/TestActionBuilderTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/test/TestActionBuilderTest.java @@ -25,6 +25,7 @@ import com.google.common.eventbus.EventBus; import com.google.devtools.build.lib.actions.Action; import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.ExecutionRequirements; import com.google.devtools.build.lib.actions.MiddlemanAction; import com.google.devtools.build.lib.actions.RunfilesTree; import com.google.devtools.build.lib.analysis.AnalysisResult; @@ -774,6 +775,7 @@ public void testEnsureXmlWrapsRunUnderAndTest() throws Exception { "foo_test(", " name = 'test',", " srcs = ['test.sh'],", + " tags = ['requires-test-xml-generation'],", ")"); scratch.file("xml_wrapper/test.sh", "#!/bin/sh", "exit 0"); useConfiguration( @@ -786,6 +788,8 @@ public void testEnsureXmlWrapsRunUnderAndTest() throws Exception { Artifact ensureXml = testAction.getEnsureXmlExecutable(); assertThat(ensureXml).isNotNull(); assertThat(testAction.getInputs().toList()).contains(ensureXml); + assertThat(testAction.getExecutionInfo()) + .containsKey(ExecutionRequirements.REQUIRES_TEST_XML_GENERATION); ImmutableList args = TestStrategy.expandedArgsFromAction(testAction, /* ensureXml= */ true); @@ -794,6 +798,21 @@ public void testEnsureXmlWrapsRunUnderAndTest() throws Exception { assertThat(PathFragment.create(args.get(3)).getBaseName()).isEqualTo("test"); } + @Test + public void testEnsureXmlIsNotInputWithoutRequiresTestXmlGenerationTag() throws Exception { + useConfiguration("--experimental_test_xml_missing_behavior=workaround"); + + TestRunnerAction testAction = + (TestRunnerAction) getGeneratingAction(getTestStatusArtifacts("//tests:small_test_2").get(0)); + + assertThat(testAction.getEnsureXmlExecutable()).isNull(); + assertThat( + testAction.getInputs().toList().stream() + .map(artifact -> artifact.getExecPath().getBaseName()) + .anyMatch(name -> name.startsWith("ensure_xml_"))) + .isFalse(); + } + @Test public void testEnsureXmlIsNotInputOutsideWorkaroundMode() throws Exception { useConfiguration("--experimental_test_xml_missing_behavior=warn"); @@ -821,7 +840,11 @@ public void testEnsureXmlUsesTargetPlatformForTests() throws Exception { " '" + TestConstants.CONSTRAINTS_PACKAGE_ROOT + "cpu:aarch64',", " ],", ")", - "foo_test(name = 'test', srcs = ['test.sh'])"); + "foo_test(", + " name = 'test',", + " srcs = ['test.sh'],", + " tags = ['requires-test-xml-generation'],", + ")"); scratch.file("xml_platform/test.sh", "#!/bin/sh", "exit 0"); useConfiguration( "--experimental_test_xml_missing_behavior=workaround", @@ -854,7 +877,11 @@ public void testEnsureXmlIsOmittedForUnsupportedExecutionPlatform() throws Excep " '" + TestConstants.CONSTRAINTS_PACKAGE_ROOT + "cpu:x86_64',", " ],", ")", - "foo_test(name = 'test', srcs = ['test.sh'])"); + "foo_test(", + " name = 'test',", + " srcs = ['test.sh'],", + " tags = ['requires-test-xml-generation'],", + ")"); scratch.file("xml_platform/test.sh", "#!/bin/sh", "exit 0"); useConfiguration( "--experimental_test_xml_missing_behavior=workaround", 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 9db5a521557b62..ad5161c1d0f4f7 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 @@ -840,6 +840,7 @@ public void workaroundWrapperProducesXmlWithoutDerivedSpawn() throws Exception { name = "wrapped_test", size = "small", srcs = ["wrapped_test.sh"], + tags = ["requires-test-xml-generation"], ) """); TestRunnerAction testRunnerAction = getTestAction("//standalone:wrapped_test"); @@ -892,6 +893,7 @@ public void workaroundWrapperIsDisabledWithoutSplitXmlGeneration() throws Except name = "unwrapped_test", size = "small", srcs = ["unwrapped_test.sh"], + tags = ["requires-test-xml-generation"], ) """); TestRunnerAction testRunnerAction = getTestAction("//standalone:unwrapped_test"); From 4ccd1dff4d700559fea42a121499b4613e7582d2 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Fri, 21 Aug 2026 12:06:07 -0700 Subject: [PATCH 2/2] Suggest XML generation tag when required --- .../build/lib/exec/StandaloneTestStrategy.java | 6 ++++++ src/test/java/com/google/devtools/build/lib/exec/BUILD | 1 + .../build/lib/exec/StandaloneTestStrategyTest.java | 10 ++++++++++ 3 files changed, 17 insertions(+) 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 0f7963bc965767..908a435f089747 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 @@ -885,6 +885,12 @@ private TestAttemptResult runTestAttempt( String.format( "Test action %s failed to produce test.xml", testAction.getOwner().getLabel()); TestXmlMissingBehavior missingBehavior = testAction.getTestXmlMissingBehavior(); + if (missingBehavior == TestXmlMissingBehavior.WORKAROUND) { + missingXmlMessage += + String.format( + "; add '%s' to the test target's tags attribute", + ExecutionRequirements.REQUIRES_TEST_XML_GENERATION); + } if (missingBehavior == TestXmlMissingBehavior.FAIL || missingBehavior == TestXmlMissingBehavior.WORKAROUND) { throw createTestExecException(TestAction.Code.MISSING_XML_OUTPUT, missingXmlMessage); 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 55f0121e86bb2f..c85f23da68122e 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/BUILD +++ b/src/test/java/com/google/devtools/build/lib/exec/BUILD @@ -27,6 +27,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/actions:action_input_helper", "//src/main/java/com/google/devtools/build/lib/actions:artifact_expander", "//src/main/java/com/google/devtools/build/lib/actions:artifacts", + "//src/main/java/com/google/devtools/build/lib/actions:execution_requirements", "//src/main/java/com/google/devtools/build/lib/actions:file_metadata", "//src/main/java/com/google/devtools/build/lib/actions:fileset_output_symlink", "//src/main/java/com/google/devtools/build/lib/actions:fileset_output_tree", 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 ad5161c1d0f4f7..617dec1aa9e8d1 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 @@ -35,6 +35,7 @@ import com.google.devtools.build.lib.actions.ActionKeyContext; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.DiscoveredModulesPruner; +import com.google.devtools.build.lib.actions.ExecutionRequirements; import com.google.devtools.build.lib.actions.InputMetadataProvider; import com.google.devtools.build.lib.actions.Spawn; import com.google.devtools.build.lib.actions.SpawnResult; @@ -816,6 +817,15 @@ public void missingXmlStrictBehaviorFailsWithoutGeneratingXml( () -> execute(testRunnerAction, actionExecutionContext, standaloneTestStrategy)); assertThat(exception).hasMessageThat().contains("failed to produce test.xml"); + if (testXmlMissingBehavior == TestXmlMissingBehavior.WORKAROUND) { + assertThat(exception) + .hasMessageThat() + .contains(ExecutionRequirements.REQUIRES_TEST_XML_GENERATION); + } else { + assertThat(exception) + .hasMessageThat() + .doesNotContain(ExecutionRequirements.REQUIRES_TEST_XML_GENERATION); + } verify(spawnStrategy, times(1)).exec(any(), any()); }