aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java175
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/LabelExpanderTest.java257
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/MakeVariableExpanderTest.java149
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java112
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/UtilTest.java36
5 files changed, 729 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java b/src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java
new file mode 100644
index 0000000000..1f24cf40af
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/DependencyResolverTest.java
@@ -0,0 +1,175 @@
+// Copyright 2015 Google Inc. 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.analysis;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ListMultimap;
+import com.google.devtools.build.lib.analysis.DependencyResolver.Dependency;
+import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
+import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
+import com.google.devtools.build.lib.analysis.util.TestAspects;
+import com.google.devtools.build.lib.analysis.util.TestAspects.AspectRequiringRule;
+import com.google.devtools.build.lib.packages.AspectDefinition;
+import com.google.devtools.build.lib.packages.AspectFactory;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.NoSuchThingException;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import javax.annotation.Nullable;
+
+/**
+ * Tests for {@link DependencyResolver}.
+ *
+ * <p>These use custom rules so that all usual and unusual cases related to aspect processing can
+ * be tested.
+ *
+ * <p>It would be nicer is we didn't have a Skyframe executor, if we didn't have that, we'd need a
+ * way to create a configuration, a package manager and a whole lot of other things, so it's just
+ * easier this way.
+ */
+@RunWith(JUnit4.class)
+public class DependencyResolverTest extends AnalysisTestCase {
+ private DependencyResolver dependencyResolver;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ dependencyResolver = new DependencyResolver() {
+ @Override
+ protected void invalidVisibilityReferenceHook(TargetAndConfiguration node, Label label) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ protected void invalidPackageGroupReferenceHook(TargetAndConfiguration node, Label label) {
+ throw new IllegalStateException();
+ }
+
+ @Nullable
+ @Override
+ protected Target getTarget(Label label) throws NoSuchThingException {
+ try {
+ return packageManager.getTarget(reporter, label);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ };
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private void pkg(String name, String... contents) throws Exception {
+ scratchFile("" + name + "/BUILD", contents);
+ }
+
+ @SafeVarargs
+ private final void setRules(RuleDefinition... rules) throws Exception {
+ ConfiguredRuleClassProvider.Builder builder =
+ new ConfiguredRuleClassProvider.Builder();
+ TestRuleClassProvider.addStandardRules(builder);
+ for (RuleDefinition rule : rules) {
+ builder.addRuleDefinition(rule);
+ }
+
+ useRuleClassProvider(builder.build());
+ update();
+ }
+
+ private ListMultimap<Attribute, Dependency> dependentNodeMap(
+ String targetName, Class<? extends ConfiguredAspectFactory> aspect) throws Exception {
+ AspectDefinition aspectDefinition = aspect == null
+ ? null
+ : AspectFactory.Util.create(aspect).getDefinition();
+ Target target = packageManager.getTarget(reporter, Label.parseAbsolute(targetName));
+ return dependencyResolver.dependentNodeMap(
+ new TargetAndConfiguration(target, getTargetConfiguration()),
+ aspectDefinition,
+ ImmutableSet.<ConfigMatchingProvider>of());
+ }
+
+ @SafeVarargs
+ private final void assertDep(
+ ListMultimap<Attribute, Dependency> dependentNodeMap,
+ String attrName,
+ String dep,
+ Class<? extends AspectFactory<?, ?, ?>>... aspects) {
+ Attribute attr = null;
+ for (Attribute candidate : dependentNodeMap.keySet()) {
+ if (candidate.getName().equals(attrName)) {
+ attr = candidate;
+ break;
+ }
+ }
+
+ assertNotNull("Attribute '" + attrName + "' not found", attr);
+ Dependency dependency = null;
+ for (Dependency candidate : dependentNodeMap.get(attr)) {
+ if (candidate.getLabel().toString().equals(dep)) {
+ dependency = candidate;
+ break;
+ }
+ }
+
+ assertNotNull("Dependency '" + dep + "' on attribute '" + attrName + "' not found", dependency);
+ assertThat(dependency.getAspects()).containsExactly((Object[]) aspects);
+ }
+
+ @Test
+ public void hasAspectsRequiredByRule() throws Exception {
+ setRules(new AspectRequiringRule(), new TestAspects.BaseRule());
+ pkg("a",
+ "aspect(name='a', foo=[':b'])",
+ "aspect(name='b', foo=[])");
+ ListMultimap<Attribute, Dependency> map = dependentNodeMap("//a:a", null);
+ assertDep(map, "foo", "//a:b", TestAspects.SimpleAspect.class);
+ }
+
+ @Test
+ public void hasAspectsRequiredByAspect() throws Exception {
+ setRules(new TestAspects.BaseRule(), new TestAspects.SimpleRule());
+ pkg("a",
+ "simple(name='a', foo=[':b'])",
+ "simple(name='b', foo=[])");
+ ListMultimap<Attribute, Dependency> map =
+ dependentNodeMap("//a:a", TestAspects.AttributeAspect.class);
+ assertDep(map, "foo", "//a:b", TestAspects.AttributeAspect.class);
+ }
+
+ @Test
+ public void hasAspectDependencies() throws Exception {
+ setRules(new TestAspects.BaseRule());
+ pkg("a", "base(name='a')");
+ pkg("extra", "base(name='extra')");
+ ListMultimap<Attribute, Dependency> map =
+ dependentNodeMap("//a:a", TestAspects.ExtraAttributeAspect.class);
+ assertDep(map, "$dep", "//extra:extra");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/LabelExpanderTest.java b/src/test/java/com/google/devtools/build/lib/analysis/LabelExpanderTest.java
new file mode 100644
index 0000000000..708408f9d9
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/LabelExpanderTest.java
@@ -0,0 +1,257 @@
+// Copyright 2010-2015 Google Inc. 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.analysis;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.Label.SyntaxException;
+import com.google.devtools.build.lib.testutil.Suite;
+import com.google.devtools.build.lib.testutil.TestSpec;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.util.Map;
+
+/**
+ * Tests for {@link LabelExpander}.
+ */
+@TestSpec(size = Suite.SMALL_TESTS)
+public class LabelExpanderTest extends BuildViewTestCase {
+ /**
+ * A dummy target that resolves labels and receives errors.
+ */
+ private ConfiguredTarget dummyTarget;
+
+ /**
+ * Artifacts generated by {@code dummyTarget} identified by their
+ * root-relative paths; to be used for mock label-to-artifact mappings.
+ */
+ private Map<String, Artifact> artifactsByName;
+
+ /**
+ * All characters that the heuristic considers to be part of a target.
+ * This is a subset of the allowed label characters. The ones left out may
+ * have a special meaning during expression expansion:
+ *
+ * <ul>
+ * <li>comma (",") - may separate labels
+ * <li>equals sign ("=") - may separate labels
+ * <li>colon (":") - can only appear in labels, not in target names
+ * </ul>
+ */
+ private static final String allowedChars = "_/.-+" + PathFragment.SEPARATOR_CHAR
+ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+ // Helper methods -----------------------------------------------------------
+
+ private void setupDummy() throws Exception {
+ dummyTarget = scratchConfiguredTarget(
+ "foo", "foo",
+ "filegroup(name = 'foo',",
+ " srcs = ['x1','x2','bar/x3', '" + allowedChars + "', 'xx11', 'x11', 'xx1'])");
+ collectArtifacts();
+ }
+
+ /**
+ * Collects all generated mock artifacts for {@code dummyTarget} and assigns
+ * the result to {@code artifactsByName}.
+ */
+ private void collectArtifacts() {
+ ImmutableMap.Builder<String, Artifact> builder = ImmutableMap.builder();
+ for (Artifact artifact : getFilesToBuild(dummyTarget)) {
+ builder.put(artifact.getRootRelativePath().toString(), artifact);
+ }
+ artifactsByName = builder.build();
+ }
+
+ /**
+ * Gets a generated artifact object for a target in package "foo" from {@code
+ * artifactsByName}.
+ */
+ private Artifact artifactFor(String targetName) {
+ return artifactsByName.get("foo/" + targetName);
+ }
+
+ /**
+ * Creates fake label in package "foo".
+ */
+ private static Label labelFor(String targetName) throws SyntaxException {
+ return Label.create("foo", targetName);
+ }
+
+ /**
+ * Asserts that an expansion with a given mapping produces the expected
+ * results.
+ */
+ private void assertExpansion(String expectedResult, String expressionToExpand,
+ Map<Label, Iterable<Artifact>> mapping) throws Exception {
+ assertEquals(expectedResult,
+ LabelExpander.expand(expressionToExpand, mapping, dummyTarget.getLabel()));
+ }
+
+ /**
+ * Asserts that an expansion with an empty mapping produces the expected
+ * results.
+ */
+ private void assertExpansion(String expected, String original) throws Exception {
+ assertExpansion(expected, original, ImmutableMap.<Label, Iterable<Artifact>>of());
+ }
+
+ // Actual tests -------------------------------------------------------------
+
+ /**
+ * Tests that if no mapping is specified, then strings expand to themselves.
+ */
+ public void testStringExpandsToItselfWhenNoMappingSpecified() throws Exception {
+ setupDummy();
+ assertExpansion("", null);
+ assertExpansion("cmd", "cmd");
+ assertExpansion("//x:y,:z,w", "//x:y,:z,w");
+ assertExpansion(allowedChars, allowedChars);
+ }
+
+ /**
+ * Tests that in case of a one-to-one label-to-artifact mapping the expansion
+ * produces the expected results.
+ */
+ public void testExpansion() throws Exception {
+ setupDummy();
+ assertExpansion("foo/x1", "x1", ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
+
+ assertExpansion("foo/x1", ":x1", ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
+
+ assertExpansion("foo/x1", "//foo:x1", ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
+ }
+
+ /**
+ * Tests that label extraction works as expected - disallowed label characters
+ * are resolved to themselves.
+ */
+ public void testLabelExtraction() throws Exception {
+ setupDummy();
+ assertExpansion("(foo/" + allowedChars + ")", "(//foo:" + allowedChars + ")",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor(allowedChars), ImmutableList.of(artifactFor(allowedChars))));
+
+ assertExpansion("foo/x1,foo/x2=foo/bar/x3", "x1,x2=bar/x3",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1")),
+ labelFor("x2"), ImmutableList.of(artifactFor("x2")),
+ labelFor("bar/x3"), ImmutableList.of(artifactFor("bar/x3"))));
+ }
+
+ /**
+ * Tests that an exception is thrown when the mapping is not one-to-one.
+ */
+ public void testThrowsWhenMappingIsNotOneToOne() throws Exception {
+ setupDummy();
+ try {
+ LabelExpander.expand("x1", ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.<Artifact>of()), dummyTarget.getLabel());
+
+ fail("Expected an exception.");
+ } catch (LabelExpander.NotUniqueExpansionException nuee) {
+ // was expected
+ }
+
+ try {
+ LabelExpander.expand("x1", ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"), artifactFor("x2"))),
+ dummyTarget.getLabel());
+
+ fail("Expected an exception.");
+ } catch (LabelExpander.NotUniqueExpansionException nuee) {
+ // was expected
+ }
+ }
+
+ /**
+ * Tests expanding labels that result in a SyntaxException.
+ */
+ public void testIllFormedLabels() throws Exception {
+ setupDummy();
+ assertExpansion("x1:x2:x3", "x1:x2:x3",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1")),
+ labelFor("x2"), ImmutableList.of(artifactFor("x2")),
+ labelFor("bar/x3"), ImmutableList.of(artifactFor("bar/x3"))));
+
+ assertExpansion("foo://x1 x1/../x2", "foo://x1 x1/../x2",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1")),
+ labelFor("x2"), ImmutableList.of(artifactFor("x2")),
+ labelFor("bar/x3"), ImmutableList.of(artifactFor("bar/x3"))));
+
+ assertExpansion("//foo:/x1", "//foo:/x1",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
+
+ assertExpansion("//foo:../x1", "//foo:../x1",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
+
+ assertExpansion("//foo:x1/../x2", "//foo:x1/../x2",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1")),
+ labelFor("x2"), ImmutableList.of(artifactFor("x2"))));
+
+ assertExpansion("//foo:x1/./x2", "//foo:x1/./x2",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1")),
+ labelFor("x2"), ImmutableList.of(artifactFor("x2"))));
+
+ assertExpansion("//foo:x1//x2", "//foo:x1//x2",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1")),
+ labelFor("x2"), ImmutableList.of(artifactFor("x2"))));
+
+ assertExpansion("//foo:x1/..", "//foo:x1/..",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
+
+ assertExpansion("//foo:x1/", "//foo:x1/",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
+
+ assertExpansion(":", ":");
+ }
+
+ /**
+ * Tests that label parsing is greedy (always extracting the longest
+ * possible label). This means that if a label is a substring of another
+ * label, it should not be expanded but be treated as part of the longer one.
+ */
+ public void testLabelIsSubstringOfValidLabel() throws Exception {
+ setupDummy();
+ assertExpansion("x3=foo/bar/x3", "x3=bar/x3",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("bar/x3"), ImmutableList.of(artifactFor("bar/x3"))));
+
+ assertExpansion("foo/x1,foo/x11,foo/xx1,foo/xx11", "x1,x11,xx1,xx11",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1")),
+ labelFor("x11"), ImmutableList.of(artifactFor("x11")),
+ labelFor("xx1"), ImmutableList.of(artifactFor("xx1")),
+ labelFor("xx11"), ImmutableList.of(artifactFor("xx11"))));
+
+ assertExpansion("//x1", "//x1",
+ ImmutableMap.<Label, Iterable<Artifact>>of(
+ labelFor("x1"), ImmutableList.of(artifactFor("x1"))));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/MakeVariableExpanderTest.java b/src/test/java/com/google/devtools/build/lib/analysis/MakeVariableExpanderTest.java
new file mode 100644
index 0000000000..dbdc1c1c70
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/MakeVariableExpanderTest.java
@@ -0,0 +1,149 @@
+// Copyright 2006-2015 Google Inc. 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.analysis;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for the {@link MakeVariableExpander}, which expands variable references of the form
+ * <code>"$x"</code> and <code>"$(foo)"</code> into their corresponding values.
+ */
+@RunWith(JUnit4.class)
+public class MakeVariableExpanderTest {
+
+ private MakeVariableExpander.Context context;
+
+ private Map<String, String> vars = new HashMap<>();
+
+ @Before
+ public void setUp() throws Exception {
+ context = new MakeVariableExpander.Context() {
+ @Override
+ public String lookupMakeVariable(String name)
+ throws MakeVariableExpander.ExpansionException {
+ // Not a Make variable. Let the shell handle the expansion.
+ if (name.startsWith("$")) {
+ return name;
+ }
+ if (!vars.containsKey(name)) {
+ throw new MakeVariableExpander.ExpansionException("$(" + name + ") not defined");
+ }
+ return vars.get(name);
+ }
+ };
+
+ vars.put("SRCS", "src1 src2");
+ }
+
+ private void assertExpansionEquals(String expected, String cmd)
+ throws MakeVariableExpander.ExpansionException {
+ assertEquals(expected, MakeVariableExpander.expand(cmd, context));
+ }
+
+ private void assertExpansionFails(String expectedErrorSuffix, String cmd) {
+ try {
+ MakeVariableExpander.expand(cmd, context);
+ fail("Expansion of " + cmd + " didn't fail as expected");
+ } catch (Exception e) {
+ assertThat(e).hasMessage(expectedErrorSuffix);
+ }
+ }
+
+ @Test
+ public void testExpansion() throws Exception {
+ vars.put("<", "src1");
+ vars.put("OUTS", "out1 out2");
+ vars.put("@", "out1");
+ vars.put("^", "src1 src2 dep1 dep2");
+ vars.put("@D", "outdir");
+ vars.put("BINDIR", "bindir");
+
+ assertExpansionEquals("src1 src2", "$(SRCS)");
+ assertExpansionEquals("src1", "$<");
+ assertExpansionEquals("out1 out2", "$(OUTS)");
+ assertExpansionEquals("out1", "$(@)");
+ assertExpansionEquals("out1", "$@");
+ assertExpansionEquals("out1,", "$@,");
+
+ assertExpansionEquals("src1 src2 out1 out2", "$(SRCS) $(OUTS)");
+
+ assertExpansionEquals("cmd", "cmd");
+ assertExpansionEquals("cmd src1 src2,", "cmd $(SRCS),");
+ assertExpansionEquals("label1 src1 src2,", "label1 $(SRCS),");
+ assertExpansionEquals(":label1 src1 src2,", ":label1 $(SRCS),");
+
+ // Note: $(location x) is considered an undefined variable;
+ assertExpansionFails("$(location label1) not defined",
+ "$(location label1), $(SRCS),");
+ }
+
+ @Test
+ public void testRecursiveExpansion() throws Exception {
+ // Expansion is recursive: $(recursive) -> $(SRCS) -> "src1 src2"
+ vars.put("recursive", "$(SRCS)");
+ assertExpansionEquals("src1 src2", "$(recursive)");
+
+ // Recursion does not span expansion boundaries:
+ // $(recur2a)$(recur2b) --> "$" + "(SRCS)" --/--> "src1 src2"
+ vars.put("recur2a", "$$");
+ vars.put("recur2b", "(SRCS)");
+ assertExpansionEquals("$(SRCS)", "$(recur2a)$(recur2b)");
+ }
+
+ @Test
+ public void testInfiniteRecursionFailsGracefully() throws Exception {
+ vars.put("infinite", "$(infinite)");
+ assertExpansionFails("potentially unbounded recursion during expansion "
+ + "of '$(infinite)'",
+ "$(infinite)");
+
+ vars.put("black", "$(white)");
+ vars.put("white", "$(black)");
+ assertExpansionFails("potentially unbounded recursion during expansion "
+ + "of '$(black)'",
+ "$(white) is the new $(black)");
+ }
+
+ @Test
+ public void testErrors() throws Exception {
+ assertExpansionFails("unterminated variable reference", "$(SRCS");
+ assertExpansionFails("unterminated $", "$");
+
+ String suffix = "instead for \"Make\" variables, or escape the '$' as '$$' if you intended "
+ + "this for the shell";
+ assertExpansionFails("'$file' syntax is not supported; use '$(file)' " + suffix,
+ "for file in a b c;do echo $file;done");
+ assertExpansionFails("'${file%:.*8}' syntax is not supported; use '$(file%:.*8)' " + suffix,
+ "${file%:.*8}");
+ }
+
+ @Test
+ public void testShellVariables() throws Exception {
+ assertExpansionEquals("for file in a b c;do echo $file;done",
+ "for file in a b c;do echo $$file;done");
+ assertExpansionEquals("${file%:.*8}", "$${file%:.*8}");
+ assertExpansionFails("$(basename file) not defined", "$(basename file)");
+ assertExpansionEquals("$(basename file)", "$$(basename file)");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java b/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java
new file mode 100644
index 0000000000..107b6fcbca
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java
@@ -0,0 +1,112 @@
+// Copyright 2015 Google Inc. 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.analysis;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Root;
+import com.google.devtools.build.lib.events.EventKind;
+import com.google.devtools.build.lib.testutil.FoundationTestCase;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Test for {@link Runfiles}.
+ */
+public class RunfilesTest extends FoundationTestCase {
+
+ private void checkWarning() {
+ assertContainsEvent("obscured by a -> /workspace/a");
+ assertEquals("Runfiles.filterListForObscuringSymlinks should have warned once",
+ 1, eventCollector.count());
+ assertEquals(EventKind.WARNING, Iterables.getOnlyElement(eventCollector).getKind());
+ }
+
+ public void testFilterListForObscuringSymlinksCatchesBadObscurer() throws Exception {
+ Map<PathFragment, Artifact> obscuringMap = new HashMap<>();
+ PathFragment pathA = new PathFragment("a");
+ Root root = Root.asSourceRoot(scratchFS().getPath("/workspace"));
+ Artifact artifactA = new Artifact(new PathFragment("a"), root);
+ obscuringMap.put(pathA, artifactA);
+ obscuringMap.put(new PathFragment("a/b"), new Artifact(new PathFragment("c/b"),
+ root));
+ assertThat(Runfiles.filterListForObscuringSymlinks(reporter, null, obscuringMap).entrySet())
+ .containsExactly(Maps.immutableEntry(pathA, artifactA)).inOrder();
+ checkWarning();
+ }
+
+ public void testFilterListForObscuringSymlinksCatchesBadGrandParentObscurer() throws Exception {
+ Map<PathFragment, Artifact> obscuringMap = new HashMap<>();
+ PathFragment pathA = new PathFragment("a");
+ Root root = Root.asSourceRoot(scratchFS().getPath("/workspace"));
+ Artifact artifactA = new Artifact(new PathFragment("a"),
+ root);
+ obscuringMap.put(pathA, artifactA);
+ obscuringMap.put(new PathFragment("a/b/c"), new Artifact(new PathFragment("b/c"),
+ root));
+ assertThat(Runfiles.filterListForObscuringSymlinks(reporter, null, obscuringMap).entrySet())
+ .containsExactly(Maps.immutableEntry(pathA, artifactA)).inOrder();
+ checkWarning();
+ }
+
+ public void testFilterListForObscuringSymlinksCatchesBadObscurerNoListener() throws Exception {
+ Map<PathFragment, Artifact> obscuringMap = new HashMap<>();
+ PathFragment pathA = new PathFragment("a");
+ Root root = Root.asSourceRoot(scratchFS().getPath("/workspace"));
+ Artifact artifactA = new Artifact(new PathFragment("a"),
+ root);
+ obscuringMap.put(pathA, artifactA);
+ obscuringMap.put(new PathFragment("a/b"), new Artifact(new PathFragment("c/b"),
+ root));
+ assertThat(Runfiles.filterListForObscuringSymlinks(null, null, obscuringMap).entrySet())
+ .containsExactly(Maps.immutableEntry(pathA, artifactA)).inOrder();
+ }
+
+ public void testFilterListForObscuringSymlinksIgnoresOkObscurer() throws Exception {
+ Map<PathFragment, Artifact> obscuringMap = new HashMap<>();
+ PathFragment pathA = new PathFragment("a");
+ Root root = Root.asSourceRoot(scratchFS().getPath("/workspace"));
+ Artifact artifactA = new Artifact(new PathFragment("a"),
+ root);
+ obscuringMap.put(pathA, artifactA);
+ obscuringMap.put(new PathFragment("a/b"), new Artifact(new PathFragment("a/b"),
+ root));
+
+ assertThat(Runfiles.filterListForObscuringSymlinks(reporter, null, obscuringMap).entrySet())
+ .containsExactly(Maps.immutableEntry(pathA, artifactA)).inOrder();
+ assertNoEvents();
+ }
+
+ public void testFilterListForObscuringSymlinksNoObscurers() throws Exception {
+ Map<PathFragment, Artifact> obscuringMap = new HashMap<>();
+ PathFragment pathA = new PathFragment("a");
+ Root root = Root.asSourceRoot(scratchFS().getPath("/workspace"));
+ Artifact artifactA = new Artifact(new PathFragment("a"),
+ root);
+ obscuringMap.put(pathA, artifactA);
+ PathFragment pathBC = new PathFragment("b/c");
+ Artifact artifactBC = new Artifact(new PathFragment("a/b"),
+ root);
+ obscuringMap.put(pathBC, artifactBC);
+ assertThat(Runfiles.filterListForObscuringSymlinks(reporter, null, obscuringMap)
+ .entrySet()).containsExactly(Maps.immutableEntry(pathA, artifactA),
+ Maps.immutableEntry(pathBC, artifactBC));
+ assertNoEvents();
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/UtilTest.java b/src/test/java/com/google/devtools/build/lib/analysis/UtilTest.java
new file mode 100644
index 0000000000..ce3558a88a
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/UtilTest.java
@@ -0,0 +1,36 @@
+// Copyright 2006-2015 Google Inc. 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.analysis;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for the Util helper class.
+ */
+@RunWith(JUnit4.class)
+public class UtilTest {
+
+ @Test
+ public void testContainsHyphen() throws Exception {
+ assertTrue(Util.containsHyphen(new PathFragment("foo/bar/with-hyphen")));
+ assertFalse(Util.containsHyphen(new PathFragment("foo/bar/no/hyphen")));
+ }
+}