diff options
author | 2015-02-25 14:18:14 +0000 | |
---|---|---|
committer | 2015-02-25 14:18:14 +0000 | |
commit | d5baac0122a72209553a338ea28648d9184ca37e (patch) | |
tree | 8d73a4dbac72f1e2f99ec0910e6ba3c848b81fcb /src/test/java/com/google/devtools/build/lib/analysis/BuildViewTestCase.java | |
parent | 7d6a4f618488a778f4e5780a1ad6f8a3e27a6ec9 (diff) |
Open-source BuildViewTestCase and the infrastructure required by it, as well as
the tests under analysis/actions. They don't run yet, because the mock client
setup is still missing.
--
MOS_MIGRATED_REVID=87149625
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/analysis/BuildViewTestCase.java')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/analysis/BuildViewTestCase.java | 1341 |
1 files changed, 1341 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTestCase.java new file mode 100644 index 0000000000..7566f2f80f --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTestCase.java @@ -0,0 +1,1341 @@ +// 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.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) { + 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) { + 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(); + } +} |