aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib
diff options
context:
space:
mode:
authorGravatar Yue Gan <yueg@google.com>2016-12-07 12:20:58 +0000
committerGravatar Laszlo Csomor <laszlocsomor@google.com>2016-12-07 12:53:49 +0000
commitaf878d04990d9eb12bda75b3571dff48d40fce1a (patch)
tree739b8760bca709f5cf2a6d8af0a9c9237b676a3a /src/main/java/com/google/devtools/build/lib
parentb9aadcbcf5b0023ced7f9acf0394ab2bf05420c8 (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')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java1
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java109
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaTestRule.java13
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt5
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java17
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java34
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java13
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/test/StandaloneTestStrategy.java41
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java16
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/test/TestStrategy.java32
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;
}