blob: 99c5740ab49149718c8a7ef01f9da91ba02448e8 [file] [log] [blame]
// Copyright 2015 Google Inc. 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.
package com.google.devtools.build.lib.analysis;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactOwner;
import com.google.devtools.build.lib.actions.Executor;
import com.google.devtools.build.lib.actions.MapBasedActionGraph;
import com.google.devtools.build.lib.actions.MiddlemanFactory;
import com.google.devtools.build.lib.actions.MutableActionGraph;
import com.google.devtools.build.lib.actions.ResourceManager;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.Root;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildInfoKey;
import com.google.devtools.build.lib.analysis.config.BinTools;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationKey;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
import com.google.devtools.build.lib.analysis.config.ConfigurationFactory;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.analysis.util.AnalysisMock;
import com.google.devtools.build.lib.analysis.util.AnalysisTestUtil;
import com.google.devtools.build.lib.buildtool.BuildRequest;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.StoredEventHandler;
import com.google.devtools.build.lib.exec.ExecutionOptions;
import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.ConstantRuleVisibility;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.OutputFile;
import com.google.devtools.build.lib.packages.PackageFactory;
import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
import com.google.devtools.build.lib.packages.PackageIdentifier;
import com.google.devtools.build.lib.packages.PackageSpecification;
import com.google.devtools.build.lib.packages.Preprocessor;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.util.MockToolsConfig;
import com.google.devtools.build.lib.pkgcache.LoadingPhaseRunner;
import com.google.devtools.build.lib.pkgcache.LoadingPhaseRunner.LoadingResult;
import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
import com.google.devtools.build.lib.pkgcache.PackageManager;
import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader;
import com.google.devtools.build.lib.rules.test.BaselineCoverageAction;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.skyframe.DiffAwareness;
import com.google.devtools.build.lib.skyframe.PrecomputedValue;
import com.google.devtools.build.lib.skyframe.SequencedSkyframeExecutor;
import com.google.devtools.build.lib.syntax.Label;
import com.google.devtools.build.lib.syntax.Label.SyntaxException;
import com.google.devtools.build.lib.testutil.FoundationTestCase;
import com.google.devtools.build.lib.testutil.MoreAsserts;
import com.google.devtools.build.lib.testutil.TestConstants;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.util.BlazeClock;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.StringUtil;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.ModifiedFileSet;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.common.options.Options;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.OptionsParsingException;
import org.mockito.Mockito;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* Common test code that creates a BuildView instance.
*/
public abstract class BuildViewTestCase extends FoundationTestCase {
protected static final int LOADING_PHASE_THREADS = 20;
protected ConfiguredRuleClassProvider ruleClassProvider;
protected ConfigurationFactory configurationFactory;
protected BuildView view;
private SequencedSkyframeExecutor skyframeExecutor;
protected BlazeDirectories directories;
protected BinTools binTools;
// Note that these configurations are virtual (they use only VFS)
protected BuildConfigurationCollection masterConfig;
protected BuildConfiguration targetConfig; // "target" or "build" config
protected OptionsParser optionsParser;
private PackageCacheOptions packageCacheOptions;
protected MockToolsConfig mockToolsConfig;
protected WorkspaceStatusAction.Factory workspaceStatusActionFactory;
private MutableActionGraph mutableActionGraph;
@Override
protected void setUp() throws Exception {
super.setUp();
AnalysisMock mock = getAnalysisMock();
directories = new BlazeDirectories(outputBase, outputBase, rootDirectory);
binTools = BinTools.forUnitTesting(directories, TestConstants.EMBEDDED_TOOLS);
mockToolsConfig = new MockToolsConfig(rootDirectory, false);
mock.setupMockClient(mockToolsConfig);
configurationFactory = mock.createConfigurationFactory();
packageCacheOptions = parsePackageCacheOptions();
workspaceStatusActionFactory =
new AnalysisTestUtil.DummyWorkspaceStatusActionFactory(directories);
mutableActionGraph = new MapBasedActionGraph();
ruleClassProvider = getRuleClassProvider();
skyframeExecutor = SequencedSkyframeExecutor.create(reporter,
new PackageFactory(ruleClassProvider, getEnvironmentExtensions()),
new TimestampGranularityMonitor(BlazeClock.instance()), directories,
workspaceStatusActionFactory,
ruleClassProvider.getBuildInfoFactories(),
ImmutableSet.<Path>of(),
ImmutableList.<DiffAwareness.Factory>of(),
Predicates.<PathFragment>alwaysFalse(),
getPreprocessorFactorySupplier(),
ImmutableMap.<SkyFunctionName, SkyFunction>of(),
getPrecomputedValues()
);
skyframeExecutor.preparePackageLoading(
new PathPackageLocator(rootDirectory), ConstantRuleVisibility.PUBLIC, true, "",
UUID.randomUUID());
useConfiguration();
setUpSkyframe();
// Also initializes ResourceManager.
ResourceManager.instance().setAvailableResources(getStartingResources());
}
protected AnalysisMock getAnalysisMock() {
try {
Class<?> providerClass = Class.forName(TestConstants.TEST_ANALYSIS_MOCK);
Field instanceField = providerClass.getField("INSTANCE");
return (AnalysisMock) instanceField.get(null);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
/** Creates or retrieves the rule class provider used in this test. */
protected ConfiguredRuleClassProvider getRuleClassProvider() {
return TestRuleClassProvider.getRuleClassProvider();
}
protected Iterable<EnvironmentExtension> getEnvironmentExtensions() {
return ImmutableList.<EnvironmentExtension>of();
}
protected ImmutableList<PrecomputedValue.Injected> getPrecomputedValues() {
return ImmutableList.of();
}
protected Preprocessor.Factory.Supplier getPreprocessorFactorySupplier() {
return Preprocessor.Factory.Supplier.NullSupplier.INSTANCE;
}
protected ResourceSet getStartingResources() {
// Effectively disable ResourceManager by default.
return ResourceSet.createWithRamCpuIo(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
}
protected final BuildConfigurationCollection createConfigurations(String... args)
throws Exception {
optionsParser = OptionsParser.newOptionsParser(Iterables.concat(Arrays.asList(
ExecutionOptions.class,
BuildRequest.BuildRequestOptions.class),
ruleClassProvider.getConfigurationOptions()));
try {
List<String> configurationArgs = new ArrayList<>();
configurationArgs.add("--experimental_extended_sanity_checks");
configurationArgs.addAll(getAnalysisMock().getOptionOverrides());
optionsParser.parse(configurationArgs);
optionsParser.parse(args);
configurationFactory.forbidSanityCheck();
BuildOptions buildOptions = ruleClassProvider.createBuildOptions(optionsParser);
ensureTargetsVisited(buildOptions.getAllLabels().values());
BuildConfigurationKey key = new BuildConfigurationKey(
buildOptions, directories,
ImmutableMap.<String, String>of());
skyframeExecutor.invalidateConfigurationCollection();
return skyframeExecutor.createConfigurations(configurationFactory, key);
} catch (InvalidConfigurationException | OptionsParsingException e) {
throw new IllegalArgumentException(e);
}
}
protected Target getTarget(String label)
throws NoSuchPackageException, NoSuchTargetException,
Label.SyntaxException, InterruptedException {
return getTarget(Label.parseAbsolute(label));
}
protected Target getTarget(Label label)
throws NoSuchPackageException, NoSuchTargetException, InterruptedException {
return getPackageManager().getTarget(reporter, label);
}
private void setUpSkyframe() {
PathPackageLocator pkgLocator = PathPackageLocator.create(
packageCacheOptions.packagePath, reporter, rootDirectory, rootDirectory);
skyframeExecutor.preparePackageLoading(pkgLocator,
packageCacheOptions.defaultVisibility, true,
ruleClassProvider.getDefaultsPackageContent(optionsParser),
UUID.randomUUID());
skyframeExecutor.setDeletedPackages(ImmutableSet.copyOf(packageCacheOptions.deletedPackages));
}
protected void setPackageCacheOptions(String... options) throws Exception {
packageCacheOptions = parsePackageCacheOptions(options);
setUpSkyframe();
}
private PackageCacheOptions parsePackageCacheOptions(String... options) throws Exception {
OptionsParser parser = OptionsParser.newOptionsParser(PackageCacheOptions.class);
parser.parse("--default_visibility=public");
parser.parse(options);
return parser.getOptions(PackageCacheOptions.class);
}
/** Used by skyframe-only tests. */
protected SequencedSkyframeExecutor getSkyframeExecutor() {
return Preconditions.checkNotNull(skyframeExecutor);
}
protected PackageManager getPackageManager() {
return skyframeExecutor.getPackageManager();
}
protected AnalysisHooks getAnalysisHooks() {
return new AnalysisHooks() {
@Override
public PackageManager getPackageManager() {
return BuildViewTestCase.this.getPackageManager();
}
@Override
public ConfiguredTarget getExistingConfiguredTarget(Target target,
BuildConfiguration configuration) {
return view.getExistingConfiguredTarget(target, configuration);
}
};
}
/**
* Invalidates all existing packages.
* @throws InterruptedException
*/
protected void invalidatePackages() throws InterruptedException {
skyframeExecutor.invalidateFilesUnderPathForTesting(ModifiedFileSet.EVERYTHING_MODIFIED,
rootDirectory);
}
/**
* Sets host and target configuration using the specified options, falling back to the default
* options for unspecified ones, and recreates the build view.
*
* @throws IllegalArgumentException
*/
protected final void useConfiguration(String... args) throws Exception {
masterConfig = createConfigurations(args);
targetConfig = getTargetConfiguration();
createBuildView();
}
/**
* Creates BuildView using current hostConfig/targetConfig values.
* Ensures that hostConfig is either identical to the targetConfig or has
* 'host' short name.
*/
protected final void createBuildView() throws Exception {
Preconditions.checkNotNull(masterConfig);
Preconditions.checkState(getHostConfiguration() == getTargetConfiguration()
|| getHostConfiguration().getShortName().equals("host"),
"Host configuration %s does not have name 'host' "
+ "and does not match target configuration %s",
getHostConfiguration(), getTargetConfiguration());
String defaultsPackageContent = ruleClassProvider.getDefaultsPackageContent(optionsParser);
skyframeExecutor.setupDefaultPackage(defaultsPackageContent);
skyframeExecutor.dropConfiguredTargets();
view = new BuildView(directories, getPackageManager(), ruleClassProvider, skyframeExecutor,
binTools, null);
view.setConfigurationsForTesting(masterConfig);
view.setArtifactRoots(
ImmutableMap.of(PackageIdentifier.createInDefaultRepo(""), rootDirectory));
simulateLoadingPhase();
}
protected CachingAnalysisEnvironment getTestAnalysisEnvironment() {
return new CachingAnalysisEnvironment(view.getArtifactFactory(),
ArtifactOwner.NULL_OWNER, /*isSystemEnv=*/true, /*extendedSanityChecks*/false, reporter,
/*skyframeEnv=*/ null, /*actionsEnabled=*/true, binTools);
}
/**
* Allows access to the prerequisites of a configured target. This is currently used in some tests
* to reach into the internals of RuleCT for white box testing. In principle, this should not be
* used; instead tests should only assert on properties of the exposed provider instances and / or
* the action graph.
*/
protected Iterable<ConfiguredTarget> getDirectPrerequisites(ConfiguredTarget target) {
return view.getDirectPrerequisites(target);
}
/**
* Creates and returns a rule context that is equivalent to the one that was used to create the
* given configured target.
*/
protected RuleContext getRuleContext(ConfiguredTarget target) {
return new RuleContext.Builder(
new StubAnalysisEnvironment(), (Rule) target.getTarget(),
target.getConfiguration(), ruleClassProvider.getPrerequisiteValidator())
.setVisibility(NestedSetBuilder.<PackageSpecification>create(
Order.STABLE_ORDER, PackageSpecification.EVERYTHING))
.setPrerequisites(view.getPrerequisiteMapForTesting(target))
.setConfigConditions(ImmutableSet.<ConfigMatchingProvider>of())
.build();
}
/**
* Creates and returns a rule context to use for Skylark tests that is equivalent to the one
* that was used to create the given configured target.
*/
protected RuleContext getRuleContextForSkylark(ConfiguredTarget target) {
// TODO(bazel-team): we need this horrible workaround because CachingAnalysisEnvironment
// only works with StoredErrorEventListener despite the fact it accepts the interface
// ErrorEventListener, so it's not possible to create it with reporter.
// See BuildView.getRuleContextForTesting().
StoredEventHandler eventHandler = new StoredEventHandler() {
@Override
public synchronized void handle(Event e) {
super.handle(e);
reporter.handle(e);
}
};
return view.getRuleContextForTesting(target, eventHandler);
}
/**
* Allows access to the prerequisites of a configured target. This is currently used in some tests
* to reach into the internals of RuleCT for white box testing. In principle, this should not be
* used; instead tests should only assert on properties of the exposed provider instances and / or
* the action graph.
*/
protected List<? extends TransitiveInfoCollection> getPrerequisites(ConfiguredTarget target,
String attributeName) {
return getRuleContext(target).getConfiguredTargetMap().get(attributeName);
}
/**
* Allows access to the prerequisites of a configured target. This is currently used in some tests
* to reach into the internals of RuleCT for white box testing. In principle, this should not be
* used; instead tests should only assert on properties of the exposed provider instances and / or
* the action graph.
*/
protected <C extends TransitiveInfoProvider> Iterable<C> getPrerequisites(
ConfiguredTarget target, String attributeName, Class<C> classType) {
return AnalysisUtils.getProviders(getPrerequisites(target, attributeName), classType);
}
/**
* Allows access to the prerequisites of a configured target. This is currently used in some tests
* to reach into the internals of RuleCT for white box testing. In principle, this should not be
* used; instead tests should only assert on properties of the exposed provider instances and / or
* the action graph.
*/
protected ImmutableList<Artifact> getPrerequisiteArtifacts(
ConfiguredTarget target, String attributeName) {
Set<Artifact> result = new LinkedHashSet<>();
for (FileProvider provider : getPrerequisites(target, attributeName, FileProvider.class)) {
Iterables.addAll(result, provider.getFilesToBuild());
}
return ImmutableList.copyOf(result);
}
protected final Action getGeneratingAction(Artifact artifact) {
Preconditions.checkNotNull(artifact);
Action action = mutableActionGraph.getGeneratingAction(artifact);
if (action != null) {
return action;
}
return view.getActionGraph().getGeneratingAction(artifact);
}
protected void simulateLoadingPhase() {
try {
ensureTargetsVisited(targetConfig.getAllLabels().values());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected ActionsTestUtil actionsTestUtil() {
return new ActionsTestUtil(view.getActionGraph());
}
private Set<Target> getTargets(Iterable<Label> labels) throws InterruptedException,
NoSuchTargetException, NoSuchPackageException{
Set<Target> targets = Sets.newHashSet();
for (Label label : labels) {
targets.add(skyframeExecutor.getPackageManager().getTarget(reporter, label));
}
return targets;
}
// Get a MutableActionGraph for testing purposes.
protected MutableActionGraph getMutableActionGraph() {
return mutableActionGraph;
}
protected TransitivePackageLoader makeVisitor() {
setUpSkyframe();
return skyframeExecutor.pkgLoader();
}
/**
* Construct the containing package of the specified labels, and all of its transitive
* dependencies. This must be done prior to configuration, as the latter is intolerant of
* NoSuchTargetExceptions.
*/
protected boolean ensureTargetsVisited(TransitivePackageLoader visitor,
Collection<Label> targets, Collection<Label> labels, boolean keepGoing)
throws InterruptedException, NoSuchTargetException, NoSuchPackageException {
boolean success = visitor.sync(reporter,
ImmutableSet.copyOf(getTargets(targets)),
ImmutableSet.copyOf(labels),
keepGoing,
/*parallelThreads=*/4,
/*maxDepth=*/Integer.MAX_VALUE);
return success;
}
protected boolean ensureTargetsVisited(Collection<Label> labels)
throws InterruptedException, NoSuchTargetException, NoSuchPackageException {
return ensureTargetsVisited(makeVisitor(), ImmutableSet.<Label>of(), labels,
/*keepGoing=*/false);
}
protected boolean ensureTargetsVisited(Label label)
throws InterruptedException, NoSuchTargetException, NoSuchPackageException {
return ensureTargetsVisited(ImmutableList.of(label));
}
protected boolean ensureTargetsVisited(String... labels)
throws InterruptedException, NoSuchTargetException, NoSuchPackageException, SyntaxException {
List<Label> actualLabels = new ArrayList<>();
for (String label : labels) {
actualLabels.add(Label.parseAbsolute(label));
}
return ensureTargetsVisited(actualLabels);
}
/**
* Returns the ConfiguredTarget for the specified label, configured for the
* "build" (aka "target") configuration.
*/
protected ConfiguredTarget getConfiguredTarget(String label)
throws NoSuchPackageException, NoSuchTargetException,
Label.SyntaxException, InterruptedException {
return getConfiguredTarget(label, targetConfig);
}
/**
* Returns the ConfiguredTarget for the specified label, using the
* given build configuration.
*/
protected ConfiguredTarget getConfiguredTarget(String label, BuildConfiguration config)
throws NoSuchPackageException, NoSuchTargetException,
Label.SyntaxException, InterruptedException {
ensureTargetsVisited(label);
return view.getConfiguredTargetForTesting(getTarget(label), config);
}
/**
* Returns the ConfiguredTarget for the specified label, using the
* given build configuration.
*/
protected ConfiguredTarget getConfiguredTarget(Label label, BuildConfiguration config)
throws NoSuchPackageException, NoSuchTargetException, InterruptedException {
ensureTargetsVisited(label);
return view.getConfiguredTargetForTesting(getTarget(label), config);
}
/**
* Returns the ConfiguredTarget for the specified file label, configured for
* the "build" (aka "target") configuration.
*/
protected FileConfiguredTarget getFileConfiguredTarget(String label)
throws NoSuchPackageException, NoSuchTargetException,
Label.SyntaxException, InterruptedException {
return (FileConfiguredTarget) getConfiguredTarget(label, targetConfig);
}
/**
* Returns the ConfiguredTarget for the specified label, configured for
* the "host" configuration.
*/
protected ConfiguredTarget getHostConfiguredTarget(String label)
throws NoSuchPackageException, NoSuchTargetException,
Label.SyntaxException, InterruptedException {
return getConfiguredTarget(label, getHostConfiguration());
}
/**
* Returns the ConfiguredTarget for the specified file label, configured for
* the "host" configuration.
*/
protected FileConfiguredTarget getHostFileConfiguredTarget(String label)
throws NoSuchPackageException, NoSuchTargetException,
Label.SyntaxException, InterruptedException {
return (FileConfiguredTarget) getHostConfiguredTarget(label);
}
/**
* Create and return a configured scratch rule.
*
* @param packageName the package name ofthe rule.
* @param ruleName the name of the rule.
* @param lines the text of the rule.
* @return the configured target instance for the created rule.
* @throws IOException
* @throws Exception
*/
protected ConfiguredTarget scratchConfiguredTarget(String packageName,
String ruleName,
String... lines)
throws IOException, Exception {
return scratchConfiguredTarget(packageName, ruleName, targetConfig, lines);
}
/**
* Create and return a scratch rule.
*
* @param packageName the package name of the rule.
* @param ruleName the name of the rule.
* @param lines the text of the rule.
* @return the rule instance for the created rule.
* @throws IOException
* @throws Exception
*/
protected Rule scratchRule(String packageName, String ruleName, String... lines)
throws Exception {
scratchFile("/" + TestConstants.TEST_WORKSPACE_DIRECTORY + "/" + packageName + "/BUILD", lines);
return (Rule) getTarget("//" + packageName + ":" + ruleName);
}
/**
* Create and return a configured scratch rule.
*
* @param packageName the package name of the rule.
* @param ruleName the name of the rule.
* @param config the configuration to use to construct the configured rule.
* @param lines the text of the rule.
* @return the configured target instance for the created rule.
* @throws IOException
* @throws Exception
*/
protected ConfiguredTarget scratchConfiguredTarget(String packageName,
String ruleName,
BuildConfiguration config,
String... lines)
throws IOException, Exception {
Target rule = scratchRule(packageName, ruleName, lines);
if (ensureTargetsVisited(rule.getLabel())) {
return view.getConfiguredTargetForTesting(rule, config);
} else {
return null;
}
}
/**
* Check that configuration of the target named 'ruleName' in the
* specified BUILD file fails with an error message ending in
* 'expectedErrorMessage'.
*
* @param packageName the package name of the generated BUILD file
* @param ruleName the rule name for the rule in the generated BUILD file
* @param expectedErrorMessage the expected error message.
* @param lines the text of the rule.
* @return the found error.
*/
protected Event checkError(String packageName,
String ruleName,
String expectedErrorMessage,
String... lines) throws Exception {
eventCollector.clear();
reporter.removeHandler(failFastHandler); // expect errors
ConfiguredTarget target = scratchConfiguredTarget(packageName, ruleName, lines);
if (target != null) {
assertTrue("Rule '" + "//" + packageName + ":" + ruleName + "' did not contain an error",
view.hasErrors(target));
}
return assertContainsEvent(expectedErrorMessage);
}
/**
* Check that configuration of the target named 'ruleName' in the
* specified BUILD file reports a warning message ending in
* 'expectedWarningMessage', and that no errors were reported.
*
* @param packageName the package name of the generated BUILD file
* @param ruleName the rule name for the rule in the generated BUILD file
* @param expectedWarningMessage the expected warning message.
* @param lines the text of the rule.
* @return the found error.
*/
protected Event checkWarning(String packageName,
String ruleName,
String expectedWarningMessage,
String... lines) throws Exception {
eventCollector.clear();
ConfiguredTarget target = scratchConfiguredTarget(packageName, ruleName,
lines);
assertFalse("Rule '" + "//" + packageName + ":" + ruleName
+ "' did contain an error",
view.hasErrors(target));
return assertContainsEvent(expectedWarningMessage);
}
/**
* Given a collection of Artifacts, returns a corresponding set of strings of
* the form "[root] [relpath]", such as "bin x/libx.a". Such strings make
* assertions easier to write.
*
* <p>The returned set preserves the order of the input.
*/
protected Set<String> artifactsToStrings(Iterable<Artifact> artifacts) {
return AnalysisTestUtil.artifactsToStrings(masterConfig, artifacts);
}
/**
* Asserts that targetName's outputs are exactly expectedOuts.
*
* @param targetName The label of a rule.
* @param expectedOuts The labels of the expected outputs of the rule.
*/
protected void assertOuts(String targetName, String... expectedOuts) throws Exception {
Rule ruleTarget = (Rule) getTarget(targetName);
for (String expectedOut : expectedOuts) {
Target outTarget = getTarget(expectedOut);
if (!(outTarget instanceof OutputFile)) {
fail("Target " + outTarget + " is not an output");
assertSame(ruleTarget, ((OutputFile) outTarget).getGeneratingRule());
// This ensures that the output artifact is wired up in the action graph
getConfiguredTarget(expectedOut);
}
}
Collection<OutputFile> outs = ruleTarget.getOutputFiles();
assertEquals("Mismatched outputs: " + outs, expectedOuts.length, outs.size());
}
/**
* Asserts that there exists a configured target file for the given label.
*/
protected void assertConfiguredTargetExists(String label) throws Exception {
assertNotNull(getFileConfiguredTarget(label));
}
/**
* Assert that the first label and the second label are both generated
* by the same command.
*/
protected void assertSameGeneratingAction(String labelA, String labelB)
throws Exception {
assertSame(
"Action for " + labelA + " did not match " + labelB,
getGeneratingActionForLabel(labelA),
getGeneratingActionForLabel(labelB));
}
protected Artifact getSourceArtifact(PathFragment rootRelativePath, Root root) {
return view.getArtifactFactory().getSourceArtifact(rootRelativePath, root);
}
protected Artifact getSourceArtifact(String name) throws IOException {
return getSourceArtifact(new PathFragment(name),
Root.asSourceRoot(scratchDir("/" + TestConstants.TEST_WORKSPACE_DIRECTORY)));
}
/**
* Gets a derived artifact, creating it if necessary. {@code ArtifactOwner} should be a genuine
* {@link LabelAndConfiguration} corresponding to a {@link ConfiguredTarget}. If called from a
* test that does not exercise the analysis phase, the convenience methods {@link
* #getBinArtifactWithNoOwner} or {@link #getGenfilesArtifactWithNoOwner} should be used instead.
*/
protected Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root,
ArtifactOwner owner) {
return view.getArtifactFactory().getDerivedArtifact(rootRelativePath, root, owner);
}
/**
* Gets a derived Artifact for testing with path of the form
* root/owner.getPackageFragment()/packageRelativePath.
*
* @see #getDerivedArtifact(PathFragment, Root, ArtifactOwner)
*/
private Artifact getPackageRelativeDerivedArtifact(String packageRelativePath, Root root,
ArtifactOwner owner) {
return getDerivedArtifact(
owner.getLabel().getPackageFragment().getRelative(packageRelativePath),
root, owner);
}
/**
* Gets a derived Artifact for testing in the {@link BuildConfiguration#getBinDirectory()}. This
* method should only be used for tests that do no analysis, and so there is no ConfiguredTarget
* to own this artifact. If the test runs the analysis phase, {@link
* #getBinArtifact(String, ArtifactOwner)} or its convenience methods should be
* used instead.
*/
protected Artifact getBinArtifactWithNoOwner(String rootRelativePath) {
return getDerivedArtifact(new PathFragment(rootRelativePath), targetConfig.getBinDirectory(),
ActionsTestUtil.NULL_ARTIFACT_OWNER);
}
/**
* Gets a derived Artifact for testing in the subdirectory of the {@link
* BuildConfiguration#getBinDirectory()} corresponding to the package of {@code owner}. So
* to specify a file foo/foo.o owned by target //foo:foo, {@code packageRelativePath} should just
* be "foo.o".
*/
protected Artifact getBinArtifact(String packageRelativePath, String owner) {
return getBinArtifact(packageRelativePath, makeLabelAndConfiguration(owner));
}
/**
* Gets a derived Artifact for testing in the subdirectory of the {@link
* BuildConfiguration#getBinDirectory()} corresponding to the package of {@code owner}. So
* to specify a file foo/foo.o owned by target //foo:foo, {@code packageRelativePath} should just
* be "foo.o".
*/
protected Artifact getBinArtifact(String packageRelativePath, ConfiguredTarget owner) {
return getPackageRelativeDerivedArtifact(packageRelativePath,
owner.getConfiguration().getBinDirectory(), new ConfiguredTargetKey(owner));
}
/**
* Gets a derived Artifact for testing in the subdirectory of the {@link
* BuildConfiguration#getBinDirectory()} corresponding to the package of {@code owner}. So
* to specify a file foo/foo.o owned by target //foo:foo, {@code packageRelativePath} should just
* be "foo.o".
*/
private Artifact getBinArtifact(String packageRelativePath, ArtifactOwner owner) {
return getPackageRelativeDerivedArtifact(packageRelativePath, targetConfig.getBinDirectory(),
owner);
}
/**
* Gets a derived Artifact for testing in the {@link BuildConfiguration#getGenfilesDirectory()}.
* This method should only be used for tests that do no analysis, and so there is no
* ConfiguredTarget to own this artifact. If the test runs the analysis phase, {@link
* #getGenfilesArtifact(String, ArtifactOwner)} or its convenience methods should be used instead.
*/
protected Artifact getGenfilesArtifactWithNoOwner(String rootRelativePath) {
return getDerivedArtifact(new PathFragment(rootRelativePath),
targetConfig.getGenfilesDirectory(), ActionsTestUtil.NULL_ARTIFACT_OWNER);
}
/**
* Gets a derived Artifact for testing in the subdirectory of the {@link
* BuildConfiguration#getGenfilesDirectory()} corresponding to the package of {@code owner}.
* So to specify a file foo/foo.o owned by target //foo:foo, {@code packageRelativePath} should
* just be "foo.o".
*/
protected Artifact getGenfilesArtifact(String packageRelativePath, String owner) {
return getGenfilesArtifact(packageRelativePath, makeLabelAndConfiguration(owner));
}
/**
* Gets a derived Artifact for testing in the subdirectory of the {@link
* BuildConfiguration#getGenfilesDirectory()} corresponding to the package of {@code owner}.
* So to specify a file foo/foo.o owned by target //foo:foo, {@code packageRelativePath} should
* just be "foo.o".
*/
protected Artifact getGenfilesArtifact(String packageRelativePath, ConfiguredTarget owner) {
return getGenfilesArtifact(packageRelativePath, new ConfiguredTargetKey(owner));
}
/**
* Gets a derived Artifact for testing in the subdirectory of the {@link
* BuildConfiguration#getGenfilesDirectory()} corresponding to the package of {@code owner}.
* So to specify a file foo/foo.o owned by target //foo:foo, {@code packageRelativePath} should
* just be "foo.o".
*/
private Artifact getGenfilesArtifact(String packageRelativePath, ArtifactOwner owner) {
return getPackageRelativeDerivedArtifact(packageRelativePath,
targetConfig.getGenfilesDirectory(),
owner);
}
protected Action getGeneratingActionForLabel(String label) throws Exception {
return getGeneratingAction(getFileConfiguredTarget(label).getArtifact());
}
protected String fileName(Artifact artifact) {
return artifact.getExecPathString();
}
protected String fileName(FileConfiguredTarget target) {
return fileName(target.getArtifact());
}
protected String fileName(String name) throws Exception {
return fileName(getFileConfiguredTarget(name));
}
protected Path getOutputPath() {
return directories.getOutputPath();
}
/**
* Verifies whether the rule checks the 'srcs' attribute validity.
*
* <p>At the call site it expects the {@code packageName} to contain:
* <ol>
* <li>{@code :gvalid} - genrule that outputs a valid file</li>
* <li>{@code :ginvalid} - genrule that outputs an invalid file</li>
* <li>{@code :gmix} - genrule that outputs a mix of valid and invalid
* files</li>
* <li>{@code :valid} - rule of type {@code ruleType} that has a valid
* file, {@code :gvalid} and {@code :gmix} in the srcs</li>
* <li>{@code :invalid} - rule of type {@code ruleType} that has an invalid
* file, {@code :ginvalid} in the srcs</li>
* <li>{@code :mix} - rule of type {@code ruleType} that has a valid and an
* invalid file in the srcs</li>
* </ol>
*
* @param packageName the package where the rules under test are located
* @param ruleType rules under test types
* @param expectedTypes expected file types
*/
protected void assertSrcsValidityForRuleType(String packageName, String ruleType,
String expectedTypes) throws Exception {
reporter.removeHandler(failFastHandler);
String descriptionSingle = ruleType + " srcs file (expected " + expectedTypes + ")";
String descriptionPlural = ruleType + " srcs files (expected " + expectedTypes + ")";
String descriptionPluralFile = "(expected " + expectedTypes + ")";
assertSrcsValidity(ruleType, packageName + ":valid", false,
"need at least one " + descriptionSingle,
"'" + packageName + ":gvalid' does not produce any " + descriptionPlural,
"'" + packageName + ":gmix' does not produce any " + descriptionPlural);
assertSrcsValidity(ruleType, packageName + ":invalid", true,
"file '" + packageName + ":a.foo' is misplaced here " + descriptionPluralFile,
"'" + packageName + ":ginvalid' does not produce any " + descriptionPlural);
assertSrcsValidity(ruleType, packageName + ":mix", true,
"'" + packageName + ":a.foo' does not produce any " + descriptionPlural);
}
protected void assertSrcsValidity(String ruleType, String targetName, boolean expectedError,
String... expectedMessages) throws Exception{
ConfiguredTarget target = getConfiguredTarget(targetName);
if (expectedError) {
assertTrue(view.hasErrors(target));
for (String expectedMessage : expectedMessages) {
String message = "in srcs attribute of " + ruleType + " rule " + targetName + ": "
+ expectedMessage;
assertContainsEvent(message);
}
} else {
assertFalse(view.hasErrors(target));
for (String expectedMessage : expectedMessages) {
String message = "in srcs attribute of " + ruleType + " rule " + target.getLabel() + ": "
+ expectedMessage;
assertDoesNotContainEvent(message);
}
}
}
private static Label makeLabel(String label) {
try {
return Label.parseAbsolute(label);
} catch (SyntaxException e) {
throw new IllegalStateException(e);
}
}
private ConfiguredTargetKey makeLabelAndConfiguration(String label) {
return new ConfiguredTargetKey(makeLabel(label), targetConfig);
}
protected static List<String> actionInputsToPaths(Iterable<? extends ActionInput> actionInputs) {
return ImmutableList.copyOf(
Iterables.transform(actionInputs, new Function<ActionInput, String>() {
@Override
public String apply(ActionInput actionInput) {
return actionInput.getExecPathString();
}
}));
}
protected String readContentAsLatin1String(Artifact artifact) throws IOException {
return new String(FileSystemUtils.readContentAsLatin1(artifact.getPath()));
}
/**
* Asserts that the predecessor closure of the given Artifact contains the same elements as those
* in expectedPredecessors, plus the given common predecessors. Only looks at predecessors of
* the given file type.
*/
public void assertPredecessorClosureSameContents(
Artifact artifact, FileType fType, Iterable<String> common, String... expectedPredecessors) {
assertSameContentsWithCommonElements(
actionsTestUtil().predecessorClosureAsCollection(artifact, fType),
expectedPredecessors, common);
}
/**
* Utility method for asserting that the contents of one collection are the
* same as those in a second plus some set of common elements.
*/
protected void assertSameContentsWithCommonElements(Iterable<Artifact> artifacts,
Iterable<String> common, String... expectedInputs) {
MoreAsserts.assertSameContents(Iterables.concat(Lists.newArrayList(expectedInputs), common),
ActionsTestUtil.prettyArtifactNames(artifacts));
}
/**
* Utility method for asserting that the contents of one collection are the
* same as those in a second plus some set of common elements.
*/
protected void assertSameContentsWithCommonElements(Iterable<String> artifacts,
String[] expectedInputs, Iterable<String> common) {
MoreAsserts.assertSameContents(Iterables.concat(Lists.newArrayList(expectedInputs), common),
artifacts);
}
/**
* Utility method for asserting that a list contains the elements of a
* sublist This is useful for checking that a list of arguments contains a
* particular set of arguments.
*/
protected void assertContainsSublist(List<String> list, List<String> sublist) {
assertContainsSublist(null, list, sublist);
}
/**
* Utility method for asserting that a list contains the elements of a
* sublist This is useful for checking that a list of arguments contains a
* particular set of arguments.
*/
protected void assertContainsSublist(String message, List<String> list, List<String> sublist) {
if (Collections.indexOfSubList(list, sublist) == -1) {
fail((message == null ? "" : (message + ' '))
+ "expected: <" + list + "> to contain sublist: <" + sublist + ">");
}
}
protected void assertContainsSelfEdgeEvent(String label) {
assertContainsEvent(label + " [self-edge]");
}
protected Iterable<Artifact> collectRunfiles(ConfiguredTarget target) {
RunfilesProvider runfilesProvider = target.getProvider(RunfilesProvider.class);
if (runfilesProvider != null) {
return runfilesProvider.getDefaultRunfiles().getAllArtifacts();
} else {
return Runfiles.EMPTY.getAllArtifacts();
}
}
protected NestedSet<Artifact> getFilesToBuild(TransitiveInfoCollection target) {
return target.getProvider(FileProvider.class).getFilesToBuild();
}
/**
* Returns all extra actions for that target (no transitive actions), no duplicate actions.
*/
protected ImmutableList<Action> getExtraActionActions(ConfiguredTarget target) {
LinkedHashSet<Action> result = new LinkedHashSet<>();
for (Artifact artifact : getExtraActionArtifacts(target)) {
result.add(getGeneratingAction(artifact));
}
return ImmutableList.copyOf(result);
}
protected ImmutableList<Action> getFilesToBuildActions(ConfiguredTarget target) {
List<Action> result = new ArrayList<>();
for (Artifact artifact : getFilesToBuild(target)) {
Action action = getGeneratingAction(artifact);
if (action != null) {
result.add(action);
}
}
return ImmutableList.copyOf(result);
}
protected NestedSet<Artifact> getOutputGroup(
TransitiveInfoCollection target, String outputGroup) {
TopLevelArtifactProvider provider = target.getProvider(TopLevelArtifactProvider.class);
return provider == null
? NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER)
: provider.getOutputGroup(outputGroup);
}
protected ImmutableList<Artifact> getExtraActionArtifacts(ConfiguredTarget target) {
return target.getProvider(ExtraActionArtifactsProvider.class).getExtraActionArtifacts();
}
protected Artifact getExecutable(String label) throws Exception {
return getConfiguredTarget(label).getProvider(FilesToRunProvider.class).getExecutable();
}
protected Artifact getExecutable(TransitiveInfoCollection target) {
return target.getProvider(FilesToRunProvider.class).getExecutable();
}
protected ImmutableList<Artifact> getFilesToRun(TransitiveInfoCollection target) {
return target.getProvider(FilesToRunProvider.class).getFilesToRun();
}
protected ImmutableList<Artifact> getFilesToRun(Label label) throws Exception {
return getConfiguredTarget(label, targetConfig)
.getProvider(FilesToRunProvider.class).getFilesToRun();
}
protected ImmutableList<Artifact> getFilesToRun(String label) throws Exception {
return getConfiguredTarget(label).getProvider(FilesToRunProvider.class).getFilesToRun();
}
protected RunfilesSupport getRunfilesSupport(String label) throws Exception {
return getConfiguredTarget(label).getProvider(FilesToRunProvider.class).getRunfilesSupport();
}
protected RunfilesSupport getRunfilesSupport(TransitiveInfoCollection target) {
return target.getProvider(FilesToRunProvider.class).getRunfilesSupport();
}
protected static Runfiles getDefaultRunfiles(ConfiguredTarget target) {
return target.getProvider(RunfilesProvider.class).getDefaultRunfiles();
}
protected static Runfiles getDataRunfiles(ConfiguredTarget target) {
return target.getProvider(RunfilesProvider.class).getDataRunfiles();
}
protected BuildConfiguration getTargetConfiguration() {
return Iterables.getOnlyElement(masterConfig.getTargetConfigurations());
}
protected BuildConfiguration getDataConfiguration() {
return getTargetConfiguration().getConfiguration(ConfigurationTransition.DATA);
}
protected BuildConfiguration getHostConfiguration() {
return getTargetConfiguration().getConfiguration(ConfigurationTransition.HOST);
}
/**
* Returns an attribute value retriever for the given rule for the target configuration.
*/
protected AttributeMap attributes(RuleConfiguredTarget ct) {
return ConfiguredAttributeMapper.of(ct);
}
protected AttributeMap attributes(ConfiguredTarget rule) {
return attributes((RuleConfiguredTarget) rule);
}
protected AnalysisResult update(List<String> targets,
boolean keepGoing,
int loadingPhaseThreads,
boolean doAnalysis,
EventBus eventBus) throws Exception {
LoadingPhaseRunner.Options loadingOptions =
Options.getDefaults(LoadingPhaseRunner.Options.class);
loadingOptions.loadingPhaseThreads = loadingPhaseThreads;
BuildView.Options viewOptions = Options.getDefaults(BuildView.Options.class);
viewOptions.keepGoing = keepGoing;
LoadingPhaseRunner runner = new LoadingPhaseRunner(getPackageManager(),
Collections.unmodifiableSet(ruleClassProvider.getRuleClassMap().keySet()));
LoadingResult loadingResult = runner.execute(reporter, eventBus, targets, loadingOptions,
getTargetConfiguration().getAllLabels(),
viewOptions.keepGoing, /*determineTests=*/false, /*callback=*/null);
if (!doAnalysis) {
// TODO(bazel-team): What's supposed to happen in this case?
return null;
}
return view.update(loadingResult, masterConfig, viewOptions,
TopLevelArtifactContext.DEFAULT, reporter, eventBus);
}
protected static Predicate<Artifact> artifactNamed(final String name) {
return new Predicate<Artifact>() {
@Override
public boolean apply(Artifact input) {
return name.equals(input.prettyPrint());
}
};
}
/**
* Utility method for tests. Converts an array of strings into a set of labels.
*
* @param strings the set of strings to be converted to labels.
* @throws SyntaxException if there are any syntax errors in the strings.
*/
public static Set<Label> asLabelSet(String... strings) throws SyntaxException {
return asLabelSet(ImmutableList.copyOf(strings));
}
/**
* Utility method for tests. Converts an array of strings into a set of labels.
*
* @param strings the set of strings to be converted to labels.
* @throws SyntaxException if there are any syntax errors in the strings.
*/
public static Set<Label> asLabelSet(Iterable<String> strings) throws SyntaxException {
Set<Label> result = Sets.newTreeSet();
for (String s : strings) {
result.add(Label.parseAbsolute(s));
}
return result;
}
protected SpawnAction getGeneratingAction(ConfiguredTarget target,
String outputName) {
Artifact found = Iterables.find(getFilesToBuild(target),
artifactNamed(outputName));
return (SpawnAction) getGeneratingAction(found);
}
protected String getErrorMsgSingleFile(String attrName, String ruleType, String ruleName,
String depRuleName) {
return "in " + attrName + " attribute of " + ruleType + " rule " + ruleName + ": '"
+ depRuleName + "' must produce a single file";
}
protected String getErrorMsgNoGoodFiles(String attrName, String ruleType, String ruleName,
String depRuleName) {
return "in " + attrName + " attribute of " + ruleType + " rule " + ruleName + ": '"
+ depRuleName + "' does not produce any " + ruleType + " " + attrName + " files";
}
protected String getErrorMsgMisplacedFiles(String attrName, String ruleType, String ruleName,
String fileName) {
return "in " + attrName + " attribute of " + ruleType + " rule " + ruleName + ": file '"
+ fileName + "' is misplaced here";
}
protected String getErrorNonExistingTarget(String attrName, String ruleType, String ruleName,
String targetName) {
return "in " + attrName + " attribute of " + ruleType + " rule " + ruleName + ": target '"
+ targetName + "' does not exist";
}
protected String getErrorNonExistingRule(String attrName, String ruleType, String ruleName,
String targetName) {
return "in " + attrName + " attribute of " + ruleType + " rule " + ruleName + ": rule '"
+ targetName + "' does not exist";
}
protected String getErrorMsgMisplacedRules(String attrName, String ruleType, String ruleName,
String depRuleType, String depRuleName) {
return "in " + attrName + " attribute of " + ruleType + " rule " + ruleName + ": "
+ depRuleType + " rule '" + depRuleName + "' is misplaced here";
}
public static String getErrorMsgNonEmptyList(String attrName, String ruleType, String ruleName) {
return "non empty attribute '" + attrName + "' in '" + ruleType
+ "' rule '" + ruleName + "' has to have at least one value";
}
protected String getErrorMsgMandatoryMissing(String attrName, String ruleType) {
return "missing value for mandatory attribute '" + attrName + "' in '" + ruleType + "' rule";
}
protected String getErrorMsgWrongAttributeValue(String value, String... expected) {
return String.format("has to be one of %s instead of '%s'",
StringUtil.joinEnglishList(ImmutableSet.copyOf(expected), "or", "'"), value);
}
private class StubAnalysisEnvironment implements AnalysisEnvironment {
@Override
public void registerAction(Action... action) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasErrors() {
return false;
}
@Override
public Artifact getEmbeddedToolArtifact(String embeddedPath) {
throw new UnsupportedOperationException();
}
@Override
public Artifact getConstantMetadataArtifact(PathFragment rootRelativePath, Root root) {
throw new UnsupportedOperationException();
}
@Override
public EventHandler getEventHandler() {
return reporter;
}
@Override
public MiddlemanFactory getMiddlemanFactory() {
throw new UnsupportedOperationException();
}
@Override
public Action getLocalGeneratingAction(Artifact artifact) {
throw new UnsupportedOperationException();
}
@Override
public Iterable<Action> getRegisteredActions() {
throw new UnsupportedOperationException();
}
@Override
public SkyFunction.Environment getSkyframeEnv() {
throw new UnsupportedOperationException();
}
@Override
public Artifact getFilesetArtifact(PathFragment rootRelativePath, Root root) {
throw new UnsupportedOperationException();
}
@Override
public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
throw new UnsupportedOperationException();
}
@Override
public Artifact getStableWorkspaceStatusArtifact() {
throw new UnsupportedOperationException();
}
@Override
public Artifact getVolatileWorkspaceStatusArtifact() {
throw new UnsupportedOperationException();
}
@Override
public ImmutableList<Artifact> getBuildInfo(RuleContext ruleContext, BuildInfoKey key) {
throw new UnsupportedOperationException();
}
@Override
public ArtifactOwner getOwner() {
throw new UnsupportedOperationException();
}
@Override
public ImmutableSet<Artifact> getOrphanArtifacts() {
throw new UnsupportedOperationException();
}
}
protected Iterable<String> baselineCoverageArtifactBasenames(ConfiguredTarget target)
throws Exception {
Artifact baselineCoverage = Iterables.getOnlyElement(target
.getProvider(TopLevelArtifactProvider.class)
.getOutputGroup(TopLevelArtifactProvider.BASELINE_COVERAGE));
BaselineCoverageAction baselineAction =
(BaselineCoverageAction) getGeneratingAction(baselineCoverage);
EventBus eventBus = new EventBus();
Executor mockExecutor = Mockito.mock(Executor.class);
Mockito.when(mockExecutor.getEventBus()).thenReturn(eventBus);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
baselineAction.newDeterministicWriter(reporter, mockExecutor).writeOutputFile(bytes);
ImmutableList.Builder<String> basenames = ImmutableList.builder();
for (String line : new String(bytes.toByteArray(), StandardCharsets.UTF_8).split("\n")) {
if (line.startsWith("SF:")) {
String basename = line.substring(line.lastIndexOf("/") + 1);
basenames.add(basename);
}
}
return basenames.build();
}
}