aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
diff options
context:
space:
mode:
authorGravatar Ulf Adams <ulfjack@google.com>2015-02-27 16:40:00 +0000
committerGravatar Ulf Adams <ulfjack@google.com>2015-03-05 14:15:36 +0000
commit79f052118e94c418247c4ea0ba6fcd3a35ca7e0d (patch)
treecf1a84ab38217485fd9e3d5644a1363c6487eb99 /src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
parentccf19eaa0045a26f2654311966b9ece51354ede1 (diff)
Move BuildViewTestCase to the lib.analysis.util package.
-- MOS_MIGRATED_REVID=87345558
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java')
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java1355
1 files changed, 1355 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
new file mode 100644
index 0000000000..5a8677a3b1
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -0,0 +1,1355 @@
+// 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.util;
+
+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.AnalysisEnvironment;
+import com.google.devtools.build.lib.analysis.AnalysisHooks;
+import com.google.devtools.build.lib.analysis.AnalysisUtils;
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
+import com.google.devtools.build.lib.analysis.BuildView;
+import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult;
+import com.google.devtools.build.lib.analysis.CachingAnalysisEnvironment;
+import com.google.devtools.build.lib.analysis.ConfiguredAttributeMapper;
+import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.ExtraActionArtifactsProvider;
+import com.google.devtools.build.lib.analysis.FileConfiguredTarget;
+import com.google.devtools.build.lib.analysis.FileProvider;
+import com.google.devtools.build.lib.analysis.FilesToRunProvider;
+import com.google.devtools.build.lib.analysis.LabelAndConfiguration;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.Runfiles;
+import com.google.devtools.build.lib.analysis.RunfilesProvider;
+import com.google.devtools.build.lib.analysis.RunfilesSupport;
+import com.google.devtools.build.lib.analysis.TopLevelArtifactProvider;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
+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.ConfigurationFactory;
+import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
+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.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 view.getRuleContextForTesting(target, new StubAnalysisEnvironment());
+ }
+
+ /**
+ * 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,
+ AnalysisTestUtil.TOP_LEVEL_ARTIFACT_CONTEXT, 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();
+ }
+}