blob: 85decff337c077244bb02e827697b95fad9e8ad3 [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.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.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,
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(),
ImmutableMap.<String, String>of(),
new TimestampGranularityMonitor(null));
skyframeExecutor.injectExtraPrecomputedValues(ImmutableList.of(PrecomputedValue.injected(
RepositoryDelegatorFunction.REPOSITORY_OVERRIDES,
ImmutableMap.<RepositoryName, PathFragment>of())));
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': 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,
EvaluationResult<SkyValue> evaluationResult =
skyframeExecutor
.getDriverForTesting()
.evaluate(
singletonTargetPattern,
keepGoing,
/*numThreads=*/ 100,
new Reporter(new EventBus(), eventCollector));
// 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.)");
}
}