// 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 org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventCollector; import com.google.devtools.build.lib.events.EventKind; import com.google.devtools.build.lib.packages.ConstantRuleVisibility; import com.google.devtools.build.lib.packages.NoSuchThingException; import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.PackageFactory; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase; import com.google.devtools.build.lib.packages.util.PreprocessorUtils; import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader; import com.google.devtools.build.lib.testutil.ManualClock; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; 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.lib.vfs.inmemoryfs.InMemoryFileSystem; import org.junit.Before; import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; abstract public class SkyframeLabelVisitorTestCase extends PackageLoadingTestCase { // Convenience constants, so test args are readable vs true/false protected static final boolean KEEP_GOING = true; protected static final boolean EXPECT_ERROR = true; protected TransitivePackageLoader visitor = null; protected CustomInMemoryFs fs = new CustomInMemoryFs(new ManualClock()); protected SkyframeExecutor skyframeExecutor; protected PreprocessorUtils.MutableFactorySupplier preprocessorFactorySupplier = new PreprocessorUtils.MutableFactorySupplier(null); abstract public PackageFactory.EnvironmentExtension getPackageEnvironmentExtension(); @Override protected FileSystem createFileSystem() { return fs; } protected Collection assertNewBuildFileConflict() throws Exception { reporter.removeHandler(failFastHandler); // expect errors scratch.file("pkg/BUILD", "sh_library(name = 'x', deps = ['//pkg2:q/sub'])"); scratch.file("pkg2/BUILD", "sh_library(name = 'q/sub')"); assertLabelsVisited( ImmutableSet.of("//pkg:x", "//pkg2:q/sub"), ImmutableSet.of("//pkg:x"), !EXPECT_ERROR, !KEEP_GOING); scratch.file("pkg2/q/BUILD"); syncPackages(); EventCollector warningCollector = new EventCollector(EventKind.WARNING); reporter.addHandler(warningCollector); assertLabelsVisitedWithErrors(ImmutableSet.of("//pkg:x"), ImmutableSet.of("//pkg:x")); assertContainsEvent("Label '//pkg2:q/sub' crosses boundary of subpackage 'pkg2/q'"); assertContainsEvent("no such target '//pkg2:q/sub'"); Collection warnings = Lists.newArrayList(warningCollector); // Check stability (not redundant). assertLabelsVisitedWithErrors(ImmutableSet.of("//pkg:x"), ImmutableSet.of("//pkg:x")); assertContainsEvent("Label '//pkg2:q/sub' crosses boundary of subpackage 'pkg2/q'"); return warnings; } /** * Asserts all labels in expectedLabels are visited by walking * the dependency trees starting at startingLabels, and no other labels are visited. * *

Errors are expected. We keep going after errors are encountered. */ protected void assertLabelsVisitedWithErrors( Set expectedLabels, Set startingLabels) throws Exception { assertLabelsVisited(expectedLabels, startingLabels, EXPECT_ERROR, KEEP_GOING); } /** * Check that the expected targets were exactly those visited, and that the packages of these * expected targets were exactly those packages visited. */ protected void assertExpectedTargets( Set expectedLabels, boolean expectError, Set startingTargets) throws Exception { assertEquals(asLabelSet(expectedLabels), getVisitedTargets(startingTargets)); Set expectedPkgs = new HashSet<>(); for (Label label : getVisitedTargets(startingTargets)) { expectedPkgs.add(label.getPackageFragment()); } assertEquals(expectedPkgs, getVisitedPackageNames(startingTargets)); if (!expectError) { Set visitedPkgs = new HashSet<>(); for (Package pkg : getErrorFreeVisitedPackages(startingTargets)) { visitedPkgs.add(pkg.getNameFragment()); } assertEquals(expectedPkgs, visitedPkgs); } } /** * Asserts all labels in expectedLabels are visited by walking * the dependency trees starting at startingLabels, and no other labels are visited. * * @param expectedLabels The expected set of labels visited. * @param startingLabels Visit the transitive closure of each of these labels. * @param expectError Whether the visitation should succeed. * @param keepGoing Whether the visitation continues after encountering * errors. */ protected void assertLabelsVisited( Set expectedLabels, Set startingLabels, boolean expectError, boolean keepGoing) throws Exception { Set startingTargets = asTargetSet(startingLabels); // Spawn a lot of threads to help uncover concurrency issues boolean result = visitor.sync( reporter, startingTargets, ImmutableSet.