diff options
author | 2016-12-07 12:20:58 +0000 | |
---|---|---|
committer | 2016-12-07 12:53:49 +0000 | |
commit | af878d04990d9eb12bda75b3571dff48d40fce1a (patch) | |
tree | 739b8760bca709f5cf2a6d8af0a9c9237b676a3a /src/main/java/com/google/devtools/build/lib | |
parent | b9aadcbcf5b0023ced7f9acf0394ab2bf05420c8 (diff) |
Add coverage support for java test.
(series 4/4 of open-sourcing coverage command for java test)
--
PiperOrigin-RevId: 141292977
MOS_MIGRATED_REVID=141292977
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib')
10 files changed, 222 insertions, 59 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java b/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java index 6ad0617071..ebffbf8e42 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java @@ -132,7 +132,6 @@ public class BaseRuleClasses { env.getToolsLabel("//tools/test:runtime")))) // Input files for test actions collecting code coverage .add(attr("$coverage_support", LABEL) - .cfg(HOST) .value(env.getLabel("//tools/defaults:coverage_support"))) // Used in the one-per-build coverage report generation action. .add(attr("$coverage_report_generator", LABEL) diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java index 58ea07a541..4c14c73b5b 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.FileProvider; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; @@ -27,19 +28,19 @@ import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; -import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.Builder; import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction; import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.ComputedSubstitution; import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution; import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Template; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.bazel.rules.BazelConfiguration; -import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder; import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder.Compression; import com.google.devtools.build.lib.rules.java.JavaCommon; +import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; import com.google.devtools.build.lib.rules.java.JavaCompilationArtifacts; import com.google.devtools.build.lib.rules.java.JavaCompilationHelper; import com.google.devtools.build.lib.rules.java.JavaConfiguration; @@ -53,6 +54,7 @@ import com.google.devtools.build.lib.rules.java.JavaUtil; import com.google.devtools.build.lib.rules.java.Jvm; import com.google.devtools.build.lib.rules.java.proto.GeneratedExtensionRegistryProvider; import com.google.devtools.build.lib.syntax.Type; +import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.util.ShellEscaper; @@ -79,6 +81,9 @@ public class BazelJavaSemantics implements JavaSemantics { private static final String JAVABUILDER_CLASS_NAME = "com.google.devtools.build.buildjar.BazelJavaBuilder"; + private static final String JACOCO_COVERAGE_RUNNER_MAIN_CLASS = + "com.google.testing.coverage.JacocoCoverageRunner"; + private BazelJavaSemantics() { } @@ -159,17 +164,6 @@ public class BazelJavaSemantics implements JavaSemantics { } @Override - public Artifact createInstrumentationMetadataArtifact( - RuleContext ruleContext, Artifact outputJar) { - return null; - } - - @Override - public void buildJavaCommandLine(Collection<Artifact> outputs, BuildConfiguration configuration, - Builder result, Label targetLabel) { - } - - @Override public Artifact createStubAction( RuleContext ruleContext, final JavaCommon javaCommon, @@ -208,6 +202,20 @@ public class BazelJavaSemantics implements JavaSemantics { } }); + JavaCompilationArtifacts javaArtifacts = javaCommon.getJavaCompilationArtifacts(); + String path = + javaArtifacts.getInstrumentedJar() != null + ? "${JAVA_RUNFILES}/" + + workspacePrefix + + javaArtifacts.getInstrumentedJar().getRootRelativePath().getPathString() + : ""; + arguments.add( + Substitution.of( + "%set_jacoco_metadata%", + ruleContext.getConfiguration().isCodeCoverageEnabled() + ? "export JACOCO_METADATA_JAR=" + path + : "")); + arguments.add(Substitution.of("%java_start_class%", ShellEscaper.escapeString(javaStartClass))); arguments.add(Substitution.ofSpaceSeparatedList("%jvm_flags%", ImmutableList.copyOf(jvmFlags))); @@ -415,12 +423,79 @@ public class BazelJavaSemantics implements JavaSemantics { return jvmFlags.build(); } + /** + * Returns whether coverage has instrumented artifacts. + */ + public static boolean hasInstrumentationMetadata(JavaTargetAttributes.Builder attributes) { + return !attributes.getInstrumentationMetadata().isEmpty(); + } + + // TODO(yueg): refactor this (only mainClass different for now) @Override - public String addCoverageSupport(JavaCompilationHelper helper, + public String addCoverageSupport( + JavaCompilationHelper helper, JavaTargetAttributes.Builder attributes, - Artifact executable, Artifact instrumentationMetadata, - JavaCompilationArtifacts.Builder javaArtifactsBuilder, String mainClass) { - return mainClass; + Artifact executable, + Artifact instrumentationMetadata, + JavaCompilationArtifacts.Builder javaArtifactsBuilder, + String mainClass) + throws InterruptedException { + // This method can be called only for *_binary/*_test targets. + Preconditions.checkNotNull(executable); + // Add our own metadata artifact (if any). + if (instrumentationMetadata != null) { + attributes.addInstrumentationMetadataEntries(ImmutableList.of(instrumentationMetadata)); + } + + if (!hasInstrumentationMetadata(attributes)) { + return mainClass; + } else { + Artifact instrumentedJar = + helper + .getRuleContext() + .getBinArtifact(helper.getRuleContext().getLabel().getName() + "_instrumented.jar"); + + // Create an instrumented Jar. This will be referenced on the runtime classpath prior + // to all other Jars. + JavaCommon.createInstrumentedJarAction( + helper.getRuleContext(), + this, + attributes.getInstrumentationMetadata(), + instrumentedJar, + mainClass); + javaArtifactsBuilder.setInstrumentedJar(instrumentedJar); + + // Add the coverage runner to the list of dependencies when compiling in coverage mode. + TransitiveInfoCollection runnerTarget = + helper.getRuleContext().getPrerequisite("$jacocorunner", Mode.TARGET); + if (runnerTarget.getProvider(JavaCompilationArgsProvider.class) != null) { + helper.addLibrariesToAttributes(ImmutableList.of(runnerTarget)); + } else { + helper + .getRuleContext() + .ruleError( + "this rule depends on " + + helper.getRuleContext().attributes().get("$jacocorunner", BuildType.LABEL) + + " which is not a java_library rule, or contains errors"); + } + // In offline instrumentation mode, add the Jacoco runtime to the classpath as well (this + // jar is not included by the coverage runner). Note that $jacoco is provided via a + // filegroup because the same jar can be used for online instrumentation, by simply adding + // it to -javaagent and -Xbootclasspath/p (similar to the Reverifier setup). The $jacoco jar + // has a "Premain-Class:" entry in its manifest, which would get erased by ijar filtering, + // hence the filegroup. + TransitiveInfoCollection agentTarget = + helper.getRuleContext().getPrerequisite("$jacoco_runtime", Mode.TARGET); + NestedSet<Artifact> filesToBuild = + agentTarget.getProvider(FileProvider.class).getFilesToBuild(); + for (Artifact jar : FileType.filter(filesToBuild, JavaSemantics.JAR)) { + attributes.addRuntimeClassPathEntry(jar); + } + } + + // We do not add the instrumented jar to the runtime classpath, but provide it in the shell + // script via an environment variable. + return JACOCO_COVERAGE_RUNNER_MAIN_CLASS; } @Override diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java index fb990f448f..ac294bd3d9 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java @@ -58,6 +58,19 @@ public final class BazelJavaTestRule implements RuleDefinition { .override(attr("stamp", TRISTATE).value(TriState.NO)) .override(attr("use_testrunner", BOOLEAN).value(true)) .override(attr(":java_launcher", LABEL).value(JavaSemantics.JAVA_LAUNCHER)) + // Input files for test actions collecting code coverage + .add( + attr("$lcov_merger", LABEL) + .value(env.getLabel("@bazel_tools//tools/test:LcovMerger_deploy.jar"))) + // Used in the one-per-build coverage report generation action. + .add( + attr("$jacoco_runtime", LABEL) + .value(env.getLabel("@bazel_tools//third_party/java/jacoco:blaze-agent"))) + .add( + attr("$jacocorunner", LABEL) + .value( + env.getLabel( + "@bazel_tools//src/java_tools/junitrunner/java/com/google/testing/coverage:JacocoCoverage"))) /* <!-- #BLAZE_RULE(java_test).ATTRIBUTE(test_class) --> The Java class to be loaded by the test runner.<br/> <p> diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt index f77051fb2c..df667e4c71 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt @@ -207,6 +207,11 @@ else CLASSPATH=%classpath% fi +# If using Jacoco in offline instrumentation mode, the CLASSPATH contains instrumented files. +# We need to make the metadata jar with uninstrumented classes available for generating +# the lcov-compatible coverage report, and we don't want it on the classpath. +%set_jacoco_metadata% + if [[ -n "$JVM_DEBUG_PORT" ]]; then JVM_DEBUG_SUSPEND=${DEFAULT_JVM_DEBUG_SUSPEND:-"y"} JVM_DEBUG_FLAGS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=${JVM_DEBUG_SUSPEND},address=${JVM_DEBUG_PORT}" diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java index 77e2b02a13..b5f2dae458 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java @@ -217,6 +217,20 @@ public final class JavaCompilationHelper { } /** + * Returns the instrumentation metadata files to be generated for a given output jar. + * + * <p>Only called if the output jar actually needs to be instrumented. + */ + @Nullable + private static Artifact createInstrumentationMetadataArtifact( + RuleContext ruleContext, Artifact outputJar) { + PathFragment packageRelativePath = outputJar.getRootRelativePath().relativeTo( + ruleContext.getPackageDirectory()); + return ruleContext.getPackageRelativeArtifact( + FileSystemUtils.replaceExtension(packageRelativePath, ".em"), outputJar.getRoot()); + } + + /** * Creates the Action that compiles Java source files and optionally instruments them for * coverage. * @@ -246,13 +260,14 @@ public final class JavaCompilationHelper { * @return the instrumentation metadata artifact or null if instrumentation is * disabled */ + @Nullable public Artifact createInstrumentationMetadata(Artifact outputJar, JavaCompilationArtifacts.Builder javaArtifactsBuilder) { // If we need to instrument the jar, add additional output (the coverage metadata file) to the // JavaCompileAction. Artifact instrumentationMetadata = null; if (shouldInstrumentJar()) { - instrumentationMetadata = semantics.createInstrumentationMetadataArtifact( + instrumentationMetadata = createInstrumentationMetadataArtifact( getRuleContext(), outputJar); if (instrumentationMetadata != null) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java index d1cc48ec1e..6d55d4e7e8 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java @@ -89,6 +89,8 @@ import java.util.logging.Logger; */ @ThreadCompatible @Immutable public final class JavaCompileAction extends AbstractAction { + private static final String JACOCO_INSTRUMENTATION_PROCESSOR = "jacoco"; + private static final String GUID = "786e174d-ed97-4e79-9f61-ae74430714cf"; private static final ResourceSet LOCAL_RESOURCES = @@ -994,6 +996,36 @@ public final class JavaCompileAction extends AbstractAction { ruleContext.getConfiguration(), semantics); } + /** + * May add extra command line options to the Java compile command line. + */ + private static void buildJavaCommandLine( + Collection<Artifact> outputs, + BuildConfiguration configuration, + CustomCommandLine.Builder result, + Label targetLabel) { + Artifact metadata = null; + for (Artifact artifact : outputs) { + if (artifact.getExecPathString().endsWith(".em")) { + metadata = artifact; + break; + } + } + + if (metadata == null) { + return; + } + + result.add("--post_processor"); + result.addExecPath(JACOCO_INSTRUMENTATION_PROCESSOR, metadata); + result.addPath( + configuration + .getCoverageMetadataDirectory(targetLabel.getPackageIdentifier().getRepository()) + .getExecPath()); + result.add("-*Test"); + result.add("-*TestCase"); + } + public JavaCompileAction build() { // TODO(bazel-team): all the params should be calculated before getting here, and the various // aggregation code below should go away. @@ -1094,7 +1126,7 @@ public final class JavaCompileAction extends AbstractAction { strictJavaDeps, compileTimeDependencyArtifacts ); - semantics.buildJavaCommandLine( + buildJavaCommandLine( outputs, configuration, paramFileContentsBuilder, targetLabel); CommandLine paramFileContents = paramFileContentsBuilder.build(); Action parameterFileWriteAction = new ParameterFileWriteAction(owner, paramFile, diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java index 0e45fbbf90..c336ba25fc 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java @@ -234,19 +234,6 @@ public interface JavaSemantics { ImmutableList<Artifact> collectResources(RuleContext ruleContext); /** - * Creates the instrumentation metadata artifact for the specified output .jar . - */ - @Nullable - Artifact createInstrumentationMetadataArtifact(RuleContext ruleContext, Artifact outputJar); - - /** - * May add extra command line options to the Java compile command line. - */ - void buildJavaCommandLine(Collection<Artifact> outputs, BuildConfiguration configuration, - CustomCommandLine.Builder result, Label targetLabel); - - - /** * Constructs the command line to call SingleJar to join all artifacts from * {@code classpath} (java code) and {@code resources} into {@code output}. */ diff --git a/src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java b/src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java index 85c8ccf3e3..bbef175a7d 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java +++ b/src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java @@ -35,9 +35,8 @@ import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventKind; import com.google.devtools.build.lib.events.Reporter; import com.google.devtools.build.lib.util.io.FileOutErr; -import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; -import com.google.devtools.build.lib.vfs.Symlinks; +import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.view.test.TestStatus.BlazeTestStatus; import com.google.devtools.build.lib.view.test.TestStatus.TestCase; import com.google.devtools.build.lib.view.test.TestStatus.TestResultData; @@ -53,8 +52,8 @@ import java.util.Map; @ExecutionStrategy(contextType = TestActionContext.class, name = { "standalone" }) public class StandaloneTestStrategy extends TestStrategy { // TODO(bazel-team) - add tests for this strategy. - private static final String COLLECT_COVERAGE = - "external/bazel_tools/tools/coverage/collect-coverage.sh"; + public static final String COLLECT_COVERAGE = + "external/bazel_tools/tools/test/collect_coverage.sh"; private final Path workspace; @@ -70,6 +69,9 @@ public class StandaloneTestStrategy extends TestStrategy { @Override public void exec(TestRunnerAction action, ActionExecutionContext actionExecutionContext) throws ExecException, InterruptedException { + Path execRoot = actionExecutionContext.getExecutor().getExecRoot(); + Path coverageDir = execRoot.getRelative(TestStrategy.getCoverageDirectory(action)); + Path runfilesDir = null; try { runfilesDir = @@ -83,12 +85,11 @@ public class StandaloneTestStrategy extends TestStrategy { throw new TestExecException(e.getMessage()); } - Path testTmpDir = TestStrategy.getTmpRoot( - workspace, actionExecutionContext.getExecutor().getExecRoot(), executionOptions) - .getChild(getTmpDirName(action.getExecutionSettings().getExecutable().getExecPath())); + Path testTmpDir = + TestStrategy.getTmpRoot(workspace, execRoot, executionOptions) + .getChild(getTmpDirName(action.getExecutionSettings().getExecutable().getExecPath())); Path workingDirectory = runfilesDir.getRelative(action.getRunfilesPrefix()); - Path execRoot = actionExecutionContext.getExecutor().getExecRoot(); TestRunnerAction.ResolvedPaths resolvedPaths = action.resolve(execRoot); Map<String, String> env = getEnv(action, execRoot, runfilesDir, testTmpDir, resolvedPaths.getXmlOutputPath()); @@ -114,17 +115,7 @@ public class StandaloneTestStrategy extends TestStrategy { Executor executor = actionExecutionContext.getExecutor(); try { - if (testTmpDir.exists(Symlinks.NOFOLLOW)) { - FileSystemUtils.deleteTree(testTmpDir); - } - FileSystemUtils.createDirectoryAndParents(testTmpDir); - } catch (IOException e) { - executor.getEventHandler().handle(Event.error("Could not create TEST_TMPDIR: " + e)); - throw new EnvironmentalExecException("Could not create TEST_TMPDIR " + testTmpDir, e); - } - - try { - FileSystemUtils.createDirectoryAndParents(workingDirectory); + prepareFileSystem(action, testTmpDir, coverageDir, workingDirectory); ResourceSet resources = action.getTestProperties().getLocalResourceUsage(executionOptions.usingLocalTestJobs()); @@ -172,13 +163,13 @@ public class StandaloneTestStrategy extends TestStrategy { if (!action.isEnableRunfiles()) { vars.put("RUNFILES_MANIFEST_ONLY", "1"); } + + PathFragment coverageDir = TestStrategy.getCoverageDirectory(action); if (isCoverageMode(action)) { - vars.put("COVERAGE_MANIFEST", - action.getExecutionSettings().getInstrumentedFileManifest().getExecPathString()); + vars.put("COVERAGE_DIR", coverageDir.toString()); vars.put("COVERAGE_OUTPUT_FILE", action.getCoverageData().getExecPathString()); - // Instruct test-setup.sh not to cd into the runfiles directory. - vars.put("RUNTEST_PRESERVE_CWD", "1"); } + return vars; } @@ -231,6 +222,10 @@ public class StandaloneTestStrategy extends TestStrategy { if (details != null) { builder.setTestCase(details); } + + if (isCoverageMode(action)) { + builder.setHasCoverage(true); + } return builder.build(); } catch (IOException e) { diff --git a/src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java index d71986193b..b7becb4eaf 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java @@ -14,6 +14,8 @@ package com.google.devtools.build.lib.rules.test; +import static com.google.devtools.build.lib.packages.BuildType.LABEL; + import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.devtools.build.lib.actions.Artifact; @@ -36,11 +38,9 @@ import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.common.options.EnumConverter; - import java.util.List; import java.util.Map; import java.util.TreeMap; - import javax.annotation.Nullable; /** @@ -207,7 +207,17 @@ public final class TestActionBuilder { NestedSet<Artifact> metadataFiles = instrumentedFiles.getInstrumentationMetadataFiles(); inputsBuilder.addTransitive(metadataFiles); inputsBuilder.addTransitive(PrerequisiteArtifacts.nestedSet( - ruleContext, "$coverage_support", Mode.HOST)); + ruleContext, "$coverage_support", Mode.DONT_CHECK)); + // We don't add this attribute to non-supported test target + if (ruleContext.isAttrDefined("$lcov_merger", LABEL)) { + Artifact lcovMerger = ruleContext.getPrerequisiteArtifact("$lcov_merger", Mode.TARGET); + if (lcovMerger != null) { + inputsBuilder.addTransitive( + PrerequisiteArtifacts.nestedSet(ruleContext, "$lcov_merger", Mode.TARGET)); + // Pass this LcovMerger_deploy.jar path to collect_coverage.sh + testEnv.put("LCOV_MERGER", lcovMerger.getExecPathString()); + } + } Artifact instrumentedFileManifest = InstrumentedFileManifestAction.getInstrumentedFileManifest(ruleContext, diff --git a/src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java b/src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java index 7590ef1d2b..ee6d67098a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java +++ b/src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java @@ -58,6 +58,8 @@ import javax.annotation.Nullable; * A strategy for executing a {@link TestRunnerAction}. */ public abstract class TestStrategy implements TestActionContext { + public static final PathFragment COVERAGE_TMP_ROOT = new PathFragment("_coverage"); + public static final String TEST_SETUP_BASENAME = "test-setup.sh"; /** @@ -168,6 +170,27 @@ public abstract class TestStrategy implements TestActionContext { public abstract void exec(TestRunnerAction action, ActionExecutionContext actionExecutionContext) throws ExecException, InterruptedException; + /** Returns true if coverage data should be gathered. */ + protected static boolean isMicroCoverageMode(TestRunnerAction action) { + return action.getMicroCoverageData() != null; + } + + /** + * Returns directory to store coverage results for the given action relative to the execution + * root. This directory is used to store all coverage results related to the test execution with + * exception of the locally generated *.gcda files. Those are stored separately using relative + * path within coverage directory. + * + * <p>Coverage directory name for the given test runner action is constructed as: {@code $(blaze + * info execution_root)/_coverage/target_path/test_log_name} where {@code test_log_name} + * is usually a target name but potentially can include extra suffix, such as a shard number (if + * test execution was sharded). + */ + protected static PathFragment getCoverageDirectory(TestRunnerAction action) { + return COVERAGE_TMP_ROOT.getRelative( + FileSystemUtils.removeExtension(action.getTestLog().getRootRelativePath())); + } + /** * Returns mutable map of default testing shell environment. By itself it is incomplete and is * modified further by the specific test strategy implementations (mostly due to the fact that @@ -198,6 +221,15 @@ public abstract class TestStrategy implements TestActionContext { env.put(TEST_BRIDGE_TEST_FILTER_ENV, testFilter); } + if (isCoverageMode(action)) { + env.put( + "COVERAGE_MANIFEST", + action.getExecutionSettings().getInstrumentedFileManifest().getExecPathString()); + // Instruct remote-runtest.sh/local-runtest.sh not to cd into the runfiles directory. + env.put("RUNTEST_PRESERVE_CWD", "1"); + env.put("MICROCOVERAGE_REQUESTED", isMicroCoverageMode(action) ? "true" : "false"); + } + return env; } |