diff options
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java new file mode 100644 index 0000000000..91297b50b1 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java @@ -0,0 +1,216 @@ +// Copyright 2017 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.pkgcache; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +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.devtools.build.lib.analysis.BlazeDirectories; +import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; +import com.google.devtools.build.lib.analysis.util.AnalysisMock; +import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.packages.NoSuchPackageException; +import com.google.devtools.build.lib.packages.Package; +import com.google.devtools.build.lib.skyframe.DiffAwareness; +import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy; +import com.google.devtools.build.lib.skyframe.PackageLookupValue.BuildFileName; +import com.google.devtools.build.lib.skyframe.PrecomputedValue; +import com.google.devtools.build.lib.skyframe.SequencedSkyframeExecutor; +import com.google.devtools.build.lib.skyframe.SkyValueDirtinessChecker; +import com.google.devtools.build.lib.skyframe.SkyframeExecutor; +import com.google.devtools.build.lib.syntax.SkylarkSemanticsOptions; +import com.google.devtools.build.lib.testutil.FoundationTestCase; +import com.google.devtools.build.lib.testutil.ManualClock; +import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor; +import com.google.devtools.build.lib.vfs.FileSystem; +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.lib.vfs.inmemoryfs.InMemoryFileSystem; +import com.google.devtools.common.options.OptionsParser; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for package loading. + */ +@RunWith(JUnit4.class) +public class BuildFileModificationTest extends FoundationTestCase { + + private ManualClock clock = new ManualClock(); + private AnalysisMock analysisMock; + private ConfiguredRuleClassProvider ruleClassProvider; + private SkyframeExecutor skyframeExecutor; + + @Before + public final void disableLogging() throws Exception { + Logger.getLogger("com.google.devtools").setLevel(Level.SEVERE); + } + + @Before + public final void initializeSkyframeExecutor() throws Exception { + analysisMock = AnalysisMock.get(); + ruleClassProvider = analysisMock.createRuleClassProvider(); + BlazeDirectories directories = + new BlazeDirectories(outputBase, outputBase, rootDirectory, analysisMock.getProductName()); + skyframeExecutor = + SequencedSkyframeExecutor.create( + analysisMock + .getPackageFactoryForTesting() + .create(ruleClassProvider, scratch.getFileSystem()), + directories, + null, /* BinTools */ + null, /* workspaceStatusActionFactory */ + ruleClassProvider.getBuildInfoFactories(), + ImmutableList.<DiffAwareness.Factory>of(), + Predicates.<PathFragment>alwaysFalse(), + AnalysisMock.get().getSkyFunctions(), + ImmutableList.<PrecomputedValue.Injected>of(), + ImmutableList.<SkyValueDirtinessChecker>of(), + analysisMock.getProductName(), + CrossRepositoryLabelViolationStrategy.ERROR, + ImmutableList.of(BuildFileName.BUILD_DOT_BAZEL, BuildFileName.BUILD)); + OptionsParser parser = OptionsParser.newOptionsParser( + PackageCacheOptions.class, SkylarkSemanticsOptions.class); + analysisMock.getInvocationPolicyEnforcer().enforce(parser); + setUpSkyframe( + parser.getOptions(PackageCacheOptions.class), + parser.getOptions(SkylarkSemanticsOptions.class)); + } + + private void setUpSkyframe( + PackageCacheOptions packageCacheOptions, + SkylarkSemanticsOptions skylarkSemanticsOptions) { + PathPackageLocator pkgLocator = PathPackageLocator.create( + null, packageCacheOptions.packagePath, reporter, rootDirectory, rootDirectory); + packageCacheOptions.showLoadingProgress = true; + packageCacheOptions.globbingThreads = 7; + skyframeExecutor.preparePackageLoading( + pkgLocator, + packageCacheOptions, + skylarkSemanticsOptions, + analysisMock.getDefaultsPackageContent(), + UUID.randomUUID(), + ImmutableMap.<String, String>of(), + ImmutableMap.<String, String>of(), + new TimestampGranularityMonitor(clock)); + skyframeExecutor.setDeletedPackages( + ImmutableSet.copyOf(packageCacheOptions.getDeletedPackages())); + } + + @Override + protected FileSystem createFileSystem() { + return new InMemoryFileSystem(clock); + } + + private void invalidatePackages() throws InterruptedException { + skyframeExecutor.invalidateFilesUnderPathForTesting( + reporter, ModifiedFileSet.EVERYTHING_MODIFIED, rootDirectory); + } + + private Package getPackage(String packageName) + throws NoSuchPackageException, InterruptedException { + return skyframeExecutor.getPackageManager().getPackage(reporter, + PackageIdentifier.createInMainRepo(packageName)); + } + + @Test + public void testCTimeChangeDetectedWithError() throws Exception { + reporter.removeHandler(failFastHandler); + Path build = scratch.file( + "a/BUILD", "cc_library(name='a', feet='stinky')".getBytes(StandardCharsets.ISO_8859_1)); + Package a1 = getPackage("a"); + assertTrue(a1.containsErrors()); + assertContainsEvent("//a:a: no such attribute 'feet'"); + eventCollector.clear(); + // writeContent updates mtime and ctime. Note that we keep the content length exactly the same. + clock.advanceMillis(1); + FileSystemUtils.writeContent( + build, "cc_library(name='a', srcs=['a.cc'])".getBytes(StandardCharsets.ISO_8859_1)); + + invalidatePackages(); + Package a2 = getPackage("a"); + assertNotSame(a1, a2); + assertFalse(a2.containsErrors()); + assertNoEvents(); + } + + @Test + public void testCTimeChangeDetected() throws Exception { + Path path = scratch.file( + "pkg/BUILD", "cc_library(name = 'foo')\n".getBytes(StandardCharsets.ISO_8859_1)); + Package oldPkg = getPackage("pkg"); + + // Note that the content has exactly the same length as before. + clock.advanceMillis(1); + FileSystemUtils.writeContent( + path, "cc_library(name = 'bar')\n".getBytes(StandardCharsets.ISO_8859_1)); + assertSame(oldPkg, getPackage("pkg")); // Change only becomes visible after invalidatePackages. + + invalidatePackages(); + + Package newPkg = getPackage("pkg"); + assertNotSame(oldPkg, newPkg); + assertNotNull(newPkg.getTarget("bar")); + } + + @Test + public void testLengthChangeDetected() throws Exception { + reporter.removeHandler(failFastHandler); + Path build = scratch.file( + "a/BUILD", "cc_library(name='a', srcs=['a.cc'])".getBytes(StandardCharsets.ISO_8859_1)); + Package a1 = getPackage("a"); + eventCollector.clear(); + // Note that we didn't advance the clock, so ctime/mtime is the same as before. + // However, the file contents are one byte longer. + FileSystemUtils.writeContent( + build, "cc_library(name='ab', srcs=['a.cc'])".getBytes(StandardCharsets.ISO_8859_1)); + + invalidatePackages(); + Package a2 = getPackage("a"); + assertNotSame(a1, a2); + assertNoEvents(); + } + + @Test + public void testTouchedBuildFileCausesReloadAfterSync() throws Exception { + Path path = scratch.file("pkg/BUILD", + "cc_library(name = 'foo')"); + + Package oldPkg = getPackage("pkg"); + // Change ctime to 1. + clock.advanceMillis(1); + path.setLastModifiedTime(1001); + assertSame(oldPkg, getPackage("pkg")); // change not yet visible + + invalidatePackages(); + + Package newPkg = getPackage("pkg"); + assertNotSame(oldPkg, newPkg); + } +} |