blob: 03b7c5109949857e48682fdd956ac58536b43b3a [file] [log] [blame]
// Copyright 2015 The Bazel Authors. 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.skyframe;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.skyframe.WalkableGraphUtils.exists;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.ActionKeyContext;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.ServerDirectories;
import com.google.devtools.build.lib.analysis.util.AnalysisMock;
import com.google.devtools.build.lib.analysis.util.DefaultBuildOptionsForTesting;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.packages.SkylarkSemanticsOptions;
import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction;
import com.google.devtools.build.lib.testutil.FoundationTestCase;
import com.google.devtools.build.lib.testutil.TestConstants;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.EvaluationContext;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.WalkableGraph;
import com.google.devtools.common.options.Options;
import java.io.IOException;
import java.util.UUID;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link PrepareDepsOfPatternsFunction}. */
@RunWith(JUnit4.class)
public class PrepareDepsOfPatternsFunctionSmartNegationTest extends FoundationTestCase {
private SkyframeExecutor skyframeExecutor;
private static final String ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING =
"config/blacklist.txt";
private static SkyKey getKeyForLabel(Label label) {
// Note that these tests used to look for TargetMarker SkyKeys before TargetMarker was
// inlined in TransitiveTraversalFunction. Because TargetMarker is now inlined, it doesn't
// appear in the graph. Instead, these tests now look for TransitiveTraversal keys.
return TransitiveTraversalValue.key(label);
}
@Before
public void setUp() throws Exception {
BlazeDirectories directories =
new BlazeDirectories(
new ServerDirectories(
getScratch().dir("/install"),
getScratch().dir("/output"),
getScratch().dir("/user_root")),
rootDirectory,
/* defaultSystemJavabase= */ null,
AnalysisMock.get().getProductName());
ConfiguredRuleClassProvider ruleClassProvider = AnalysisMock.get().createRuleClassProvider();
skyframeExecutor =
SequencedSkyframeExecutor.create(
AnalysisMock.get()
.getPackageFactoryBuilderForTesting(directories)
.build(ruleClassProvider, fileSystem),
fileSystem,
directories,
new ActionKeyContext(),
/*workspaceStatusActionFactory=*/ null,
AnalysisMock.get().createRuleClassProvider().getBuildInfoFactories(),
ImmutableList.of(),
AnalysisMock.get().getSkyFunctions(directories),
ImmutableList.of(),
BazelSkyframeExecutorConstants.HARDCODED_BLACKLISTED_PACKAGE_PREFIXES,
PathFragment.create(ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING),
BazelSkyframeExecutorConstants.CROSS_REPOSITORY_LABEL_VIOLATION_STRATEGY,
BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY,
BazelSkyframeExecutorConstants.ACTION_ON_IO_EXCEPTION_READING_BUILD_FILE,
DefaultBuildOptionsForTesting.getDefaultBuildOptionsForTest(ruleClassProvider));
TestConstants.processSkyframeExecutorForTesting(skyframeExecutor);
skyframeExecutor.preparePackageLoading(
new PathPackageLocator(
outputBase,
ImmutableList.of(Root.fromPath(rootDirectory)),
BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY),
Options.getDefaults(PackageCacheOptions.class),
Options.getDefaults(SkylarkSemanticsOptions.class),
AnalysisMock.get().getDefaultsPackageContent(),
UUID.randomUUID(),
ImmutableMap.<String, String>of(),
new TimestampGranularityMonitor(null));
skyframeExecutor.setActionEnv(ImmutableMap.<String, String>of());
skyframeExecutor.injectExtraPrecomputedValues(
ImmutableList.of(
PrecomputedValue.injected(
RepositoryDelegatorFunction.RESOLVED_FILE_INSTEAD_OF_WORKSPACE,
Optional.<RootedPath>absent()),
PrecomputedValue.injected(
RepositoryDelegatorFunction.REPOSITORY_OVERRIDES,
ImmutableMap.<RepositoryName, PathFragment>of()),
PrecomputedValue.injected(
RepositoryDelegatorFunction.DEPENDENCY_FOR_UNCONDITIONAL_FETCHING,
RepositoryDelegatorFunction.DONT_FETCH_UNCONDITIONALLY)));
scratch.file(ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING);
}
@Test
public void testRecursiveEvaluationFailsOnBadBuildFile() throws Exception {
// Given a well-formed package "@//foo" and a malformed package "@//foo/foo",
createFooAndFooFoo();
// Given a target pattern sequence consisting of a recursive pattern for "//foo/...",
ImmutableList<String> patternSequence = ImmutableList.of("//foo/...");
// When PrepareDepsOfPatternsFunction completes evaluation (with no error because it was
// recovered from),
WalkableGraph walkableGraph =
getGraphFromPatternsEvaluation(
patternSequence, /*successExpected=*/ true, /*keepGoing=*/ true);
// Then the graph contains package values for "@//foo" and "@//foo/foo",
assertThat(exists(PackageValue.key(PackageIdentifier.parse("@//foo")), walkableGraph)).isTrue();
assertThat(exists(PackageValue.key(PackageIdentifier.parse("@//foo/foo")), walkableGraph))
.isTrue();
// But the graph does not contain a value for the target "@//foo/foo:foofoo".
assertThat(exists(getKeyForLabel(Label.create("@//foo/foo", "foofoo")), walkableGraph))
.isFalse();
}
@Test
public void testNegativePatternBlocksPatternEvaluation() throws Exception {
// Given a well-formed package "//foo" and a malformed package "//foo/foo",
createFooAndFooFoo();
// Given a target pattern sequence consisting of a recursive pattern for "//foo/..." followed
// by a negative pattern for the malformed package,
ImmutableList<String> patternSequence = ImmutableList.of("//foo/...", "-//foo/foo/...");
assertSkipsFoo(patternSequence);
}
@Test
public void testBlacklistPatternBlocksPatternEvaluation() throws Exception {
// Given a well-formed package "//foo" and a malformed package "//foo/foo",
createFooAndFooFoo();
// Given a target pattern sequence consisting of a recursive pattern for "//foo/...",
ImmutableList<String> patternSequence = ImmutableList.of("//foo/...");
// and a blacklist for the malformed package,
scratch.overwriteFile(ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING, "foo/foo");
assertSkipsFoo(patternSequence);
}
private void assertSkipsFoo(ImmutableList<String> patternSequence) throws Exception {
// When PrepareDepsOfPatternsFunction completes evaluation (successfully),
WalkableGraph walkableGraph =
getGraphFromPatternsEvaluation(
patternSequence, /*successExpected=*/ true, /*keepGoing=*/ true);
// Then the graph contains a package value for "@//foo",
assertThat(exists(PackageValue.key(PackageIdentifier.parse("@//foo")), walkableGraph)).isTrue();
// But no package value for "@//foo/foo",
assertThat(exists(PackageValue.key(PackageIdentifier.parse("@//foo/foo")), walkableGraph))
.isFalse();
// And the graph does not contain a value for the target "@//foo/foo:foofoo".
Label label = Label.create("@//foo/foo", "foofoo");
assertThat(exists(getKeyForLabel(label), walkableGraph)).isFalse();
}
@Test
public void testNegativeNonTBDPatternsAreSkippedWithWarnings() throws Exception {
// Given a target pattern sequence with a negative non-TBD pattern,
ImmutableList<String> patternSequence = ImmutableList.of("-//foo/bar");
// When PrepareDepsOfPatternsFunction completes evaluation,
getGraphFromPatternsEvaluation(patternSequence, /*successExpected=*/ true, /*keepGoing=*/ true);
// Then a event is published that says that negative non-TBD patterns are skipped.
assertContainsEvent(
"Skipping '-//foo/bar, excludedSubdirs=[], filteringPolicy=[]': Negative target patterns of"
+ " types other than \"targets below directory\" are not permitted.");
}
// Helpers:
private WalkableGraph getGraphFromPatternsEvaluation(
ImmutableList<String> patternSequence, boolean successExpected, boolean keepGoing)
throws InterruptedException {
SkyKey independentTarget = PrepareDepsOfPatternsValue.key(patternSequence, "");
ImmutableList<SkyKey> singletonTargetPattern = ImmutableList.of(independentTarget);
// When PrepareDepsOfPatternsFunction completes evaluation,
EvaluationContext evaluationContext =
EvaluationContext.newBuilder()
.setKeepGoing(keepGoing)
.setNumThreads(100)
.setEventHander(new Reporter(new EventBus(), eventCollector))
.build();
EvaluationResult<SkyValue> evaluationResult =
skyframeExecutor.getDriverForTesting().evaluate(singletonTargetPattern, evaluationContext);
// The evaluation has no errors if success was expected.
assertThat(evaluationResult.hasError()).isNotEqualTo(successExpected);
return Preconditions.checkNotNull(evaluationResult.getWalkableGraph());
}
private void createFooAndFooFoo() throws IOException {
scratch.file(
"foo/BUILD", "genrule(name = 'foo',", " outs = ['out.txt'],", " cmd = 'touch $@')");
scratch.file(
"foo/foo/BUILD", "genrule(name = 'foofoo',", " This isn't even remotely grammatical.)");
}
}