| // 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 com.google.common.base.Preconditions; |
| 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.devtools.build.lib.analysis.util.BuildViewTestCase; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.cmdline.RepositoryName; |
| import com.google.devtools.build.lib.events.NullEventHandler; |
| import com.google.devtools.build.lib.packages.NoSuchTargetException; |
| import com.google.devtools.build.lib.packages.Package; |
| import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension; |
| import com.google.devtools.build.lib.packages.Rule; |
| import com.google.devtools.build.lib.packages.WorkspaceFileValue; |
| import com.google.devtools.build.lib.packages.WorkspaceFileValue.WorkspaceFileKey; |
| import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; |
| import com.google.devtools.build.lib.rules.repository.ManagedDirectoriesKnowledgeImpl; |
| import com.google.devtools.build.lib.rules.repository.ManagedDirectoriesKnowledgeImpl.ManagedDirectoriesListener; |
| import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils; |
| 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.Root; |
| import com.google.devtools.build.lib.vfs.RootedPath; |
| import com.google.devtools.build.skyframe.EvaluationResult; |
| import com.google.devtools.build.skyframe.Injectable; |
| import com.google.devtools.build.skyframe.SkyFunctionName; |
| import com.google.devtools.build.skyframe.SkyKey; |
| import com.google.devtools.build.skyframe.SkyValue; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.StarlarkSemantics; |
| import net.starlark.java.syntax.ParserInput; |
| import net.starlark.java.syntax.StarlarkFile; |
| import org.hamcrest.BaseMatcher; |
| import org.hamcrest.Description; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * Test for {@link WorkspaceFileFunction}. |
| */ |
| @RunWith(JUnit4.class) |
| public class WorkspaceFileFunctionTest extends BuildViewTestCase { |
| |
| private TestManagedDirectoriesKnowledge testManagedDirectoriesKnowledge; |
| |
| @Override |
| protected ManagedDirectoriesKnowledge getManagedDirectoriesKnowledge() { |
| testManagedDirectoriesKnowledge = new TestManagedDirectoriesKnowledge(); |
| return testManagedDirectoriesKnowledge; |
| } |
| |
| @Override |
| protected Iterable<EnvironmentExtension> getEnvironmentExtensions() { |
| return ImmutableList.of(); |
| } |
| |
| private static Label getLabelMapping(Package pkg, String name) throws NoSuchTargetException { |
| return (Label) ((Rule) pkg.getTarget(name)).getAttr("actual"); |
| } |
| |
| private RootedPath createWorkspaceFile(String... contents) throws IOException { |
| Path workspacePath = scratch.overwriteFile("WORKSPACE", contents); |
| return RootedPath.toRootedPath( |
| Root.fromPath(workspacePath.getParentDirectory()), |
| PathFragment.create(workspacePath.getBaseName())); |
| } |
| |
| // Dummy hamcrest matcher that match the function name of a skykey |
| static class SkyKeyMatchers extends BaseMatcher<SkyKey> { |
| private final SkyFunctionName functionName; |
| |
| public SkyKeyMatchers(SkyFunctionName functionName) { |
| this.functionName = functionName; |
| } |
| @Override |
| public boolean matches(Object item) { |
| if (item instanceof SkyKey) { |
| return ((SkyKey) item).functionName().equals(functionName); |
| } |
| return false; |
| } |
| |
| @Override |
| public void describeTo(Description description) {} |
| } |
| |
| private <T extends SkyValue> EvaluationResult<T> eval(SkyKey key) throws InterruptedException { |
| getSkyframeExecutor() |
| .invalidateFilesUnderPathForTesting( |
| reporter, |
| ModifiedFileSet.builder().modify(PathFragment.create("WORKSPACE")).build(), |
| Root.fromPath(rootDirectory)); |
| return SkyframeExecutorTestUtils.evaluate( |
| getSkyframeExecutor(), key, /*keepGoing=*/ false, reporter); |
| } |
| |
| @Test |
| public void testLoadToChunkMapSimple() throws Exception { |
| scratch.file("a.bzl", "a = 'a'"); |
| scratch.file("b.bzl", "b = 'b'"); |
| scratch.file("BUILD", ""); |
| RootedPath workspace = |
| createWorkspaceFile( |
| "workspace(name = 'good')", |
| "load('//:a.bzl', 'a')", |
| "x = 1 #for chunk break", |
| "load('//:b.bzl', 'b')"); |
| SkyKey key1 = WorkspaceFileValue.key(workspace, 1); |
| EvaluationResult<WorkspaceFileValue> result1 = eval(key1); |
| WorkspaceFileValue value1 = result1.get(key1); |
| assertThat(value1.getLoadToChunkMap()).containsEntry("//:a.bzl", 1); |
| |
| SkyKey key2 = WorkspaceFileValue.key(workspace, 2); |
| EvaluationResult<WorkspaceFileValue> result2 = eval(key2); |
| WorkspaceFileValue value2 = result2.get(key2); |
| assertThat(value2.getLoadToChunkMap()).containsEntry("//:a.bzl", 1); |
| assertThat(value2.getLoadToChunkMap()).containsEntry("//:b.bzl", 2); |
| } |
| |
| @Test |
| public void testLoadToChunkMapDoesNotOverrideDuplicate() throws Exception { |
| scratch.file("a.bzl", "a = 'a'"); |
| scratch.file("BUILD", ""); |
| RootedPath workspace = |
| createWorkspaceFile( |
| "workspace(name = 'good')", |
| "load('//:a.bzl', 'a')", |
| "x = 1 #for chunk break", |
| "load('//:a.bzl', 'a')"); |
| SkyKey key1 = WorkspaceFileValue.key(workspace, 1); |
| EvaluationResult<WorkspaceFileValue> result1 = eval(key1); |
| WorkspaceFileValue value1 = result1.get(key1); |
| assertThat(value1.getLoadToChunkMap()).containsEntry("//:a.bzl", 1); |
| |
| SkyKey key2 = WorkspaceFileValue.key(workspace, 2); |
| EvaluationResult<WorkspaceFileValue> result2 = eval(key2); |
| WorkspaceFileValue value2 = result2.get(key2); |
| assertThat(value2.getLoadToChunkMap()).containsEntry("//:a.bzl", 1); |
| assertThat(value2.getLoadToChunkMap()).doesNotContainEntry("//:a.bzl", 2); |
| } |
| |
| @Test |
| public void testRepositoryMappingInChunks() throws Exception { |
| scratch.file("b.bzl", "b = 'b'"); |
| scratch.file("BUILD", ""); |
| RootedPath workspace = |
| createWorkspaceFile( |
| "workspace(name = 'good')", |
| "local_repository(name = 'a', path = '../a', repo_mapping = {'@x' : '@y'})", |
| "load('//:b.bzl', 'b')", |
| "local_repository(name = 'b', path = '../b', repo_mapping = {'@x' : '@y'})"); |
| RepositoryName a = RepositoryName.create("@a"); |
| RepositoryName b = RepositoryName.create("@b"); |
| RepositoryName x = RepositoryName.create("@x"); |
| RepositoryName y = RepositoryName.create("@y"); |
| RepositoryName good = RepositoryName.create("@good"); |
| RepositoryName main = RepositoryName.create("@"); |
| |
| SkyKey key0 = WorkspaceFileValue.key(workspace, 0); |
| EvaluationResult<WorkspaceFileValue> result0 = eval(key0); |
| WorkspaceFileValue value0 = result0.get(key0); |
| assertThat(value0.getRepositoryMapping()).containsEntry(a, ImmutableMap.of(x, y, good, main)); |
| |
| SkyKey key1 = WorkspaceFileValue.key(workspace, 1); |
| EvaluationResult<WorkspaceFileValue> result1 = eval(key1); |
| WorkspaceFileValue value1 = result1.get(key1); |
| assertThat(value1.getRepositoryMapping()).containsEntry(a, ImmutableMap.of(x, y, good, main)); |
| assertThat(value1.getRepositoryMapping()).containsEntry(b, ImmutableMap.of(x, y, good, main)); |
| } |
| |
| @Test |
| public void setTestManagedDirectoriesKnowledge() throws Exception { |
| StarlarkSemantics semantics = getStarlarkSemantics(); |
| Injectable injectable = getSkyframeExecutor().injectable(); |
| try { |
| TestManagedDirectoriesListener listener = new TestManagedDirectoriesListener(); |
| ManagedDirectoriesKnowledgeImpl knowledge = new ManagedDirectoriesKnowledgeImpl(listener); |
| |
| RepositoryName one = RepositoryName.create("@repo1"); |
| RepositoryName two = RepositoryName.create("@repo2"); |
| RepositoryName three = RepositoryName.create("@repo3"); |
| |
| PathFragment pf1 = PathFragment.create("dir1"); |
| PathFragment pf2 = PathFragment.create("dir2"); |
| PathFragment pf3 = PathFragment.create("dir3"); |
| |
| assertThat(knowledge.getManagedDirectories(one)).isEmpty(); |
| assertThat(knowledge.getOwnerRepository(pf1)).isNull(); |
| |
| WorkspaceFileValue workspaceFileValue = createWorkspaceFileValueForTest(); |
| boolean isChanged = knowledge.workspaceHeaderReloaded(null, workspaceFileValue); |
| |
| assertThat(isChanged).isTrue(); |
| assertThat(listener.getRepositoryNames()).containsExactly(one, two); |
| |
| assertThat(knowledge.getManagedDirectories(one)).containsExactly(pf1, pf2); |
| assertThat(knowledge.getManagedDirectories(two)).containsExactly(pf3); |
| assertThat(knowledge.getManagedDirectories(three)).isEmpty(); |
| |
| assertThat(knowledge.getOwnerRepository(pf1)).isEqualTo(one); |
| assertThat(knowledge.getOwnerRepository(pf2)).isEqualTo(one); |
| assertThat(knowledge.getOwnerRepository(pf3)).isEqualTo(two); |
| |
| // Nothing changed, let's test the behavior. |
| listener.reset(); |
| isChanged = knowledge.workspaceHeaderReloaded(workspaceFileValue, workspaceFileValue); |
| assertThat(isChanged).isFalse(); |
| assertThat(listener.getRepositoryNames()).containsExactly(one, two); |
| |
| assertThat(knowledge.getManagedDirectories(one)).containsExactly(pf1, pf2); |
| assertThat(knowledge.getManagedDirectories(two)).containsExactly(pf3); |
| assertThat(knowledge.getManagedDirectories(three)).isEmpty(); |
| |
| assertThat(knowledge.getOwnerRepository(pf1)).isEqualTo(one); |
| assertThat(knowledge.getOwnerRepository(pf2)).isEqualTo(one); |
| assertThat(knowledge.getOwnerRepository(pf3)).isEqualTo(two); |
| } finally { |
| PrecomputedValue.STARLARK_SEMANTICS.set(injectable, semantics); |
| } |
| } |
| |
| @Test |
| public void testManagedDirectories() throws Exception { |
| StarlarkSemantics semantics = getStarlarkSemantics(); |
| Injectable injectable = getSkyframeExecutor().injectable(); |
| try { |
| createWorkspaceFileValueForTest(); |
| |
| // Test intentionally introduces errors. |
| reporter.removeHandler(failFastHandler); |
| |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': 'dir1', '@repo2': ['dir3']}", |
| "managed_directories attribute value should be of the type attr.string_list_dict()," |
| + " mapping repository name to the list of managed directories."); |
| |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': ['dir1'], '@repo2': ['dir1']}", |
| "managed_directories attribute should not contain multiple (or duplicate) repository" |
| + " mappings for the same directory ('dir1')."); |
| |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': ['']}", "Expected managed directory path to be non-empty string."); |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': ['/abc']}", |
| "Expected managed directory path ('/abc') to be relative to the workspace root."); |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': ['../abc']}", |
| "Expected managed directory path ('../abc') to be under the workspace root."); |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': ['a/b', 'a/b']}", |
| "managed_directories attribute should not contain multiple (or duplicate)" |
| + " repository mappings for the same directory ('a/b')."); |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': [], '@repo1': [] }", "dictionary expression has duplicate key: \"@repo1\""); |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': ['a/b'], '@repo2': ['a/b/c/..'] }", |
| "managed_directories attribute should not contain multiple (or duplicate)" |
| + " repository mappings for the same directory ('a/b/c/..')."); |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': ['a'], '@repo2': ['a/b'] }", |
| "managed_directories attribute value can not contain nested mappings." |
| + " 'a/b' is a descendant of 'a'."); |
| assertManagedDirectoriesParsingError( |
| "{'@repo1': ['a/b'], '@repo2': ['a'] }", |
| "managed_directories attribute value can not contain nested mappings." |
| + " 'a/b' is a descendant of 'a'."); |
| |
| assertManagedDirectoriesParsingError( |
| "{'repo1': []}", |
| "Cannot parse repository name 'repo1'. Repository name should start with '@'."); |
| } finally { |
| PrecomputedValue.STARLARK_SEMANTICS.set(injectable, semantics); |
| } |
| } |
| |
| private WorkspaceFileValue createWorkspaceFileValueForTest() throws Exception { |
| WorkspaceFileValue workspaceFileValue = |
| parseWorkspaceFileValue( |
| "workspace(", |
| " name = 'rr',", |
| " managed_directories = {'@repo1': ['dir1', 'dir2'], '@repo2': ['dir3/dir1/..']}", |
| ")"); |
| ImmutableMap<PathFragment, RepositoryName> managedDirectories = |
| workspaceFileValue.getManagedDirectories(); |
| assertThat(managedDirectories).isNotNull(); |
| assertThat(managedDirectories).hasSize(3); |
| assertThat(managedDirectories) |
| .containsExactly( |
| PathFragment.create("dir1"), RepositoryName.create("@repo1"), |
| PathFragment.create("dir2"), RepositoryName.create("@repo1"), |
| PathFragment.create("dir3"), RepositoryName.create("@repo2")); |
| return workspaceFileValue; |
| } |
| |
| private void assertManagedDirectoriesParsingError( |
| String managedDirectoriesValue, String expectedError) throws Exception { |
| parseWorkspaceFileValueWithError( |
| expectedError, |
| "workspace(", |
| " name = 'rr',", |
| " managed_directories = " + managedDirectoriesValue, |
| ")"); |
| } |
| |
| private WorkspaceFileValue parseWorkspaceFileValue(String... lines) throws Exception { |
| WorkspaceFileValue workspaceFileValue = parseWorkspaceFileValueImpl(lines); |
| Package pkg = workspaceFileValue.getPackage(); |
| if (pkg.containsErrors()) { |
| throw new RuntimeException( |
| Preconditions.checkNotNull(Iterables.getFirst(eventCollector, null)).getMessage()); |
| } |
| return workspaceFileValue; |
| } |
| |
| private void parseWorkspaceFileValueWithError(String expectedError, String... lines) |
| throws Exception { |
| WorkspaceFileValue workspaceFileValue = parseWorkspaceFileValueImpl(lines); |
| Package pkg = workspaceFileValue.getPackage(); |
| assertThat(pkg.containsErrors()).isTrue(); |
| assertContainsEvent(expectedError); |
| } |
| |
| private WorkspaceFileValue parseWorkspaceFileValueImpl(String[] lines) |
| throws IOException, InterruptedException { |
| RootedPath workspaceFile = createWorkspaceFile(lines); |
| WorkspaceFileKey key = WorkspaceFileValue.key(workspaceFile); |
| EvaluationResult<WorkspaceFileValue> result = eval(key); |
| return result.get(key); |
| } |
| |
| @Test |
| public void testInvalidRepo() throws Exception { |
| // Test intentionally introduces errors. |
| reporter.removeHandler(failFastHandler); |
| |
| RootedPath workspacePath = createWorkspaceFile("workspace(name = 'foo$')"); |
| SkyKey key = ExternalPackageFunction.key(workspacePath); |
| EvaluationResult<PackageValue> evaluationResult = eval(key); |
| Package pkg = evaluationResult.get(key).getPackage(); |
| assertThat(pkg.containsErrors()).isTrue(); |
| assertContainsEvent("foo$ is not a legal workspace name"); |
| } |
| |
| @Test |
| public void testBindFunction() throws Exception { |
| String[] lines = {"bind(name = 'foo/bar',", "actual = '//foo:bar')"}; |
| RootedPath workspacePath = createWorkspaceFile(lines); |
| |
| SkyKey key = ExternalPackageFunction.key(workspacePath); |
| EvaluationResult<PackageValue> evaluationResult = eval(key); |
| Package pkg = evaluationResult.get(key).getPackage(); |
| assertThat(getLabelMapping(pkg, "foo/bar")) |
| .isEqualTo(Label.parseAbsolute("//foo:bar", ImmutableMap.of())); |
| assertNoEvents(); |
| } |
| |
| @Test |
| public void testBindArgsReversed() throws Exception { |
| String[] lines = {"bind(actual = '//foo:bar', name = 'foo/bar')"}; |
| RootedPath workspacePath = createWorkspaceFile(lines); |
| |
| SkyKey key = ExternalPackageFunction.key(workspacePath); |
| EvaluationResult<PackageValue> evaluationResult = eval(key); |
| Package pkg = evaluationResult.get(key).getPackage(); |
| assertThat(getLabelMapping(pkg, "foo/bar")) |
| .isEqualTo(Label.parseAbsolute("//foo:bar", ImmutableMap.of())); |
| assertNoEvents(); |
| } |
| |
| @Test |
| public void testNonExternalBinding() throws Exception { |
| // Test intentionally introduces errors. |
| reporter.removeHandler(failFastHandler); |
| |
| // name must be a valid label name. |
| String[] lines = {"bind(name = 'foo:bar', actual = '//bar/baz')"}; |
| RootedPath workspacePath = createWorkspaceFile(lines); |
| |
| SkyKey key = ExternalPackageFunction.key(workspacePath); |
| EvaluationResult<PackageValue> evaluationResult = eval(key); |
| Package pkg = evaluationResult.get(key).getPackage(); |
| assertThat(pkg.containsErrors()).isTrue(); |
| assertContainsEvent("target names may not contain ':'"); |
| } |
| |
| @Test |
| public void testWorkspaceFileParsingError() throws Exception { |
| // Test intentionally introduces errors. |
| reporter.removeHandler(failFastHandler); |
| |
| // //external:bar:baz is not a legal package. |
| String[] lines = {"bind(name = 'foo/bar', actual = '//external:bar:baz')"}; |
| RootedPath workspacePath = createWorkspaceFile(lines); |
| |
| SkyKey key = ExternalPackageFunction.key(workspacePath); |
| EvaluationResult<PackageValue> evaluationResult = eval(key); |
| Package pkg = evaluationResult.get(key).getPackage(); |
| assertThat(pkg.containsErrors()).isTrue(); |
| assertContainsEvent("target names may not contain ':'"); |
| } |
| |
| @Test |
| public void testNoWorkspaceFile() throws Exception { |
| // Create and immediately delete to make sure we got the right file. |
| RootedPath workspacePath = createWorkspaceFile(); |
| workspacePath.asPath().delete(); |
| |
| SkyKey key = ExternalPackageFunction.key(workspacePath); |
| EvaluationResult<PackageValue> evaluationResult = eval(key); |
| Package pkg = evaluationResult.get(key).getPackage(); |
| assertThat(pkg.containsErrors()).isFalse(); |
| assertNoEvents(); |
| } |
| |
| @Test |
| public void testListBindFunction() throws Exception { |
| String[] lines = { |
| "L = ['foo', 'bar']", "bind(name = '%s/%s' % (L[0], L[1]),", "actual = '//foo:bar')" |
| }; |
| RootedPath workspacePath = createWorkspaceFile(lines); |
| |
| SkyKey key = ExternalPackageFunction.key(workspacePath); |
| EvaluationResult<PackageValue> evaluationResult = eval(key); |
| Package pkg = evaluationResult.get(key).getPackage(); |
| assertThat(getLabelMapping(pkg, "foo/bar")) |
| .isEqualTo(Label.parseAbsolute("//foo:bar", ImmutableMap.of())); |
| assertNoEvents(); |
| } |
| |
| @Test |
| public void testWorkspaceFileValueListener() throws Exception { |
| // Normally, syscalls cache is reset in the sync() method of the SkyframeExecutor, before |
| // diffing. |
| // But here we are calling only actual diffing part, exposed for testing: |
| // handleDiffsForTesting(), so we better turn off the syscalls cache. |
| skyframeExecutor.turnOffSyscallCacheForTesting(); |
| |
| createWorkspaceFile("workspace(name = 'old')"); |
| skyframeExecutor.handleDiffsForTesting(NullEventHandler.INSTANCE); |
| assertThat(testManagedDirectoriesKnowledge.getLastWorkspaceName()).isEqualTo("old"); |
| assertThat(testManagedDirectoriesKnowledge.getCnt()).isEqualTo(1); |
| |
| createWorkspaceFile("workspace(name = 'changed')"); |
| skyframeExecutor.handleDiffsForTesting(NullEventHandler.INSTANCE); |
| assertThat(testManagedDirectoriesKnowledge.getLastWorkspaceName()).isEqualTo("changed"); |
| assertThat(testManagedDirectoriesKnowledge.getCnt()).isEqualTo(2); |
| } |
| |
| @Test |
| public void testDoNotSymlinkInExecroot() throws Exception { |
| StarlarkSemantics semantics = getStarlarkSemantics(); |
| Injectable injectable = getSkyframeExecutor().injectable(); |
| |
| try { |
| StarlarkSemantics semanticsWithNinjaActions = |
| StarlarkSemantics.builder() |
| .setBool(BuildLanguageOptions.EXPERIMENTAL_NINJA_ACTIONS, true) |
| .build(); |
| PrecomputedValue.STARLARK_SEMANTICS.set(injectable, semanticsWithNinjaActions); |
| |
| assertThat( |
| parseWorkspaceFileValue("toplevel_output_directories(paths = [\"out\"])") |
| .getDoNotSymlinkInExecrootPaths()) |
| .containsExactly("out"); |
| assertThat( |
| parseWorkspaceFileValue( |
| "toplevel_output_directories(paths = [\"out\", \"one more with" |
| + " space\"])") |
| .getDoNotSymlinkInExecrootPaths()) |
| .containsExactly("out", "one more with space"); |
| // Empty sequence is allowed. |
| assertThat( |
| parseWorkspaceFileValue("toplevel_output_directories(paths = [])") |
| .getDoNotSymlinkInExecrootPaths()) |
| .isEmpty(); |
| |
| // Test now intentionally introduces errors. |
| reporter.removeHandler(failFastHandler); |
| |
| parseWorkspaceFileValueWithError( |
| "toplevel_output_directories should not " |
| + "contain duplicate values: \"out\" is specified more then once.", |
| "toplevel_output_directories(paths = [\"out\", \"out\"])"); |
| parseWorkspaceFileValueWithError( |
| "toplevel_output_directories can only accept " |
| + "top level directories under workspace, \"out/subdir\" " |
| + "can not be specified as an attribute.", |
| "toplevel_output_directories(paths = [\"out/subdir\"])"); |
| parseWorkspaceFileValueWithError( |
| "Empty path can not be passed to " + "toplevel_output_directories.", |
| "toplevel_output_directories(paths = [\"\"])"); |
| parseWorkspaceFileValueWithError( |
| "toplevel_output_directories can only " |
| + "accept top level directories under workspace, \"/usr/local/bin\" " |
| + "can not be specified as an attribute.", |
| "toplevel_output_directories(paths = [\"/usr/local/bin\"])"); |
| } finally { |
| PrecomputedValue.STARLARK_SEMANTICS.set(injectable, semantics); |
| } |
| } |
| |
| @Test |
| public void testMangledExternalWorkspaceFileIsIgnored() throws Exception { |
| scratch.file("secondary/WORKSPACE", "garbage"); |
| RootedPath workspace = |
| createWorkspaceFile( |
| "workspace(name = 'good')", |
| "local_repository(name = \"secondary\", path = \"./secondary/\")"); |
| |
| SkyKey key1 = WorkspaceFileValue.key(workspace, 1); |
| EvaluationResult<WorkspaceFileValue> result1 = eval(key1); |
| WorkspaceFileValue value1 = result1.get(key1); |
| RepositoryName good = RepositoryName.create("@good"); |
| RepositoryName main = RepositoryName.create("@"); |
| RepositoryName secondary = RepositoryName.create("@secondary"); |
| assertThat(value1.getRepositoryMapping()).containsEntry(secondary, ImmutableMap.of(good, main)); |
| assertNoEvents(); |
| } |
| |
| private static class TestManagedDirectoriesKnowledge implements ManagedDirectoriesKnowledge { |
| private String lastWorkspaceName; |
| private int cnt = 0; |
| |
| @Nullable |
| @Override |
| public RepositoryName getOwnerRepository(PathFragment relativePathFragment) { |
| return null; |
| } |
| |
| @Override |
| public ImmutableSet<PathFragment> getManagedDirectories(RepositoryName repositoryName) { |
| return null; |
| } |
| |
| @Override |
| public boolean workspaceHeaderReloaded( |
| @Nullable WorkspaceFileValue oldValue, @Nullable WorkspaceFileValue newValue) { |
| if (Objects.equals(oldValue, newValue)) { |
| return false; |
| } |
| ++cnt; |
| lastWorkspaceName = newValue != null ? newValue.getPackage().getWorkspaceName() : null; |
| return true; |
| } |
| |
| private String getLastWorkspaceName() { |
| return lastWorkspaceName; |
| } |
| |
| private int getCnt() { |
| return cnt; |
| } |
| } |
| |
| private static class TestManagedDirectoriesListener implements ManagedDirectoriesListener { |
| @Nullable private Set<RepositoryName> repositoryNames; |
| |
| @Override |
| public void onManagedDirectoriesRefreshed(Set<RepositoryName> repositoryNames) { |
| this.repositoryNames = repositoryNames; |
| } |
| |
| @Nullable |
| public Set<RepositoryName> getRepositoryNames() { |
| return repositoryNames; |
| } |
| |
| public void reset() { |
| repositoryNames = null; |
| } |
| } |
| |
| // tests of splitAST, an internal helper function |
| |
| @Test |
| public void testSplitASTNoLoad() { |
| List<StarlarkFile> asts = getASTs("foo_bar = 1"); |
| assertThat(asts).hasSize(1); |
| assertThat(asts.get(0).getStatements()).hasSize(1); |
| } |
| |
| @Test |
| public void testSplitASTOneLoadAtTop() { |
| List<StarlarkFile> asts = getASTs("load('//:foo.bzl', 'bar')", "foo_bar = 1"); |
| assertThat(asts).hasSize(1); |
| assertThat(asts.get(0).getStatements()).hasSize(2); |
| } |
| |
| @Test |
| public void testSplitASTOneLoad() { |
| List<StarlarkFile> asts = getASTs("foo_bar = 1", "load('//:foo.bzl', 'bar')"); |
| assertThat(asts).hasSize(2); |
| assertThat(asts.get(0).getStatements()).hasSize(1); |
| assertThat(asts.get(1).getStatements()).hasSize(1); |
| } |
| |
| @Test |
| public void testSplitASTTwoSuccessiveLoads() { |
| List<StarlarkFile> asts = |
| getASTs("foo_bar = 1", "load('//:foo.bzl', 'bar')", "load('//:bar.bzl', 'foo')"); |
| assertThat(asts).hasSize(2); |
| assertThat(asts.get(0).getStatements()).hasSize(1); |
| assertThat(asts.get(1).getStatements()).hasSize(2); |
| } |
| |
| @Test |
| public void testSplitASTTwoSucessiveLoadsWithNonLoadStatement() { |
| List<StarlarkFile> asts = |
| getASTs( |
| "foo_bar = 1", |
| "load('//:foo.bzl', 'bar')", |
| "load('//:bar.bzl', 'foo')", |
| "local_repository(name = 'foobar', path = '/bar/foo')"); |
| assertThat(asts).hasSize(2); |
| assertThat(asts.get(0).getStatements()).hasSize(1); |
| assertThat(asts.get(1).getStatements()).hasSize(3); |
| } |
| |
| @Test |
| public void testSplitASTThreeLoadsThreeSegments() { |
| List<StarlarkFile> asts = |
| getASTs( |
| "foo_bar = 1", |
| "load('//:foo.bzl', 'bar')", |
| "load('//:bar.bzl', 'foo')", |
| "local_repository(name = 'foobar', path = '/bar/foo')", |
| "load('@foobar//:baz.bzl', 'bleh')"); |
| assertThat(asts).hasSize(3); |
| assertThat(asts.get(0).getStatements()).hasSize(1); |
| assertThat(asts.get(1).getStatements()).hasSize(3); |
| assertThat(asts.get(2).getStatements()).hasSize(1); |
| } |
| |
| @Test |
| public void testSplitASTThreeLoadsThreeSegmentsWithContent() { |
| List<StarlarkFile> asts = |
| getASTs( |
| "foo_bar = 1", |
| "load('//:foo.bzl', 'bar')", |
| "load('//:bar.bzl', 'foo')", |
| "local_repository(name = 'foobar', path = '/bar/foo')", |
| "load('@foobar//:baz.bzl', 'bleh')", |
| "bleh()"); |
| assertThat(asts).hasSize(3); |
| assertThat(asts.get(0).getStatements()).hasSize(1); |
| assertThat(asts.get(1).getStatements()).hasSize(3); |
| assertThat(asts.get(2).getStatements()).hasSize(2); |
| } |
| |
| private static ImmutableList<StarlarkFile> getASTs(String... lines) { |
| return WorkspaceFileFunction.splitAST(StarlarkFile.parse(ParserInput.fromLines(lines))); |
| } |
| } |