Open source some packages tests.
--
MOS_MIGRATED_REVID=92727637
diff --git a/src/test/java/BUILD b/src/test/java/BUILD
index ef8e053..30f2bcc 100644
--- a/src/test/java/BUILD
+++ b/src/test/java/BUILD
@@ -53,6 +53,10 @@
"com/google/devtools/build/lib/vfs/util/*.java",
"com/google/devtools/build/lib/events/util/*.java",
]),
+ data = [
+ "//src/main/native:libunix.dylib",
+ "//src/main/native:libunix.so",
+ ],
deps = [
":testutil",
"//src/main/java:bazel-core",
@@ -102,10 +106,7 @@
],
),
args = ["com.google.devtools.build.lib.AllTests"],
- data = glob([test_prefix + "/vfs/*.zip"]) + [
- "//src/main/native:libunix.dylib",
- "//src/main/native:libunix.so",
- ],
+ data = glob([test_prefix + "/vfs/*.zip"]),
deps = [
":foundations_testutil",
":test_runner",
@@ -131,10 +132,6 @@
args = [
"com.google.devtools.build.lib.AllTests",
],
- data = [
- "//src/main/native:libunix.dylib",
- "//src/main/native:libunix.so",
- ],
jvm_flags = ["-Dblaze.os=Windows"],
deps = [
":foundations_testutil",
@@ -174,10 +171,6 @@
"com/google/devtools/build/lib/actions/*.java",
]),
args = ["com.google.devtools.build.lib.AllTests"],
- data = [
- "//src/main/native:libunix.dylib",
- "//src/main/native:libunix.so",
- ],
deps = [
":actions_testutil",
":foundations_testutil",
@@ -200,20 +193,15 @@
"com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java",
"com/google/devtools/build/lib/analysis/util/*.java",
"com/google/devtools/build/lib/exec/util/*.java",
- "com/google/devtools/build/lib/packages/util/*.java",
"com/google/devtools/build/lib/skyframe/util/*.java",
]),
- data = [
- "//src/main/native:libunix.dylib",
- "//src/main/native:libunix.so",
- ],
resources = [
"MOCK_CROSSTOOL",
],
deps = [
":actions_testutil",
":foundations_testutil",
- ":test_runner",
+ ":packages_testutil",
":testutil",
"//src/main/java:bazel-core",
"//src/main/java:concurrent",
@@ -302,6 +290,7 @@
":actions_testutil",
":analysis_testutil",
":foundations_testutil",
+ ":packages_testutil",
":test_runner",
":testutil",
"//src/main/java:bazel-core",
@@ -332,6 +321,46 @@
],
)
+java_library(
+ name = "packages_testutil",
+ srcs = glob([
+ "com/google/devtools/build/lib/packages/util/*.java",
+ ]),
+ deps = [
+ ":foundations_testutil",
+ ":testutil",
+ "//src/main/java:bazel-core",
+ "//src/main/protobuf:proto_extra_actions_base",
+ "//third_party:guava",
+ "//third_party:guava-testlib",
+ "//third_party:jsr305",
+ "//third_party:junit4",
+ "//third_party:mockito",
+ "//third_party:truth",
+ ],
+)
+
+java_test(
+ name = "packages_test",
+ srcs = glob([
+ "com/google/devtools/build/lib/packages/*.java",
+ ]),
+ args = ["com.google.devtools.build.lib.AllTests"],
+ deps = [
+ ":actions_testutil",
+ ":foundations_testutil",
+ ":packages_testutil",
+ ":test_runner",
+ ":testutil",
+ "//src/main/java:bazel-core",
+ "//third_party:guava",
+ "//third_party:guava-testlib",
+ "//third_party:jsr305",
+ "//third_party:junit4",
+ "//third_party:truth",
+ ],
+)
+
cc_binary(
name = "com/google/devtools/build/lib/shell/killmyself",
srcs = ["com/google/devtools/build/lib/shell/killmyself.cc"],
@@ -345,8 +374,6 @@
args = ["com.google.devtools.build.lib.AllTests"],
data = [
":com/google/devtools/build/lib/shell/killmyself",
- "//src/main/native:libunix.dylib",
- "//src/main/native:libunix.so",
],
deps = [
":foundations_testutil",
@@ -369,10 +396,6 @@
"com/google/devtools/build/lib/syntax/*.java",
]),
args = ["com.google.devtools.build.lib.AllTests"],
- data = [
- "//src/main/native:libunix.dylib",
- "//src/main/native:libunix.so",
- ],
deps = [
":foundations_testutil",
":test_runner",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index 0bc8e87..a2cfe08 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -1264,7 +1264,7 @@
+ depRuleType + " rule '" + depRuleName + "' is misplaced here";
}
- public static String getErrorMsgNonEmptyList(String attrName, String ruleType, String ruleName) {
+ protected String getErrorMsgNonEmptyList(String attrName, String ruleType, String ruleName) {
return "non empty attribute '" + attrName + "' in '" + ruleType
+ "' rule '" + ruleName + "' has to have at least one value";
}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/AttributeTest.java b/src/test/java/com/google/devtools/build/lib/packages/AttributeTest.java
new file mode 100644
index 0000000..c15e9ac
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/AttributeTest.java
@@ -0,0 +1,248 @@
+// 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.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.Type.INTEGER;
+import static com.google.devtools.build.lib.packages.Type.LABEL;
+import static com.google.devtools.build.lib.packages.Type.LABEL_LIST;
+import static com.google.devtools.build.lib.packages.Type.STRING;
+import static com.google.devtools.build.lib.packages.Type.STRING_LIST;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Predicates;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.util.FileType;
+import com.google.devtools.build.lib.util.FileTypeSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests of Attribute code.
+ */
+@RunWith(JUnit4.class)
+public class AttributeTest {
+
+ private void assertDefaultValue(Object expected, Attribute attr) {
+ assertEquals(expected, attr.getDefaultValue(null));
+ }
+
+ private void assertType(Type<?> expectedType, Attribute attr) {
+ assertEquals(expectedType, attr.getType());
+ }
+
+ @Test
+ public void testBasics() throws Exception {
+ Attribute attr = attr("foo", Type.INTEGER).mandatory().value(3).build();
+ assertEquals("foo", attr.getName());
+ assertEquals(3, attr.getDefaultValue(null));
+ assertEquals(Type.INTEGER, attr.getType());
+ assertTrue(attr.isMandatory());
+ assertTrue(attr.isDocumented());
+ attr = attr("$foo", Type.INTEGER).build();
+ assertFalse(attr.isDocumented());
+ }
+
+ @Test
+ public void testNonEmptyReqiresListType() throws Exception {
+ try {
+ attr("foo", Type.INTEGER).nonEmpty().value(3).build();
+ fail();
+ } catch (NullPointerException e) {
+ assertThat(e).hasMessage("attribute 'foo' must be a list");
+ }
+ }
+
+ @Test
+ public void testNonEmpty() throws Exception {
+ Attribute attr = attr("foo", Type.LABEL_LIST).nonEmpty().legacyAllowAnyFileType().build();
+ assertEquals("foo", attr.getName());
+ assertEquals(Type.LABEL_LIST, attr.getType());
+ assertTrue(attr.isNonEmpty());
+ }
+
+ @Test
+ public void testSingleArtifactReqiresLabelType() throws Exception {
+ try {
+ attr("foo", Type.INTEGER).singleArtifact().value(3).build();
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("attribute 'foo' must be a label-valued type");
+ }
+ }
+
+ @Test
+ public void testDoublePropertySet() {
+ Attribute.Builder<String> builder = attr("x", STRING).mandatory().cfg(HOST).undocumented("")
+ .value("y");
+ try {
+ builder.mandatory();
+ fail();
+ } catch (IllegalStateException expected) {
+ // expected
+ }
+ try {
+ builder.cfg(HOST);
+ fail();
+ } catch (IllegalStateException expected) {
+ // expected
+ }
+ try {
+ builder.undocumented("");
+ fail();
+ } catch (IllegalStateException expected) {
+ // expected
+ }
+ try {
+ builder.value("z");
+ fail();
+ } catch (IllegalStateException expected) {
+ // expected
+ }
+
+ builder = attr("$x", STRING);
+ try {
+ builder.undocumented("");
+ fail();
+ } catch (IllegalStateException expected) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests the "convenience factories" (string, label, etc) for default
+ * values.
+ */
+ @Test
+ public void testConvenienceFactoriesDefaultValues() throws Exception {
+ assertDefaultValue(0,
+ attr("x", INTEGER).build());
+ assertDefaultValue(42,
+ attr("x", INTEGER).value(42).build());
+
+ assertDefaultValue("",
+ attr("x", STRING).build());
+ assertDefaultValue("foo",
+ attr("x", STRING).value("foo").build());
+
+ Label label = Label.parseAbsolute("//foo:bar");
+ assertDefaultValue(null,
+ attr("x", LABEL).legacyAllowAnyFileType().build());
+ assertDefaultValue(label,
+ attr("x", LABEL).legacyAllowAnyFileType().value(label).build());
+
+ List<String> slist = Arrays.asList("foo", "bar");
+ assertDefaultValue(Collections.emptyList(),
+ attr("x", STRING_LIST).build());
+ assertDefaultValue(slist,
+ attr("x", STRING_LIST).value(slist).build());
+
+ List<Label> llist = Arrays.asList(Label.parseAbsolute("//foo:bar"),
+ Label.parseAbsolute("//foo:wiz"));
+ assertDefaultValue(Collections.emptyList(),
+ attr("x", LABEL_LIST).legacyAllowAnyFileType().build());
+ assertDefaultValue(llist,
+ attr("x", LABEL_LIST).legacyAllowAnyFileType().value(llist).build());
+ }
+
+ /**
+ * Tests the "convenience factories" (string, label, etc) for types.
+ */
+ @Test
+ public void testConvenienceFactoriesTypes() throws Exception {
+ assertType(INTEGER,
+ attr("x", INTEGER).build());
+ assertType(INTEGER,
+ attr("x", INTEGER).value(42).build());
+
+ assertType(STRING,
+ attr("x", STRING).build());
+ assertType(STRING,
+ attr("x", STRING).value("foo").build());
+
+ Label label = Label.parseAbsolute("//foo:bar");
+ assertType(LABEL,
+ attr("x", LABEL).legacyAllowAnyFileType().build());
+ assertType(LABEL,
+ attr("x", LABEL).legacyAllowAnyFileType().value(label).build());
+
+ List<String> slist = Arrays.asList("foo", "bar");
+ assertType(STRING_LIST,
+ attr("x", STRING_LIST).build());
+ assertType(STRING_LIST,
+ attr("x", STRING_LIST).value(slist).build());
+
+ List<Label> llist = Arrays.asList(Label.parseAbsolute("//foo:bar"),
+ Label.parseAbsolute("//foo:wiz"));
+ assertType(LABEL_LIST,
+ attr("x", LABEL_LIST).legacyAllowAnyFileType().build());
+ assertType(LABEL_LIST,
+ attr("x", LABEL_LIST).legacyAllowAnyFileType().value(llist).build());
+ }
+
+ @Test
+ public void testCloneBuilder() {
+ FileTypeSet txtFiles = FileTypeSet.of(FileType.of("txt"));
+ RuleClass.Builder.RuleClassNamePredicate ruleClasses =
+ new RuleClass.Builder.RuleClassNamePredicate("mock_rule");
+
+ Attribute parentAttr = attr("x", LABEL_LIST)
+ .allowedFileTypes(txtFiles)
+ .mandatory()
+ .build();
+
+ Attribute childAttr1 = parentAttr.cloneBuilder().build();
+ assertEquals("x", childAttr1.getName());
+ assertEquals(txtFiles, childAttr1.getAllowedFileTypesPredicate());
+ assertEquals(Predicates.alwaysTrue(), childAttr1.getAllowedRuleClassesPredicate());
+ assertTrue(childAttr1.isMandatory());
+ assertFalse(childAttr1.isNonEmpty());
+
+ Attribute childAttr2 = parentAttr.cloneBuilder()
+ .nonEmpty()
+ .allowedRuleClasses(ruleClasses)
+ .build();
+ assertEquals("x", childAttr2.getName());
+ assertEquals(txtFiles, childAttr2.getAllowedFileTypesPredicate());
+ assertEquals(ruleClasses, childAttr2.getAllowedRuleClassesPredicate());
+ assertTrue(childAttr2.isMandatory());
+ assertTrue(childAttr2.isNonEmpty());
+
+ //Check if the parent attribute is unchanged
+ assertFalse(parentAttr.isNonEmpty());
+ assertEquals(Predicates.alwaysTrue(), parentAttr.getAllowedRuleClassesPredicate());
+ }
+
+ /**
+ * Tests that configurability settings are properly received.
+ */
+ @Test
+ public void testConfigurability() {
+ assertTrue(attr("foo_configurable", Type.LABEL_LIST).legacyAllowAnyFileType().build()
+ .isConfigurable());
+ assertFalse(attr("foo_nonconfigurable", Type.LABEL_LIST).legacyAllowAnyFileType()
+ .nonconfigurable("test").build().isConfigurable());
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java b/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java
new file mode 100644
index 0000000..c5f5c79
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java
@@ -0,0 +1,103 @@
+// 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.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.devtools.build.lib.events.util.EventCollectionApparatus;
+import com.google.devtools.build.lib.packages.util.PackageFactoryApparatus;
+import com.google.devtools.build.lib.testutil.Scratch;
+import com.google.devtools.build.lib.vfs.Path;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A test for the {@code exports_files} function defined in
+ * {@link PackageFactory}.
+ */
+@RunWith(JUnit4.class)
+public class ExportsFilesTest {
+
+ private Scratch scratch = new Scratch("/workspace");
+ private EventCollectionApparatus events = new EventCollectionApparatus();
+ private PackageFactoryApparatus packages = new PackageFactoryApparatus(events, scratch);
+
+ private Package pkg() throws Exception {
+ Path buildFile = scratch.file("pkg/BUILD",
+ "exports_files(['foo.txt', 'bar.txt'])");
+ return packages.createPackage("pkg", buildFile);
+ }
+
+ @Test
+ public void testExportsFilesRegistersFilesWithPackage() throws Exception {
+ List<String> names = getFileNamesOf(pkg());
+ String expected = "//pkg:BUILD //pkg:bar.txt //pkg:foo.txt";
+ assertEquals(expected, Joiner.on(' ').join(names));
+ }
+
+ /**
+ * Returns the names of the input files that are known to pkg.
+ */
+ private static List<String> getFileNamesOf(Package pkg) {
+ List<String> names = new ArrayList<>();
+ for (FileTarget target : pkg.getTargets(FileTarget.class)) {
+ names.add(target.getLabel().toString());
+ }
+ Collections.sort(names);
+ return names;
+ }
+
+ @Test
+ public void testFileThatsNotRegisteredYieldsUnknownTargetException() throws Exception {
+ try {
+ pkg().getTarget("baz.txt");
+ fail();
+ } catch (NoSuchTargetException e) {
+ assertThat(e).hasMessage("no such target '//pkg:baz.txt':"
+ + " target 'baz.txt' not declared in package 'pkg' defined by /workspace/pkg/BUILD");
+ }
+ }
+
+ @Test
+ public void testRegisteredFilesAreRetrievable() throws Exception {
+ Package pkg = pkg();
+ assertEquals("foo.txt", pkg.getTarget("foo.txt").getName());
+ assertEquals("bar.txt", pkg.getTarget("bar.txt").getName());
+ }
+
+ @Test
+ public void testExportsFilesAndRuleNameConflict() throws Exception {
+ events.setFailFast(false);
+
+ Path buildFile = scratch.file("pkg2/BUILD",
+ "exports_files(['foo'])",
+ "genrule(name = 'foo', srcs = ['bar'], outs = [],",
+ " cmd = '/bin/true')");
+ Package pkg = packages.createPackage("pkg2", buildFile);
+ events.assertContainsEvent("rule 'foo' in package 'pkg2' conflicts with "
+ + "existing source file");
+ assertTrue(pkg.getTarget("foo") instanceof InputFile);
+ }
+
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java b/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
new file mode 100644
index 0000000..bd8d536
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
@@ -0,0 +1,254 @@
+// Copyright 2008-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.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.devtools.build.lib.packages.GlobCache.BadGlobException;
+import com.google.devtools.build.lib.testutil.Scratch;
+import com.google.devtools.build.lib.testutil.TestUtils;
+import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for {@link GlobCache}
+ */
+@RunWith(JUnit4.class)
+public class GlobCacheTest {
+
+ private static final List<String> NONE = Collections.emptyList();
+
+ private Scratch scratch = new Scratch("/workspace");
+
+ private Path packageDirectory;
+ private Path buildFile;
+ private GlobCache cache;
+
+ @Before
+ public void setUp() throws Exception {
+ buildFile = scratch.file("isolated/BUILD",
+ "# contents don't matter in this test");
+ scratch.file("isolated/sub/BUILD",
+ "# contents don't matter in this test");
+
+ packageDirectory = buildFile.getParentDirectory();
+
+ scratch.file("isolated/first.txt",
+ "# this is first.txt");
+
+ scratch.file("isolated/second.txt",
+ "# this is second.txt");
+
+ scratch.file("isolated/first.js",
+ "# this is first.js");
+
+ scratch.file("isolated/second.js",
+ "# this is second.js");
+
+ // Files in subdirectories
+
+ scratch.file("isolated/foo/first.js",
+ "# this is foo/first.js");
+
+ scratch.file("isolated/foo/second.js",
+ "# this is foo/second.js");
+
+ scratch.file("isolated/bar/first.js",
+ "# this is bar/first.js");
+
+ scratch.file("isolated/bar/second.js",
+ "# this is bar/second.js");
+
+ scratch.file("isolated/sub/sub.js",
+ "# this is sub/sub.js");
+
+ cache = new GlobCache(packageDirectory, PackageIdentifier.createInDefaultRepo("isolated"),
+ new CachingPackageLocator() {
+ @Override
+ public Path getBuildFileForPackage(String packageName) {
+ if (packageName.equals("isolated")) {
+ return scratch.resolve("isolated/BUILD");
+ } else if (packageName.equals("isolated/sub")) {
+ return scratch.resolve("isolated/sub/BUILD");
+ } else {
+ return null;
+ }
+ }
+ }, null, TestUtils.getPool());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileSystemUtils.deleteTreesBelow(scratch.getFileSystem().getRootDirectory());
+ }
+
+ @Test
+ public void testSafeGlob() throws Exception {
+ List<Path> paths = cache.safeGlob("*.js", false).get();
+ assertPathsAre(paths,
+ "/workspace/isolated/first.js", "/workspace/isolated/second.js");
+ }
+
+ @Test
+ public void testSafeGlobInvalidPatterns() throws Exception {
+ for (String pattern : new String[] {
+ "Foo?.txt", "List{Test}.py", "List(Test).py" }) {
+ try {
+ cache.safeGlob(pattern, false);
+ fail("Expected pattern " + pattern + " to fail");
+ } catch (BadGlobException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void testGetGlob() throws Exception {
+ List<String> glob = cache.getGlob("*.js");
+ assertThat(glob).containsExactly("first.js", "second.js");
+ }
+
+ @Test
+ public void testGetGlob_subdirectory() throws Exception {
+ List<String> glob = cache.getGlob("foo/*.js");
+ assertThat(glob).containsExactly("foo/first.js", "foo/second.js");
+ }
+
+ @Test
+ public void testGetKeySet() throws Exception {
+ assertThat(cache.getKeySet()).isEmpty();
+
+ cache.getGlob("*.java");
+ assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false));
+
+ cache.getGlob("*.java");
+ assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false));
+
+ cache.getGlob("*.js");
+ assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.js", false));
+
+ cache.getGlob("*.java", true);
+ assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.js", false),
+ Pair.of("*.java", true));
+
+ try {
+ cache.getGlob("invalid?");
+ fail("Expected an invalid regex exception");
+ } catch (BadGlobException expected) {
+ }
+ assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.js", false),
+ Pair.of("*.java", true));
+
+ cache.getGlob("foo/first.*");
+ assertThat(cache.getKeySet()).containsExactly(Pair.of("*.java", false), Pair.of("*.java", true),
+ Pair.of("*.js", false), Pair.of("foo/first.*", false));
+ }
+
+ @Test
+ public void testGlob() throws Exception {
+ assertEmpty(cache.glob(list("*.java"), NONE, false));
+
+ assertThat(cache.glob(list("*.*"), NONE, false)).containsExactly("first.js", "first.txt",
+ "second.js", "second.txt").inOrder();
+
+ assertThat(cache.glob(list("*.*"), list("first.js"), false)).containsExactly("first.txt",
+ "second.js", "second.txt").inOrder();
+
+ assertThat(cache.glob(list("*.txt", "first.*"), NONE, false)).containsExactly("first.txt",
+ "second.txt", "first.js").inOrder();
+ }
+
+ @Test
+ public void testSetGlobPaths() throws Exception {
+ // This pattern matches no files.
+ String pattern = "fake*.java";
+ assertThat(cache.getKeySet()).doesNotContain(pattern);
+
+ List<String> results = cache.getGlob(pattern, false);
+
+ assertThat(cache.getKeySet()).contains(Pair.of(pattern, false));
+ assertThat(results).isEmpty();
+
+ cache.setGlobPaths(pattern, false, Futures.<List<Path>>immediateFuture(Lists.newArrayList(
+ scratch.resolve("isolated/fake.txt"),
+ scratch.resolve("isolated/fake.py"))));
+
+ assertThat(cache.getGlob(pattern, false)).containsExactly("fake.py", "fake.txt");
+ }
+
+ @Test
+ public void testGlobsUpToDate() throws Exception {
+ assertTrue(cache.globsUpToDate());
+
+ // Initialize the cache
+ cache.getGlob("*.txt");
+ assertTrue(cache.globsUpToDate());
+
+ cache.getGlob("*.js");
+ assertTrue(cache.globsUpToDate());
+
+ // Change the filesystem
+ scratch.file("isolated/third.txt",
+ "# this is third.txt");
+ assertFalse(cache.globsUpToDate());
+
+ // Fool the cache to observe the method's behavior.
+ cache.setGlobPaths("*.txt", false, Futures.<List<Path>>immediateFuture(Lists.newArrayList(
+ scratch.resolve("isolated/first.txt"),
+ scratch.resolve("isolated/second.txt"),
+ scratch.resolve("isolated/third.txt"))));
+ assertTrue(cache.globsUpToDate());
+ }
+
+ @Test
+ public void testRecursiveGlobDoesNotMatchSubpackage() throws Exception {
+ List<String> glob = cache.getGlob("**/*.js");
+ assertThat(glob).containsExactly("first.js", "second.js", "foo/first.js", "bar/first.js",
+ "foo/second.js", "bar/second.js");
+ }
+
+ private void assertEmpty(Collection<?> glob) {
+ assertThat(glob).isEmpty();
+ }
+
+ private void assertPathsAre(List<Path> paths, String... strings) {
+ List<String> pathStrings = new ArrayList<>();
+ for (Path path : paths) {
+ pathStrings.add(path.getPathString());
+ }
+ assertThat(pathStrings).containsExactlyElementsIn(Arrays.asList(strings));
+ }
+
+ /* syntactic shorthand for Lists.newArrayList(strings) */
+ private List<String> list(String... strings) {
+ return Lists.newArrayList(strings);
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
new file mode 100644
index 0000000..7eb0ed4
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
@@ -0,0 +1,830 @@
+// 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.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.substitutePlaceholderIntoTemplate;
+import static com.google.devtools.build.lib.packages.Type.BOOLEAN;
+import static com.google.devtools.build.lib.packages.Type.INTEGER;
+import static com.google.devtools.build.lib.packages.Type.LABEL;
+import static com.google.devtools.build.lib.packages.Type.LABEL_LIST;
+import static com.google.devtools.build.lib.packages.Type.OUTPUT_LIST;
+import static com.google.devtools.build.lib.packages.Type.STRING;
+import static com.google.devtools.build.lib.packages.Type.STRING_LIST;
+
+import com.google.common.base.Predicate;
+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.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+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.events.Location;
+import com.google.devtools.build.lib.events.Location.LineAndColumn;
+import com.google.devtools.build.lib.packages.Attribute.ValidityPredicate;
+import com.google.devtools.build.lib.packages.Package.Builder;
+import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.Label.SyntaxException;
+import com.google.devtools.build.lib.vfs.Path;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for {@link RuleClass}.
+ */
+public class RuleClassTest extends PackageLoadingTestCase {
+ private static final RuleClass.ConfiguredTargetFactory<Object, Object>
+ DUMMY_CONFIGURED_TARGET_FACTORY = new RuleClass.ConfiguredTargetFactory<Object, Object>() {
+ @Override
+ public Object create(Object ruleContext) throws InterruptedException {
+ throw new IllegalStateException();
+ }
+ };
+
+ private static final class DummyFragment extends BuildConfiguration.Fragment {
+ @Override
+ public String getName() {
+ return "dummy-for-testing";
+ }
+
+ @Override
+ public String cacheKey() {
+ return "some cache key";
+ }
+ }
+
+ private static final Predicate<String> PREFERRED_DEPENDENCY_PREDICATE = Predicates.alwaysFalse();
+
+ private static RuleClass createRuleClassA() throws Label.SyntaxException {
+ return new RuleClass("ruleA", false, false, false, false, false, false,
+ ImplicitOutputsFunction.NONE, RuleClass.NO_CHANGE,
+ DUMMY_CONFIGURED_TARGET_FACTORY, PredicatesWithMessage.<Rule>alwaysTrue(),
+ PREFERRED_DEPENDENCY_PREDICATE, ImmutableSet.<Class<?>>of(), null, null,
+ ImmutableSet.<Class<?>>of(), false, true,
+ attr("my-string-attr", STRING).mandatory().build(),
+ attr("my-label-attr", LABEL).mandatory().legacyAllowAnyFileType()
+ .value(Label.parseAbsolute("//default:label")).build(),
+ attr("my-labellist-attr", LABEL_LIST).mandatory().legacyAllowAnyFileType().build(),
+ attr("my-integer-attr", INTEGER).value(42).build(),
+ attr("my-string-attr2", STRING).mandatory().value((String) null).build(),
+ attr("my-stringlist-attr", STRING_LIST).build(),
+ attr("my-sorted-stringlist-attr", STRING_LIST).orderIndependent().build());
+ }
+
+ private static RuleClass createRuleClassB(RuleClass ruleClassA) {
+ // emulates attribute inheritance
+ List<Attribute> attributes = new ArrayList<>(ruleClassA.getAttributes());
+ attributes.add(attr("another-string-attr", STRING).mandatory().build());
+ return new RuleClass("ruleB", false, false, false, false, false, false,
+ ImplicitOutputsFunction.NONE, RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(), false, true,
+ attributes.toArray(new Attribute[0]));
+ }
+
+ public void testRuleClassBasics() throws Exception {
+ RuleClass ruleClassA = createRuleClassA();
+
+ assertEquals("ruleA", ruleClassA.getName());
+ assertEquals(7, ruleClassA.getAttributeCount());
+
+ assertEquals(0, (int) ruleClassA.getAttributeIndex("my-string-attr"));
+ assertEquals(1, (int) ruleClassA.getAttributeIndex("my-label-attr"));
+ assertEquals(2, (int) ruleClassA.getAttributeIndex("my-labellist-attr"));
+ assertEquals(3, (int) ruleClassA.getAttributeIndex("my-integer-attr"));
+ assertEquals(4, (int) ruleClassA.getAttributeIndex("my-string-attr2"));
+ assertEquals(5, (int) ruleClassA.getAttributeIndex("my-stringlist-attr"));
+ assertEquals(6, (int) ruleClassA.getAttributeIndex("my-sorted-stringlist-attr"));
+
+ assertEquals(ruleClassA.getAttribute(0),
+ ruleClassA.getAttributeByName("my-string-attr"));
+ assertEquals(ruleClassA.getAttribute(1),
+ ruleClassA.getAttributeByName("my-label-attr"));
+ assertEquals(ruleClassA.getAttribute(2),
+ ruleClassA.getAttributeByName("my-labellist-attr"));
+ assertEquals(ruleClassA.getAttribute(3),
+ ruleClassA.getAttributeByName("my-integer-attr"));
+ assertEquals(ruleClassA.getAttribute(4),
+ ruleClassA.getAttributeByName("my-string-attr2"));
+ assertEquals(ruleClassA.getAttribute(5),
+ ruleClassA.getAttributeByName("my-stringlist-attr"));
+ assertEquals(ruleClassA.getAttribute(6),
+ ruleClassA.getAttributeByName("my-sorted-stringlist-attr"));
+
+ assertEquals("", // default based on type
+ ruleClassA.getAttribute(0).getDefaultValue(null));
+ assertEquals(Label.parseAbsolute("//default:label"),
+ ruleClassA.getAttribute(1).getDefaultValue(null));
+ assertEquals(Collections.emptyList(),
+ ruleClassA.getAttribute(2).getDefaultValue(null));
+ assertEquals(42,
+ ruleClassA.getAttribute(3).getDefaultValue(null));
+ assertEquals(null, // default explicitly specified
+ ruleClassA.getAttribute(4).getDefaultValue(null));
+ assertEquals(Collections.emptyList(),
+ ruleClassA.getAttribute(5).getDefaultValue(null));
+ assertEquals(Collections.emptyList(),
+ ruleClassA.getAttribute(6).getDefaultValue(null));
+ }
+
+ public void testRuleClassInheritance() throws Exception {
+ RuleClass ruleClassA = createRuleClassA();
+ RuleClass ruleClassB = createRuleClassB(ruleClassA);
+
+ assertEquals("ruleB", ruleClassB.getName());
+ assertEquals(8, ruleClassB.getAttributeCount());
+
+ assertEquals(0, (int) ruleClassB.getAttributeIndex("my-string-attr"));
+ assertEquals(1, (int) ruleClassB.getAttributeIndex("my-label-attr"));
+ assertEquals(2, (int) ruleClassB.getAttributeIndex("my-labellist-attr"));
+ assertEquals(3, (int) ruleClassB.getAttributeIndex("my-integer-attr"));
+ assertEquals(4, (int) ruleClassB.getAttributeIndex("my-string-attr2"));
+ assertEquals(5, (int) ruleClassB.getAttributeIndex("my-stringlist-attr"));
+ assertEquals(6, (int) ruleClassB.getAttributeIndex("my-sorted-stringlist-attr"));
+ assertEquals(7, (int) ruleClassB.getAttributeIndex("another-string-attr"));
+
+ assertEquals(ruleClassB.getAttribute(0),
+ ruleClassB.getAttributeByName("my-string-attr"));
+ assertEquals(ruleClassB.getAttribute(1),
+ ruleClassB.getAttributeByName("my-label-attr"));
+ assertEquals(ruleClassB.getAttribute(2),
+ ruleClassB.getAttributeByName("my-labellist-attr"));
+ assertEquals(ruleClassB.getAttribute(3),
+ ruleClassB.getAttributeByName("my-integer-attr"));
+ assertEquals(ruleClassB.getAttribute(4),
+ ruleClassB.getAttributeByName("my-string-attr2"));
+ assertEquals(ruleClassB.getAttribute(5),
+ ruleClassB.getAttributeByName("my-stringlist-attr"));
+ assertEquals(ruleClassB.getAttribute(6),
+ ruleClassB.getAttributeByName("my-sorted-stringlist-attr"));
+ assertEquals(ruleClassB.getAttribute(7),
+ ruleClassB.getAttributeByName("another-string-attr"));
+ }
+
+ private static final String TEST_PACKAGE_NAME = "testpackage";
+
+ private static final String TEST_RULE_NAME = "my-rule-A";
+
+ private static final int TEST_RULE_DEFINED_AT_LINE = 42;
+
+ private static final String TEST_RULE_LABEL = "//" + TEST_PACKAGE_NAME + ":" + TEST_RULE_NAME;
+
+ private Path testBuildfilePath;
+ private Location testRuleLocation;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ testBuildfilePath = scratch.resolve("/home/user/workspace/testpackage/BUILD");
+ testRuleLocation = Location.fromPathAndStartColumn(
+ testBuildfilePath, 0, 0, new LineAndColumn(TEST_RULE_DEFINED_AT_LINE, 0));
+ }
+
+ private Package.Builder createDummyPackageBuilder() {
+ return new Builder(PackageIdentifier.createInDefaultRepo(TEST_PACKAGE_NAME))
+ .setFilename(testBuildfilePath)
+ .setMakeEnv(new MakeEnvironment.Builder());
+ }
+
+ public void testDuplicatedDeps() throws Exception {
+ RuleClass depsRuleClass = new RuleClass("ruleDeps", false, false, false, false, false, false,
+ ImplicitOutputsFunction.NONE, RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(), false, true,
+ attr("list1", LABEL_LIST).mandatory().legacyAllowAnyFileType().build(),
+ attr("list2", LABEL_LIST).mandatory().legacyAllowAnyFileType().build(),
+ attr("list3", LABEL_LIST).mandatory().legacyAllowAnyFileType().build());
+
+ // LinkedHashMap -> predictable iteration order for testing
+ Map<String, Object> attributeValues = new LinkedHashMap<>();
+ attributeValues.put("list1", Lists.newArrayList("//testpackage:dup1", ":dup1", ":nodup"));
+ attributeValues.put("list2", Lists.newArrayList(":nodup1", ":nodup2"));
+ attributeValues.put("list3", Lists.newArrayList(":dup1", ":dup1", ":dup2", ":dup2"));
+
+ reporter.removeHandler(failFastHandler);
+ createRule(depsRuleClass, "depsRule", attributeValues, testRuleLocation);
+
+ assertSame(3, eventCollector.count());
+ assertDupError("//testpackage:dup1", "list1", "depsRule");
+ assertDupError("//testpackage:dup1", "list3", "depsRule");
+ assertDupError("//testpackage:dup2", "list3", "depsRule");
+ }
+
+ private void assertDupError(String label, String attrName, String ruleName) {
+ assertContainsEvent(String.format("Label '%s' is duplicated in the '%s' attribute of rule '%s'",
+ label, attrName, ruleName));
+ }
+
+ public void testCreateRuleWithLegacyPublicVisibility() throws Exception {
+ RuleClass ruleClass = new RuleClass("ruleVis", false, false, false, false, false, false,
+ ImplicitOutputsFunction.NONE, RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(), false, true,
+ attr("visibility", LABEL_LIST).legacyAllowAnyFileType().build());
+ Map<String, Object> attributeValues = new HashMap<>();
+ attributeValues.put("visibility", Arrays.asList("//visibility:legacy_public"));
+
+ reporter.removeHandler(failFastHandler);
+ EventCollector collector = new EventCollector(EventKind.ERRORS);
+ reporter.addHandler(collector);
+
+ createRule(ruleClass, TEST_RULE_NAME, attributeValues, testRuleLocation);
+
+ assertContainsEvent("//visibility:legacy_public only allowed in package declaration");
+ }
+
+ public void testCreateRule() throws Exception {
+ RuleClass ruleClassA = createRuleClassA();
+
+ // LinkedHashMap -> predictable iteration order for testing
+ Map<String, Object> attributeValues = new LinkedHashMap<>();
+ attributeValues.put("my-labellist-attr", "foobar"); // wrong type
+ attributeValues.put("bogus-attr", "foobar"); // no such attr
+ attributeValues.put("my-stringlist-attr", Arrays.asList("foo", "bar"));
+
+ reporter.removeHandler(failFastHandler);
+ EventCollector collector = new EventCollector(EventKind.ERRORS);
+ reporter.addHandler(collector);
+
+ Rule rule = createRule(ruleClassA, TEST_RULE_NAME, attributeValues, testRuleLocation);
+
+ // TODO(blaze-team): (2009) refactor to use assertContainsEvent
+ Iterator<String> expectedMessages = Arrays.asList(
+ "expected value of type 'list(label)' for attribute 'my-labellist-attr' "
+ + "in 'ruleA' rule, but got 'foobar' (string)",
+ "no such attribute 'bogus-attr' in 'ruleA' rule",
+ "missing value for mandatory "
+ + "attribute 'my-string-attr' in 'ruleA' rule",
+ "missing value for mandatory attribute 'my-label-attr' in 'ruleA' rule",
+ "missing value for mandatory "
+ + "attribute 'my-labellist-attr' in 'ruleA' rule",
+ "missing value for mandatory "
+ + "attribute 'my-string-attr2' in 'ruleA' rule"
+ ).iterator();
+
+ for (Event event : collector) {
+ assertEquals(TEST_RULE_DEFINED_AT_LINE,
+ event.getLocation().getStartLineAndColumn().getLine());
+ assertEquals(testBuildfilePath.asFragment(), event.getLocation().getPath());
+ assertEquals(TEST_RULE_LABEL + ": " + expectedMessages.next(), event.getMessage());
+ }
+
+ // Test basic rule properties:
+ assertEquals("ruleA",
+ rule.getRuleClass());
+ assertEquals(TEST_RULE_NAME,
+ rule.getName());
+ assertEquals(TEST_RULE_LABEL,
+ rule.getLabel().toString());
+
+ // Test attribute access:
+ AttributeMap attributes = RawAttributeMapper.of(rule);
+ assertEquals("//default:label",
+ attributes.get("my-label-attr", Type.LABEL).toString());
+ assertEquals(42,
+ attributes.get("my-integer-attr", Type.INTEGER).intValue());
+ assertEquals("", // missing attribute -> default chosen based on type
+ attributes.get("my-string-attr", Type.STRING));
+ assertThat(attributes.get("my-labellist-attr", Type.LABEL_LIST)).isEmpty();
+ assertEquals(Arrays.asList("foo", "bar"),
+ attributes.get("my-stringlist-attr", Type.STRING_LIST));
+ try {
+ attributes.get("my-labellist-attr", Type.STRING); // wrong type
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Attribute my-labellist-attr is not of type string "
+ + "in rule //testpackage:my-rule-A");
+ }
+ }
+
+ public void testImplicitOutputs() throws Exception {
+ RuleClass ruleClassC = new RuleClass("ruleC", false, false, false, false, false, false,
+ ImplicitOutputsFunction.fromTemplates("foo-%{name}.bar",
+ "lib%{name}-wazoo-%{name}.mumble",
+ "stuff-%{outs}-bar"),
+ RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(), false, true,
+ attr("outs", OUTPUT_LIST).build());
+
+ Map<String, Object> attributeValues = new HashMap<>();
+ attributeValues.put("outs", Collections.singletonList("explicit_out"));
+
+ Rule rule = createRule(ruleClassC, "myrule", attributeValues, testRuleLocation);
+
+ Set<String> set = new HashSet<>();
+ for (OutputFile outputFile : rule.getOutputFiles()) {
+ set.add(outputFile.getName());
+ assertSame(rule, outputFile.getGeneratingRule());
+ }
+ assertThat(set).containsExactly("foo-myrule.bar", "libmyrule-wazoo-myrule.mumble",
+ "stuff-explicit_out-bar", "explicit_out");
+ }
+
+ public void testImplicitOutsWithBasenameDirname() throws Exception {
+ RuleClass ruleClass = new RuleClass("ruleClass", false, false, false, false, false, false,
+ ImplicitOutputsFunction.fromTemplates("%{dirname}lib%{basename}.bar"), RuleClass.NO_CHANGE,
+ DUMMY_CONFIGURED_TARGET_FACTORY, PredicatesWithMessage.<Rule>alwaysTrue(),
+ PREFERRED_DEPENDENCY_PREDICATE, ImmutableSet.<Class<?>>of(), null, null,
+ ImmutableSet.<Class<?>>of(), false, true);
+
+ Rule rule = createRule(ruleClass, "myRule", Collections.<String, Object>emptyMap(),
+ testRuleLocation);
+ assertEquals("libmyRule.bar", Iterables.getOnlyElement(rule.getOutputFiles()).getName());
+
+ Rule ruleWithSlash = createRule(ruleClass, "myRule/with/slash",
+ Collections.<String, Object>emptyMap(), testRuleLocation);
+ assertEquals("myRule/with/libslash.bar",
+ Iterables.getOnlyElement(ruleWithSlash.getOutputFiles()).getName());
+ }
+
+ /**
+ * Helper routine that instantiates a rule class with the given computed default and supporting
+ * attributes for the default to reference.
+ */
+ private static RuleClass getRuleClassWithComputedDefault(Attribute computedDefault) {
+ return new RuleClass("ruleClass", false, false, false, false, false, false,
+ ImplicitOutputsFunction.fromTemplates("empty"), RuleClass.NO_CHANGE,
+ DUMMY_CONFIGURED_TARGET_FACTORY, PredicatesWithMessage.<Rule>alwaysTrue(),
+ PREFERRED_DEPENDENCY_PREDICATE, ImmutableSet.<Class<?>>of(), null, null,
+ ImmutableSet.<Class<?>>of(), false, true,
+ attr("condition", BOOLEAN).value(false).build(),
+ attr("declared1", BOOLEAN).value(false).build(),
+ attr("declared2", BOOLEAN).value(false).build(),
+ attr("nonconfigurable", BOOLEAN).nonconfigurable("test").value(false).build(),
+ computedDefault);
+ }
+
+ /**
+ * Helper routine that checks that a computed default is valid and bound to the expected value.
+ */
+ private void checkValidComputedDefault(Object expectedValue, Attribute computedDefault,
+ ImmutableMap<String, Object> attrValueMap) throws Exception {
+ assertTrue(computedDefault.getDefaultValueForTesting() instanceof Attribute.ComputedDefault);
+ Rule rule = createRule(getRuleClassWithComputedDefault(computedDefault), "myRule",
+ attrValueMap, testRuleLocation);
+ AttributeMap attributes = RawAttributeMapper.of(rule);
+ assertEquals(
+ expectedValue,
+ attributes.get(computedDefault.getName(), computedDefault.getType()));
+ }
+
+ /**
+ * Helper routine that checks that a computed default is invalid due to declared dependency
+ * issues and fails with the expected message.
+ */
+ private void checkInvalidComputedDefault(Attribute computedDefault, String expectedMessage)
+ throws Exception {
+ try {
+ createRule(getRuleClassWithComputedDefault(computedDefault), "myRule",
+ ImmutableMap.<String, Object>of(), testRuleLocation);
+ fail("Expected computed default \"" + computedDefault.getName() + "\" to fail with "
+ + "declaration errors");
+ } catch (IllegalArgumentException e) {
+ // Expected outcome.
+ assertThat(e).hasMessage(expectedMessage);
+ }
+ }
+
+ /**
+ * Tests computed default values are computed as expected.
+ */
+ public void testComputedDefault() throws Exception {
+ Attribute computedDefault =
+ attr("$result", BOOLEAN).value(new Attribute.ComputedDefault("condition") {
+ @Override
+ public Object getDefault(AttributeMap rule) {
+ return rule.get("condition", Type.BOOLEAN);
+ }
+ }).build();
+
+ checkValidComputedDefault(Boolean.FALSE, computedDefault,
+ ImmutableMap.<String, Object>of("condition", Boolean.FALSE));
+ checkValidComputedDefault(Boolean.TRUE, computedDefault,
+ ImmutableMap.<String, Object>of("condition", Boolean.TRUE));
+ }
+
+ /**
+ * Tests that computed defaults can only read attribute values for configurable attributes that
+ * have been explicitly declared.
+ */
+ public void testComputedDefaultDeclarations() throws Exception {
+ checkValidComputedDefault(
+ Boolean.FALSE,
+ attr("$good_default_no_declares", BOOLEAN).value(
+ new Attribute.ComputedDefault() {
+ @Override public Object getDefault(AttributeMap rule) {
+ // OK: not a value check:
+ return rule.isAttributeValueExplicitlySpecified("undeclared");
+ }
+ }).build(),
+ ImmutableMap.<String, Object>of());
+
+ checkValidComputedDefault(
+ Boolean.FALSE,
+ attr("$good_default_one_declare", BOOLEAN).value(
+ new Attribute.ComputedDefault("declared1") {
+ @Override public Object getDefault(AttributeMap rule) {
+ return rule.get("declared1", Type.BOOLEAN);
+ }
+ }).build(),
+ ImmutableMap.<String, Object>of());
+
+ checkValidComputedDefault(
+ Boolean.FALSE,
+ attr("$good_default_two_declares", BOOLEAN).value(
+ new Attribute.ComputedDefault("declared1", "declared2") {
+ @Override public Object getDefault(AttributeMap rule) {
+ return rule.get("declared1", Type.BOOLEAN) && rule.get("declared2", Type.BOOLEAN);
+ }
+ }).build(),
+ ImmutableMap.<String, Object>of());
+
+ checkInvalidComputedDefault(
+ attr("$bad_default_no_declares", BOOLEAN).value(
+ new Attribute.ComputedDefault() {
+ @Override public Object getDefault(AttributeMap rule) {
+ return rule.get("declared1", Type.BOOLEAN);
+ }
+ }).build(),
+ "attribute \"declared1\" isn't available in this computed default context");
+
+ checkInvalidComputedDefault(
+ attr("$bad_default_one_declare", BOOLEAN).value(
+ new Attribute.ComputedDefault("declared1") {
+ @Override public Object getDefault(AttributeMap rule) {
+ return rule.get("declared1", Type.BOOLEAN) || rule.get("declared2", Type.BOOLEAN);
+ }
+ }).build(),
+ "attribute \"declared2\" isn't available in this computed default context");
+
+ checkInvalidComputedDefault(
+ attr("$bad_default_two_declares", BOOLEAN).value(
+ new Attribute.ComputedDefault("declared1", "declared2") {
+ @Override public Object getDefault(AttributeMap rule) {
+ return rule.get("condition", Type.BOOLEAN);
+ }
+ }).build(),
+ "attribute \"condition\" isn't available in this computed default context");
+ }
+
+ /**
+ * Tests that computed defaults *can* read attribute values for non-configurable attributes
+ * without needing to explicitly declare them.
+ */
+ public void testComputedDefaultWithNonConfigurableAttributes() throws Exception {
+ checkValidComputedDefault(
+ Boolean.FALSE,
+ attr("$good_default_reading_undeclared_nonconfigurable_attribute", BOOLEAN).value(
+ new Attribute.ComputedDefault() {
+ @Override public Object getDefault(AttributeMap rule) {
+ return rule.get("nonconfigurable", Type.BOOLEAN);
+ }
+ }).build(),
+ ImmutableMap.<String, Object>of());
+ }
+
+ public void testOutputsAreOrdered() throws Exception {
+ RuleClass ruleClassC = new RuleClass("ruleC", false, false, false, false, false, false,
+ ImplicitOutputsFunction.fromTemplates("first-%{name}", "second-%{name}", "out-%{outs}"),
+ RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(), false, true,
+ attr("outs", OUTPUT_LIST).build());
+
+ Map<String, Object> attributeValues = new HashMap<>();
+ attributeValues.put("outs", ImmutableList.of("third", "fourth"));
+
+ Rule rule = createRule(ruleClassC, "myrule", attributeValues, testRuleLocation);
+
+ List<String> actual = new ArrayList<>();
+ for (OutputFile outputFile : rule.getOutputFiles()) {
+ actual.add(outputFile.getName());
+ assertSame(rule, outputFile.getGeneratingRule());
+ }
+ assertWithMessage("unexpected output set").that(actual).containsExactly("first-myrule",
+ "second-myrule", "out-third", "out-fourth", "third", "fourth");
+ assertWithMessage("invalid output ordering").that(actual).containsExactly("first-myrule",
+ "second-myrule", "out-third", "out-fourth", "third", "fourth").inOrder();
+ }
+
+ public void testSubstitutePlaceholderIntoTemplate() throws Exception {
+ RuleClass ruleClass = new RuleClass("ruleA", false, false, false, false, false, false,
+ ImplicitOutputsFunction.NONE, RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(), false, true,
+ attr("a", STRING_LIST).mandatory().build(),
+ attr("b", STRING_LIST).mandatory().build(),
+ attr("c", STRING_LIST).mandatory().build(),
+ attr("baz", STRING_LIST).mandatory().build(),
+ attr("empty", STRING_LIST).build());
+
+ Map<String, Object> attributeValues = new LinkedHashMap<>();
+ attributeValues.put("a", ImmutableList.of("a", "A"));
+ attributeValues.put("b", ImmutableList.of("b", "B"));
+ attributeValues.put("c", ImmutableList.of("c", "C"));
+ attributeValues.put("baz", ImmutableList.of("baz", "BAZ"));
+ attributeValues.put("empty", ImmutableList.<String>of());
+
+ AttributeMap rule = RawAttributeMapper.of(
+ createRule(ruleClass, "testrule", attributeValues, testRuleLocation));
+
+ assertThat(substitutePlaceholderIntoTemplate("foo", rule)).containsExactly("foo");
+ assertThat(substitutePlaceholderIntoTemplate("foo-%{baz}-bar", rule)).containsExactly(
+ "foo-baz-bar", "foo-BAZ-bar").inOrder();
+ assertThat(substitutePlaceholderIntoTemplate("%{a}-%{b}-%{c}", rule)).containsExactly("a-b-c",
+ "a-b-C", "a-B-c", "a-B-C", "A-b-c", "A-b-C", "A-B-c", "A-B-C").inOrder();
+ assertThat(substitutePlaceholderIntoTemplate("%{a", rule)).containsExactly("%{a");
+ assertThat(substitutePlaceholderIntoTemplate("%{a}}", rule)).containsExactly("a}", "A}")
+ .inOrder();
+ assertThat(substitutePlaceholderIntoTemplate("x%{a}y%{empty}", rule)).isEmpty();
+ }
+
+ public void testOrderIndependentAttribute() throws Exception {
+ RuleClass ruleClassA = createRuleClassA();
+
+ List<String> list = Arrays.asList("foo", "bar", "baz");
+ Map<String, Object> attributeValues = new LinkedHashMap<>();
+ // mandatory values
+ attributeValues.put("my-string-attr", "");
+ attributeValues.put("my-label-attr", "//project");
+ attributeValues.put("my-string-attr2", "");
+ attributeValues.put("my-labellist-attr", Collections.emptyList());
+ // to compare the effect of .orderIndependent()
+ attributeValues.put("my-stringlist-attr", list);
+ attributeValues.put("my-sorted-stringlist-attr", list);
+
+ Rule rule = createRule(ruleClassA, "testrule", attributeValues, testRuleLocation);
+ AttributeMap attributes = RawAttributeMapper.of(rule);
+
+ assertEquals(list,
+ attributes.get("my-stringlist-attr", Type.STRING_LIST));
+ assertEquals(Arrays.asList("bar", "baz", "foo"),
+ attributes.get("my-sorted-stringlist-attr", Type.STRING_LIST));
+ }
+
+ public void testNonEmptyGood() throws Exception {
+ RuleClass mneRuleClass = setupNonEmpty(
+ attr("list1", LABEL_LIST).mandatory().legacyAllowAnyFileType().build(),
+ attr("list2", LABEL_LIST).nonEmpty().legacyAllowAnyFileType().build(),
+ attr("list3", STRING_LIST).nonEmpty().build());
+
+ Map<String, Object> attributeValues = new LinkedHashMap<>();
+ attributeValues.put("list1", Lists.newArrayList());
+ attributeValues.put("list2", Lists.newArrayList(":nodup1", ":nodup2"));
+ attributeValues.put("list3", Lists.newArrayList("val1", "val2"));
+
+ createRule(mneRuleClass, "ruleTestMNE", attributeValues, testRuleLocation);
+ }
+
+ public void testNonEmptyFail() throws Exception {
+ RuleClass mandNonEmptyRuleClass = setupNonEmpty(
+ attr("list", LABEL_LIST).nonEmpty().legacyAllowAnyFileType().build());
+
+ Map<String, Object> attributeValues = new LinkedHashMap<>();
+ attributeValues.put("list", Lists.newArrayList());
+
+ reporter.removeHandler(failFastHandler);
+ createRule(mandNonEmptyRuleClass, "ruleTestMNE", attributeValues, testRuleLocation);
+
+ assertSame(1, eventCollector.count());
+ assertContainsEvent(getErrorMsgNonEmptyList(
+ "list", "ruleMNE", "//testpackage:ruleTestMNE"));
+ }
+
+ private RuleClass setupNonEmpty(Attribute... attributes) {
+ RuleClass mandNonEmptyRuleClass = new RuleClass(
+ "ruleMNE", false, false, false, false, false, false,
+ ImplicitOutputsFunction.NONE, RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(), false, true,
+ attributes);
+ return mandNonEmptyRuleClass;
+ }
+
+ public void testNonEmptyWrongDefVal() throws Exception {
+ List<Label> emptyList = ImmutableList.of();
+ RuleClass mandNonEmptyRuleClass = new RuleClass(
+ "ruleMNE", false, false, false, false, false, false,
+ ImplicitOutputsFunction.NONE, RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(), false, true,
+ attr("list", LABEL_LIST).nonEmpty().legacyAllowAnyFileType().value(emptyList).build());
+
+ Map<String, Object> attributeValues = new LinkedHashMap<>();
+ reporter.removeHandler(failFastHandler);
+ createRule(mandNonEmptyRuleClass, "ruleTestMNE", attributeValues, testRuleLocation);
+
+ assertSame(1, eventCollector.count());
+
+ assertContainsEvent(getErrorMsgNonEmptyList(
+ "list", "ruleMNE", "//testpackage:ruleTestMNE"));
+ }
+
+ private Rule createRule(RuleClass ruleClass, String name, Map<String, Object> attributeValues,
+ Location location) throws SyntaxException {
+ Package.Builder pkgBuilder = createDummyPackageBuilder();
+ Label ruleLabel;
+ try {
+ ruleLabel = pkgBuilder.createLabel(name);
+ } catch (Label.SyntaxException e) {
+ throw new IllegalArgumentException("Rule has illegal label");
+ }
+ return ruleClass.createRuleWithLabel(pkgBuilder, ruleLabel, attributeValues,
+ reporter, null, location);
+ }
+
+ public void testOverrideWithWrongType() {
+ try {
+ RuleClass parentRuleClass = createParentRuleClass();
+
+ RuleClass.Builder childRuleClassBuilder = new RuleClass.Builder(
+ "child_rule", RuleClassType.NORMAL, false, parentRuleClass);
+ childRuleClassBuilder.override(attr("attr", INTEGER));
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("The type of the new attribute 'int' is different from "
+ + "the original one 'string'.");
+ }
+ }
+
+ public void testOverrideWithRightType() {
+ RuleClass parentRuleClass = createParentRuleClass();
+
+ RuleClass.Builder childRuleClassBuilder = new RuleClass.Builder(
+ "child_rule", RuleClassType.NORMAL, false, parentRuleClass);
+ childRuleClassBuilder.override(attr("attr", STRING));
+ }
+
+ public void testCopyAndOverrideAttribute() throws Exception {
+ RuleClass parentRuleClass = createParentRuleClass();
+ RuleClass childRuleClass = createChildRuleClass(parentRuleClass);
+
+ Map<String, Object> parentValues = new LinkedHashMap<>();
+ Map<String, Object> childValues = new LinkedHashMap<>();
+ childValues.put("attr", "somevalue");
+ createRule(parentRuleClass, "parent_rule", parentValues, testRuleLocation);
+ createRule(childRuleClass, "child_rule", childValues, testRuleLocation);
+ }
+
+ public void testCopyAndOverrideAttributeMandatoryMissing() throws Exception {
+ RuleClass parentRuleClass = createParentRuleClass();
+ RuleClass childRuleClass = createChildRuleClass(parentRuleClass);
+
+ Map<String, Object> childValues = new LinkedHashMap<>();
+ reporter.removeHandler(failFastHandler);
+ createRule(childRuleClass, "child_rule", childValues, testRuleLocation);
+
+ assertSame(1, eventCollector.count());
+ assertContainsEvent("//testpackage:child_rule: missing value for mandatory "
+ + "attribute 'attr' in 'child_rule' rule");
+ }
+
+ public void testRequiredFragmentInheritance() throws Exception {
+ RuleClass parentRuleClass = createParentRuleClass();
+ RuleClass childRuleClass = createChildRuleClass(parentRuleClass);
+ assertThat(parentRuleClass.getRequiredConfigurationFragments())
+ .containsExactly(DummyFragment.class);
+ assertThat(childRuleClass.getRequiredConfigurationFragments())
+ .containsExactly(DummyFragment.class);
+ }
+
+ private RuleClass createParentRuleClass() {
+ RuleClass parentRuleClass = new RuleClass("parent_rule", false, false, false, false, false,
+ false, ImplicitOutputsFunction.NONE, RuleClass.NO_CHANGE, DUMMY_CONFIGURED_TARGET_FACTORY,
+ PredicatesWithMessage.<Rule>alwaysTrue(), PREFERRED_DEPENDENCY_PREDICATE,
+ ImmutableSet.<Class<?>>of(), null, null, ImmutableSet.<Class<?>>of(DummyFragment.class),
+ false, true, attr("attr", STRING).build());
+ return parentRuleClass;
+ }
+
+ private RuleClass createChildRuleClass(RuleClass parentRuleClass) {
+ RuleClass.Builder childRuleClassBuilder = new RuleClass.Builder(
+ "child_rule", RuleClassType.NORMAL, false, parentRuleClass);
+ return childRuleClassBuilder.override(
+ childRuleClassBuilder
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .copy("attr").mandatory())
+ .add(attr("tags", STRING_LIST))
+ .build();
+ }
+
+ public void testValidityChecker() throws Exception {
+ RuleClass depClass = new RuleClass.Builder("dep", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("tags", STRING_LIST))
+ .build();
+ final Rule dep1 = createRule(depClass, "dep1", Collections.<String, Object>emptyMap(),
+ testRuleLocation);
+ final Rule dep2 = createRule(depClass, "dep2", Collections.<String, Object>emptyMap(),
+ testRuleLocation);
+
+ ValidityPredicate checker = new ValidityPredicate() {
+ @Override
+ public String checkValid(Rule from, Rule to) {
+ assertEquals("top", from.getName());
+ if (to.getName().equals("dep1")) {
+ return "pear";
+ } else if (to.getName().equals("dep2")) {
+ return null;
+ } else {
+ fail("invalid dependency");
+ return null;
+ }
+ }
+ };
+
+ RuleClass topClass = new RuleClass.Builder("top", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("tags", STRING_LIST))
+ .add(attr("deps", LABEL_LIST).legacyAllowAnyFileType()
+ .validityPredicate(checker))
+ .build();
+
+ Rule topRule = createRule(topClass, "top", Collections.<String, Object>emptyMap(),
+ testRuleLocation);
+
+ assertEquals("pear", topClass.getAttributeByName("deps").getValidityPredicate().checkValid(
+ topRule, dep1));
+ assertEquals(null, topClass.getAttributeByName("deps").getValidityPredicate().checkValid(
+ topRule, dep2));
+ }
+
+ /**
+ * Tests structure for making certain rules "preferential choices" for certain files
+ * under --compile_one_dependency.
+ */
+ public void testPreferredDependencyChecker() throws Exception {
+ final String cppFile = "file.cc";
+ final String textFile = "file.txt";
+
+ // Default: not preferred for anything.
+ RuleClass defaultClass = new RuleClass.Builder("defaultClass", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("tags", STRING_LIST))
+ .build();
+ final Rule defaultRule = createRule(defaultClass, "defaultRule",
+ Collections.<String, Object>emptyMap(), testRuleLocation);
+ assertFalse(defaultRule.getRuleClassObject().isPreferredDependency(cppFile));
+ assertFalse(defaultRule.getRuleClassObject().isPreferredDependency(textFile));
+
+ // Make a rule that's preferred for C++ sources.
+ RuleClass cppClass = new RuleClass.Builder("cppClass", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("tags", STRING_LIST))
+ .setPreferredDependencyPredicate(new Predicate<String>() {
+ @Override
+ public boolean apply(String filename) {
+ return filename.endsWith(".cc");
+ }
+ })
+ .build();
+ final Rule cppRule = createRule(cppClass, "cppRule",
+ Collections.<String, Object>emptyMap(), testRuleLocation);
+ assertTrue(cppRule.getRuleClassObject().isPreferredDependency(cppFile));
+ assertFalse(cppRule.getRuleClassObject().isPreferredDependency(textFile));
+ }
+
+ public void testBadRuleClassNames() {
+ expectError(RuleClassType.NORMAL, "8abc");
+ expectError(RuleClassType.NORMAL, "_abc");
+ expectError(RuleClassType.NORMAL, "a b");
+ }
+
+ private void expectError(RuleClassType type, String name) {
+ try {
+ type.checkName(name);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/TestSizeTest.java b/src/test/java/com/google/devtools/build/lib/packages/TestSizeTest.java
new file mode 100644
index 0000000..af3d458
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/TestSizeTest.java
@@ -0,0 +1,70 @@
+// Copyright 2012-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.packages;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests the various methods of {@link TestSize}
+ */
+@RunWith(JUnit4.class)
+public class TestSizeTest {
+
+ @Test
+ public void testBasicConversion() {
+ assertEquals(TestSize.valueOf("SMALL"), TestSize.SMALL);
+ assertEquals(TestSize.valueOf("MEDIUM"), TestSize.MEDIUM);
+ assertEquals(TestSize.valueOf("LARGE"), TestSize.LARGE);
+ assertEquals(TestSize.valueOf("ENORMOUS"), TestSize.ENORMOUS);
+ }
+
+ @Test
+ public void testGetDefaultTimeout() {
+ assertEquals(TestTimeout.SHORT, TestSize.SMALL.getDefaultTimeout());
+ assertEquals(TestTimeout.MODERATE, TestSize.MEDIUM.getDefaultTimeout());
+ assertEquals(TestTimeout.LONG, TestSize.LARGE.getDefaultTimeout());
+ assertEquals(TestTimeout.ETERNAL, TestSize.ENORMOUS.getDefaultTimeout());
+ }
+
+ @Test
+ public void testGetDefaultShards() {
+ assertEquals(2, TestSize.SMALL.getDefaultShards());
+ assertEquals(10, TestSize.MEDIUM.getDefaultShards());
+ assertEquals(20, TestSize.LARGE.getDefaultShards());
+ assertEquals(30, TestSize.ENORMOUS.getDefaultShards());
+ }
+
+ @Test
+ public void testGetTestSizeFromString() {
+ assertNull(TestSize.getTestSize("Small"));
+ assertNull(TestSize.getTestSize("Koala"));
+ assertEquals(TestSize.SMALL, TestSize.getTestSize("small"));
+ assertEquals(TestSize.MEDIUM, TestSize.getTestSize("medium"));
+ assertEquals(TestSize.LARGE, TestSize.getTestSize("large"));
+ assertEquals(TestSize.ENORMOUS, TestSize.getTestSize("enormous"));
+ }
+
+ @Test
+ public void testGetTestSizeFromDefaultTimeout() {
+ assertEquals(TestSize.SMALL, TestSize.getTestSize(TestTimeout.SHORT));
+ assertEquals(TestSize.MEDIUM, TestSize.getTestSize(TestTimeout.MODERATE));
+ assertEquals(TestSize.LARGE, TestSize.getTestSize(TestTimeout.LONG));
+ assertEquals(TestSize.ENORMOUS, TestSize.getTestSize(TestTimeout.ETERNAL));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/TestTimeoutTest.java b/src/test/java/com/google/devtools/build/lib/packages/TestTimeoutTest.java
new file mode 100644
index 0000000..d83c1e5
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/TestTimeoutTest.java
@@ -0,0 +1,93 @@
+// Copyright 2009-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.packages;
+
+import static com.google.devtools.build.lib.packages.TestTimeout.ETERNAL;
+import static com.google.devtools.build.lib.packages.TestTimeout.LONG;
+import static com.google.devtools.build.lib.packages.TestTimeout.MODERATE;
+import static com.google.devtools.build.lib.packages.TestTimeout.SHORT;
+import static com.google.devtools.build.lib.packages.TestTimeout.getSuggestedTestTimeout;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests the various methods of {@link TestTimeout}
+ */
+@RunWith(JUnit4.class)
+public class TestTimeoutTest {
+
+ @Test
+ public void testBasicConversion() throws Exception {
+ assertSame(SHORT, TestTimeout.valueOf("SHORT"));
+ assertSame(MODERATE, TestTimeout.valueOf("MODERATE"));
+ assertSame(LONG, TestTimeout.valueOf("LONG"));
+ assertSame(ETERNAL, TestTimeout.valueOf("ETERNAL"));
+ }
+
+ @Test
+ public void testSuggestedTestSize() throws Exception {
+ assertEquals(SHORT, getSuggestedTestTimeout(0));
+ assertEquals(SHORT, getSuggestedTestTimeout(2));
+ assertEquals(SHORT, getSuggestedTestTimeout(6));
+ assertEquals(SHORT, getSuggestedTestTimeout(59));
+ assertEquals(MODERATE, getSuggestedTestTimeout(60));
+ assertEquals(MODERATE, getSuggestedTestTimeout(299));
+ assertEquals(LONG, getSuggestedTestTimeout(300));
+ assertEquals(LONG, getSuggestedTestTimeout(899));
+ assertEquals(ETERNAL, getSuggestedTestTimeout(900));
+ assertEquals(ETERNAL, getSuggestedTestTimeout(1234567890));
+ }
+
+ @Test
+ public void testIsInRangeExact() throws Exception {
+ assertTrue(SHORT.isInRangeExact(0));
+ assertTrue(SHORT.isInRangeExact(1));
+ assertFalse(SHORT.isInRangeExact(60));
+ assertTrue(MODERATE.isInRangeExact(60));
+ assertTrue(MODERATE.isInRangeExact(299));
+ assertFalse(MODERATE.isInRangeExact(300));
+ assertTrue(LONG.isInRangeExact(300));
+ assertTrue(LONG.isInRangeExact(899));
+ assertFalse(LONG.isInRangeExact(900));
+ assertTrue(ETERNAL.isInRangeExact(900));
+ assertFalse(ETERNAL.isInRangeExact(1234567890));
+ }
+
+ @Test
+ public void testIsInRangeFuzzy() throws Exception {
+ assertFuzzyRange(SHORT, 0, 105);
+ assertFuzzyRange(MODERATE, 8, 525);
+ assertFuzzyRange(LONG, 75, 1575);
+ assertFuzzyRange(ETERNAL, 225, Integer.MAX_VALUE);
+ }
+
+ private void assertFuzzyRange(TestTimeout timeout, int min, int max) {
+ if (min > 0) {
+ assertFalse(timeout.isInRangeFuzzy(min - 1));
+ }
+ assertTrue(timeout.isInRangeFuzzy(min));
+ assertTrue(timeout.isInRangeFuzzy(min + 1));
+ assertTrue(timeout.isInRangeFuzzy(max - 1));
+ assertTrue(timeout.isInRangeFuzzy(max));
+ if (max < Integer.MAX_VALUE) {
+ assertFalse(timeout.isInRangeFuzzy(max + 1));
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/TypeTest.java b/src/test/java/com/google/devtools/build/lib/packages/TypeTest.java
new file mode 100644
index 0000000..39c5dbe
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/TypeTest.java
@@ -0,0 +1,744 @@
+// 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.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertSameContents;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.packages.Type.ConversionException;
+import com.google.devtools.build.lib.syntax.FilesetEntry;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.SelectorList;
+import com.google.devtools.build.lib.syntax.SelectorValue;
+import com.google.devtools.build.lib.testutil.MoreAsserts;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Test of type-conversions using Type.
+ */
+@RunWith(JUnit4.class)
+public class TypeTest {
+
+ private Label currentRule;
+
+ @Before
+ public void setUp() throws Exception {
+ this.currentRule = Label.parseAbsolute("//quux:baz");
+ }
+
+ @Test
+ public void testInteger() throws Exception {
+ Object x = 3;
+ assertEquals(x, Type.INTEGER.convert(x, null));
+ assertThat(Type.INTEGER.getLabels(x)).isEmpty();
+ }
+
+ @Test
+ public void testNonInteger() throws Exception {
+ try {
+ Type.INTEGER.convert("foo", null);
+ fail();
+ } catch (Type.ConversionException e) {
+ // This does not use assertMessageContainsWordsWithQuotes because at least
+ // one test should test exact wording (but they all shouldn't to make
+ // changing/improving the messages easy).
+ assertThat(e).hasMessage("expected value of type 'int', but got 'foo' (string)");
+ }
+ }
+
+ // Ensure that types are reported correctly.
+ @Test
+ public void testTypeErrorMessage() throws Exception {
+ try {
+ Type.STRING_LIST.convert("[(1,2), 3, 4]", "myexpr", null);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("expected value of type 'list(string)' for myexpr, "
+ + "but got '[(1,2), 3, 4]' (string)");
+ }
+ }
+
+ @Test
+ public void testString() throws Exception {
+ Object s = "foo";
+ assertEquals(s, Type.STRING.convert(s, null));
+ assertThat(Type.STRING.getLabels(s)).isEmpty();
+ }
+
+ @Test
+ public void testNonString() throws Exception {
+ try {
+ Type.STRING.convert(3, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "3", "string");
+ }
+ }
+
+ @Test
+ public void testBoolean() throws Exception {
+ Object myTrue = true;
+ Object myFalse = false;
+ assertEquals(Boolean.TRUE, Type.BOOLEAN.convert(1, null));
+ assertEquals(Boolean.FALSE, Type.BOOLEAN.convert(0, null));
+ assertTrue(Type.BOOLEAN.convert(true, null));
+ assertTrue(Type.BOOLEAN.convert(myTrue, null));
+ assertFalse(Type.BOOLEAN.convert(false, null));
+ assertFalse(Type.BOOLEAN.convert(myFalse, null));
+ assertThat(Type.BOOLEAN.getLabels(myTrue)).isEmpty();
+ }
+
+ @Test
+ public void testNonBoolean() throws Exception {
+ try {
+ Type.BOOLEAN.convert("unexpected", null);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "unexpected");
+ }
+ // Integers other than [0, 1] should fail.
+ try {
+ Type.BOOLEAN.convert(2, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertEquals(e.getMessage(), "boolean is not one of [0, 1]");
+ }
+ try {
+ Type.BOOLEAN.convert(-1, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertEquals(e.getMessage(), "boolean is not one of [0, 1]");
+ }
+ }
+
+ @Test
+ public void testTriState() throws Exception {
+ assertEquals(TriState.YES, Type.TRISTATE.convert(1, null));
+ assertEquals(TriState.NO, Type.TRISTATE.convert(0, null));
+ assertEquals(TriState.AUTO, Type.TRISTATE.convert(-1, null));
+ assertEquals(TriState.YES, Type.TRISTATE.convert(true, null));
+ assertEquals(TriState.NO, Type.TRISTATE.convert(false, null));
+ assertEquals(TriState.YES, Type.TRISTATE.convert(TriState.YES, null));
+ assertEquals(TriState.NO, Type.TRISTATE.convert(TriState.NO, null));
+ assertEquals(TriState.AUTO, Type.TRISTATE.convert(TriState.AUTO, null));
+ assertThat(Type.TRISTATE.getLabels(TriState.YES)).isEmpty();
+ }
+
+ @Test
+ public void testTriStateDoesNotAcceptArbitraryIntegers() throws Exception {
+ List<Integer> listOfCases = Lists.newArrayList(2, 3, -5, -2, 20);
+ for (Object entry : listOfCases) {
+ try {
+ Type.TRISTATE.convert(entry, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ // Expected.
+ }
+ }
+ }
+
+ @Test
+ public void testTriStateDoesNotAcceptStrings() throws Exception {
+ List<String> listOfCases = Lists.newArrayList("bad", "true", "auto", "false");
+ for (Object entry : listOfCases) {
+ try {
+ Type.TRISTATE.convert(entry, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ // Expected.
+ }
+ }
+ }
+
+ @Test
+ public void testTagConversion() throws Exception {
+ assertSameContents(Sets.newHashSet("attribute"),
+ Type.BOOLEAN.toTagSet(true, "attribute"));
+ assertSameContents(Sets.newHashSet("noattribute"),
+ Type.BOOLEAN.toTagSet(false, "attribute"));
+
+ assertSameContents(Sets.newHashSet("whiskey"),
+ Type.STRING.toTagSet("whiskey", "preferred_cocktail"));
+
+ assertSameContents(Sets.newHashSet("cheddar", "ementaler", "gruyere"),
+ Type.STRING_LIST.toTagSet(
+ Lists.newArrayList("cheddar", "ementaler", "gruyere"), "cheeses"));
+ }
+
+ @Test
+ public void testIllegalTagConversionByType() throws Exception {
+ try {
+ Type.TRISTATE.toTagSet(TriState.AUTO, "some_tristate");
+ fail("Expect UnsuportedOperationException");
+ } catch (UnsupportedOperationException e) {
+ // Success.
+ }
+ try {
+ Type.LICENSE.toTagSet(License.NO_LICENSE, "output_license");
+ fail("Expect UnsuportedOperationException");
+ } catch (UnsupportedOperationException e) {
+ // Success.
+ }
+ }
+
+ @Test
+ public void testIllegalTagConversIonFromNullOnSupportedType() throws Exception {
+ try {
+ Type.BOOLEAN.toTagSet(null, "a_boolean");
+ fail("Expect UnsuportedOperationException");
+ } catch (IllegalStateException e) {
+ // Success.
+ }
+ }
+
+ @Test
+ public void testLabel() throws Exception {
+ Label label = Label.parseAbsolute("//foo:bar");
+ assertEquals(label, Type.LABEL.convert("//foo:bar", null, currentRule));
+ assertThat(Type.LABEL.getLabels(label)).containsExactly(label);
+ }
+
+ @Test
+ public void testNodepLabel() throws Exception {
+ Label label = Label.parseAbsolute("//foo:bar");
+ assertEquals(label, Type.NODEP_LABEL.convert("//foo:bar", null, currentRule));
+ assertThat(Type.NODEP_LABEL.getLabels(label)).containsExactly(label);
+ }
+
+ @Test
+ public void testRelativeLabel() throws Exception {
+ assertEquals(Label.parseAbsolute("//quux:wiz"),
+ Type.LABEL.convert(":wiz", null, currentRule));
+ assertEquals(Label.parseAbsolute("//quux:wiz"),
+ Type.LABEL.convert("wiz", null, currentRule));
+ try {
+ Type.LABEL.convert("wiz", null);
+ fail();
+ } catch (NullPointerException e) {
+ /* ok */
+ }
+ }
+
+ @Test
+ public void testInvalidLabel() throws Exception {
+ try {
+ Type.LABEL.convert("not a label", null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "not a label");
+ }
+ }
+
+ @Test
+ public void testNonLabel() throws Exception {
+ try {
+ Type.LABEL.convert(3, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "3", "string");
+ }
+ }
+
+ @Test
+ public void testStringList() throws Exception {
+ Object input = Arrays.asList("foo", "bar", "wiz");
+ List<String> converted =
+ Type.STRING_LIST.convert(input, null);
+ assertEquals(input, converted);
+ assertNotSame(input, converted);
+ assertThat(Type.STRING_LIST.getLabels(input)).isEmpty();
+ }
+
+ @Test
+ public void testStringDict() throws Exception {
+ Object input = ImmutableMap.of("foo", "bar",
+ "wiz", "bang");
+ Map<String, String> converted = Type.STRING_DICT.convert(input, null);
+ assertEquals(input, converted);
+ assertNotSame(input, converted);
+ assertThat(Type.STRING_DICT.getLabels(converted)).isEmpty();
+ }
+
+ @Test
+ public void testStringDictBadElements() throws Exception {
+ Object input = ImmutableMap.of("foo", Arrays.asList("bar", "baz"),
+ "wiz", "bang");
+ try {
+ Type.STRING_DICT.convert(input, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("expected value of type 'string' for dict value element, but got "
+ + "'[\"bar\", \"baz\"]' (List)");
+ }
+ }
+
+ @Test
+ public void testNonStringList() throws Exception {
+ try {
+ Type.STRING_LIST.convert(3, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "3", "list(string)");
+ }
+ }
+
+ @Test
+ public void testStringListBadElements() throws Exception {
+ Object input = Arrays.<Object>asList("foo", "bar", 1);
+ try {
+ Type.STRING_LIST.convert(input, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "1", "string");
+ }
+ }
+
+ @Test
+ public void testLabelList() throws Exception {
+ Object input = Arrays.asList("//foo:bar", ":wiz");
+ List<Label> converted =
+ Type.LABEL_LIST.convert(input , null, currentRule);
+ List<Label> expected =
+ Arrays.asList(Label.parseAbsolute("//foo:bar"),
+ Label.parseAbsolute("//quux:wiz"));
+ assertEquals(expected, converted);
+ assertNotSame(expected, converted);
+ assertThat(Type.LABEL_LIST.getLabels(converted)).containsExactlyElementsIn(expected);
+ }
+
+ @Test
+ public void testNonLabelList() throws Exception {
+ try {
+ Type.LABEL_LIST.convert(3, null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "3", "list(label)");
+ }
+ }
+
+ @Test
+ public void testLabelListBadElements() throws Exception {
+ Object list = Arrays.<Object>asList("//foo:bar", 2, "foo");
+ try {
+ Type.LABEL_LIST.convert(list, null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "2", "string");
+ }
+ }
+
+ @Test
+ public void testLabelListSyntaxError() throws Exception {
+ Object list = Arrays.<Object>asList("//foo:bar/..", "foo");
+ try {
+ Type.LABEL_LIST.convert(list, "myexpr", currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("invalid label '//foo:bar/..' in element 0 of myexpr: "
+ + "invalid target name 'bar/..': "
+ + "target names may not contain up-level references '..'");
+ }
+ }
+
+ @Test
+ public void testLabelListDict() throws Exception {
+ Object input = ImmutableMap.of("foo", Arrays.asList("//foo:bar"),
+ "wiz", Arrays.asList(":bang"));
+ Map<String, List<Label>> converted = Type.LABEL_LIST_DICT.convert(input, null, currentRule);
+ Label fooLabel = Label.parseAbsolute("//foo:bar");
+ Label bangLabel = Label.parseAbsolute("//quux:bang");
+ Map<?, ?> expected = ImmutableMap.<String, List<Label>>of(
+ "foo", Arrays.<Label>asList(fooLabel),
+ "wiz", Arrays.<Label>asList(bangLabel));
+ assertEquals(expected, converted);
+ assertNotSame(expected, converted);
+ assertThat(Type.LABEL_LIST_DICT.getLabels(converted)).containsExactly(fooLabel, bangLabel);
+ }
+
+ @Test
+ public void testLabelListDictBadFirstElement() throws Exception {
+ Object input = ImmutableMap.of(2, Arrays.asList("//foo:bar"),
+ "wiz", Arrays.asList(":bang"));
+ try {
+ Type.LABEL_LIST_DICT.convert(input, null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("expected value of type 'string' for dict key element,"
+ + " but got '2' (int)");
+ }
+ }
+
+ @Test
+ public void testLabelListDictBadSecondElement() throws Exception {
+ Object input = ImmutableMap.of("foo", "//foo:bar",
+ "wiz", Arrays.asList(":bang"));
+ try {
+ Type.LABEL_LIST_DICT.convert(input, null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "//foo:bar", "list(label)");
+ }
+ }
+
+ @Test
+ public void testLabelListDictBadElements1() throws Exception {
+ Object input = ImmutableMap.of("foo", "bar",
+ "bar", Arrays.asList("//foo:bar"),
+ "wiz", Arrays.asList(":bang"));
+ try {
+ Type.LABEL_LIST_DICT.convert(input, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("expected value of type 'list(label)' for dict value element, "
+ + "but got 'bar' (string)");
+ }
+ }
+
+ @Test
+ public void testLabelListDictSyntaxError() throws Exception {
+ Object input = ImmutableMap.of("foo", Arrays.asList("//foo:.."),
+ "wiz", Arrays.asList(":bang"));
+ try {
+ Type.LABEL_LIST_DICT.convert(input, "baz", currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("invalid label '//foo:..' in element 0 of dict value element: "
+ + "invalid target name '..': "
+ + "target names may not contain up-level references '..'");
+ }
+ }
+
+ @Test
+ public void testStringListDict() throws Exception {
+ Object input = ImmutableMap.of("foo", Arrays.asList("foo", "bar"),
+ "wiz", Arrays.asList("bang"));
+ Map<String, List<String>> converted =
+ Type.STRING_LIST_DICT.convert(input, null, currentRule);
+ Map<?, ?> expected = ImmutableMap.<String, List<String>>of(
+ "foo", Arrays.asList("foo", "bar"),
+ "wiz", Arrays.asList("bang"));
+ assertEquals(expected, converted);
+ assertNotSame(expected, converted);
+ assertThat(Type.STRING_LIST_DICT.getLabels(converted)).isEmpty();
+ }
+
+ @Test
+ public void testStringListDictBadFirstElement() throws Exception {
+ Object input = ImmutableMap.of(2, Arrays.asList("foo", "bar"),
+ "wiz", Arrays.asList("bang"));
+ try {
+ Type.STRING_LIST_DICT.convert(input, null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage(
+ "expected value of type 'string' for dict key element, but got '2' (int)");
+ }
+ }
+
+ @Test
+ public void testStringListDictBadSecondElement() throws Exception {
+ Object input = ImmutableMap.of("foo", "bar",
+ "wiz", Arrays.asList("bang"));
+ try {
+ Type.STRING_LIST_DICT.convert(input, null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "bar", "list(string)");
+ }
+ }
+
+ @Test
+ public void testStringListDictBadElements1() throws Exception {
+ Object input = ImmutableMap.of(Arrays.asList("foo"), Arrays.asList("bang"),
+ "wiz", Arrays.asList("bang"));
+ try {
+ Type.STRING_LIST_DICT.convert(input, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("expected value of type 'string' for dict key element, but got "
+ + "'[\"foo\"]' (List)");
+ }
+ }
+
+ @Test
+ public void testStringDictUnary() throws Exception {
+ Object input = ImmutableMap.of("foo", "bar",
+ "wiz", "bang");
+ Map<?, ?> converted =
+ Type.STRING_DICT_UNARY.convert(input, null, currentRule);
+ Map<?, ?> expected = ImmutableMap.<String, String>of(
+ "foo", "bar",
+ "wiz", "bang");
+ assertEquals(expected, converted);
+ assertNotSame(expected, converted);
+ assertThat(Type.STRING_DICT_UNARY.getLabels(converted)).isEmpty();
+ }
+
+ @Test
+ public void testStringDictUnaryBadFirstElement() throws Exception {
+ Object input = ImmutableMap.of(2, Arrays.asList("foo", "bar"),
+ "wiz", Arrays.asList("bang"));
+ try {
+ Type.STRING_DICT_UNARY.convert(input, null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("expected value of type 'string' for dict key element, but got "
+ + "'2' (int)");
+ }
+ }
+
+ @Test
+ public void testStringDictUnaryBadSecondElement() throws Exception {
+ Object input = ImmutableMap.of("foo", "bar",
+ "wiz", Arrays.asList("bang"));
+ try {
+ Type.STRING_DICT_UNARY.convert(input, null, currentRule);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("expected value of type 'string' for dict value element, but got "
+ + "'[\"bang\"]' (List)");
+ }
+ }
+
+ @Test
+ public void testStringDictUnaryBadElements1() throws Exception {
+ Object input = ImmutableMap.of("foo", "bar",
+ Arrays.asList("foo", "bar"),
+ Arrays.<Object>asList("wiz", "bang"));
+ try {
+ Type.STRING_DICT_UNARY.convert(input, null);
+ fail();
+ } catch (Type.ConversionException e) {
+ assertThat(e).hasMessage("expected value of type 'string' for dict key element, but got "
+ + "'[\"foo\", \"bar\"]' (List)");
+ }
+ }
+
+ @Test
+ public void testStringDictThrowsConversionException() throws Exception {
+ try {
+ Type.STRING_DICT.convert("some string", null);
+ fail();
+ } catch (ConversionException e) {
+ assertThat(e).hasMessage("Expected a map for dictionary but got a java.lang.String");
+ }
+ }
+
+ @Test
+ public void testFilesetEntry() throws Exception {
+ Label srcDir = Label.create("foo", "src");
+ Label entryLabel = Label.create("foo", "entry");
+ FilesetEntry input =
+ new FilesetEntry(srcDir, ImmutableList.of(entryLabel), null, null, null, null);
+ assertEquals(input, Type.FILESET_ENTRY.convert(input, null, currentRule));
+ assertThat(Type.FILESET_ENTRY.getLabels(input)).containsExactly(entryLabel);
+ }
+
+ @Test
+ public void testFilesetEntryList() throws Exception {
+ Label srcDir = Label.create("foo", "src");
+ Label entry1Label = Label.create("foo", "entry1");
+ Label entry2Label = Label.create("foo", "entry");
+ List<FilesetEntry> input = ImmutableList.of(
+ new FilesetEntry(srcDir, ImmutableList.of(entry1Label), null, null, null, null),
+ new FilesetEntry(srcDir, ImmutableList.of(entry2Label), null, null, null, null));
+ assertEquals(input, Type.FILESET_ENTRY_LIST.convert(input, null, currentRule));
+ assertThat(Type.FILESET_ENTRY_LIST.getLabels(input)).containsExactly(entry1Label, entry2Label);
+ }
+
+ /**
+ * Tests basic {@link Type.Selector} functionality.
+ */
+ @Test
+ public void testSelector() throws Exception {
+ Object input = ImmutableMap.of(
+ "//conditions:a", "//a:a",
+ "//conditions:b", "//b:b",
+ Type.Selector.DEFAULT_CONDITION_KEY, "//d:d");
+ Type.Selector<Label> selector = new Type.Selector<>(input, null, currentRule, Type.LABEL);
+ assertEquals(Type.LABEL, selector.getOriginalType());
+
+ Map<Label, Label> expectedMap = ImmutableMap.of(
+ Label.parseAbsolute("//conditions:a"), Label.create("a", "a"),
+ Label.parseAbsolute("//conditions:b"), Label.create("b", "b"),
+ Label.parseAbsolute(Type.Selector.DEFAULT_CONDITION_KEY), Label.create("d", "d"));
+ assertSameContents(expectedMap.entrySet(), selector.getEntries().entrySet());
+ }
+
+ /**
+ * Tests that creating a {@link Type.Selector} over a mismatching native type triggers an
+ * exception.
+ */
+ @Test
+ public void testSelectorWrongType() throws Exception {
+ Object input = ImmutableMap.of(
+ "//conditions:a", "not a label",
+ "//conditions:b", "also not a label",
+ Type.Selector.DEFAULT_CONDITION_KEY, "whatever");
+ try {
+ new Type.Selector<Label>(input, null, currentRule, Type.LABEL);
+ fail("Expected Selector instantiation to fail since the input isn't a selection of labels");
+ } catch (ConversionException e) {
+ assertThat(e.getMessage()).contains("invalid label 'not a label'");
+ }
+ }
+
+ /**
+ * Tests that non-label selector keys trigger an exception.
+ */
+ @Test
+ public void testSelectorKeyIsNotALabel() throws Exception {
+ Object input = ImmutableMap.of(
+ "not a label", "//a:a",
+ Type.Selector.DEFAULT_CONDITION_KEY, "whatever");
+ try {
+ new Type.Selector<Label>(input, null, currentRule, Type.LABEL);
+ fail("Expected Selector instantiation to fail since the key isn't a label");
+ } catch (ConversionException e) {
+ assertThat(e.getMessage()).contains("invalid label 'not a label'");
+ }
+ }
+
+ /**
+ * Tests that {@link Type.Selector} correctly references its default value.
+ */
+ @Test
+ public void testSelectorDefault() throws Exception {
+ Object input = ImmutableMap.of(
+ "//conditions:a", "//a:a",
+ "//conditions:b", "//b:b",
+ Type.Selector.DEFAULT_CONDITION_KEY, "//d:d");
+ assertEquals(
+ Label.create("d", "d"),
+ new Type.Selector<Label>(input, null, currentRule, Type.LABEL).getDefault());
+ }
+
+ @Test
+ public void testSelectorList() throws Exception {
+ Object selector1 = new SelectorValue(ImmutableMap.of("//conditions:a",
+ ImmutableList.of("//a:a"), "//conditions:b", ImmutableList.of("//b:b")));
+ Object selector2 = new SelectorValue(ImmutableMap.of("//conditions:c",
+ ImmutableList.of("//c:c"), "//conditions:d", ImmutableList.of("//d:d")));
+ Type.SelectorList<List<Label>> selectorList = new Type.SelectorList<>(
+ ImmutableList.of(selector1, selector2), null, currentRule, Type.LABEL_LIST);
+
+ assertEquals(Type.LABEL_LIST, selectorList.getOriginalType());
+ assertSameContents(
+ ImmutableSet.of(
+ Label.parseAbsolute("//conditions:a"), Label.parseAbsolute("//conditions:b"),
+ Label.parseAbsolute("//conditions:c"), Label.parseAbsolute("//conditions:d")),
+ selectorList.getKeyLabels());
+
+ List<Type.Selector<List<Label>>> selectors = selectorList.getSelectors();
+ assertSameContents(
+ ImmutableMap.of(
+ Label.parseAbsolute("//conditions:a"), ImmutableList.of(Label.create("a", "a")),
+ Label.parseAbsolute("//conditions:b"), ImmutableList.of(Label.create("b", "b")))
+ .entrySet(),
+ selectors.get(0).getEntries().entrySet());
+ assertSameContents(
+ ImmutableMap.of(
+ Label.parseAbsolute("//conditions:c"), ImmutableList.of(Label.create("c", "c")),
+ Label.parseAbsolute("//conditions:d"), ImmutableList.of(Label.create("d", "d")))
+ .entrySet(),
+ selectors.get(1).getEntries().entrySet());
+ }
+
+ @Test
+ public void testSelectorListMixedTypes() throws Exception {
+ Object selector1 =
+ new SelectorValue(ImmutableMap.of("//conditions:a", ImmutableList.of("//a:a")));
+ Object selector2 =
+ new SelectorValue(ImmutableMap.of("//conditions:b", "//b:b"));
+ try {
+ new Type.SelectorList<>(ImmutableList.of(selector1, selector2), null, currentRule,
+ Type.LABEL_LIST);
+ fail("Expected SelectorList initialization to fail on mixed element types");
+ } catch (ConversionException e) {
+ assertThat(e.getMessage()).contains("expected value of type 'list(label)'");
+ }
+ }
+
+ /**
+ * Tests that {@link Type#selectableConvert} returns either the native type or a selector
+ * on that type, in accordance with the provided input.
+ */
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testSelectableConvert() throws Exception {
+ Object nativeInput = Arrays.asList("//a:a1", "//a:a2");
+ Object selectableInput =
+ SelectorList.of(new SelectorValue(ImmutableMap.of(
+ "//conditions:a", nativeInput,
+ Type.Selector.DEFAULT_CONDITION_KEY, nativeInput)));
+ List<Label> expectedLabels = ImmutableList.of(Label.create("a", "a1"), Label.create("a", "a2"));
+
+ // Conversion to direct type:
+ Object converted = Type.LABEL_LIST.selectableConvert(nativeInput, null, currentRule);
+ assertTrue(converted instanceof List<?>);
+ assertSameContents(expectedLabels, (List<Label>) converted);
+
+ // Conversion to selectable type:
+ converted = Type.LABEL_LIST.selectableConvert(selectableInput, null, currentRule);
+ Type.SelectorList<?> selectorList = (Type.SelectorList<?>) converted;
+ assertSameContents(
+ ImmutableMap.of(
+ Label.parseAbsolute("//conditions:a"), expectedLabels,
+ Label.parseAbsolute(Type.Selector.DEFAULT_CONDITION_KEY), expectedLabels).entrySet(),
+ ((Type.Selector<Label>) selectorList.getSelectors().get(0)).getEntries().entrySet());
+ }
+
+ /**
+ * Tests that {@link Type#convert} fails on selector inputs.
+ */
+ @Test
+ public void testConvertDoesNotAcceptSelectables() throws Exception {
+ Object selectableInput = SelectorList.of(
+ new SelectorValue(ImmutableMap.of("//conditions:a", Arrays.asList("//a:a1", "//a:a2"))));
+ try {
+ Type.LABEL_LIST.convert(selectableInput, null, currentRule);
+ fail("Expected conversion to fail on a selectable input");
+ } catch (ConversionException e) {
+ assertThat(e.getMessage()).contains("expected value of type 'list(label)'");
+ }
+ }
+
+ /**
+ * Tests for "reserved" key labels (i.e. not intended to map to actual targets).
+ */
+ @Test
+ public void testReservedKeyLabels() throws Exception {
+ assertFalse(Type.Selector.isReservedLabel(Label.parseAbsolute("//condition:a")));
+ assertTrue(Type.Selector.isReservedLabel(
+ Label.parseAbsolute(Type.Selector.DEFAULT_CONDITION_KEY)));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/PackageLoadingTestCase.java b/src/test/java/com/google/devtools/build/lib/packages/util/PackageLoadingTestCase.java
new file mode 100644
index 0000000..8d7ee5b
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/PackageLoadingTestCase.java
@@ -0,0 +1,244 @@
+// 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.packages.util;
+
+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.common.collect.Sets;
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
+import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
+import com.google.devtools.build.lib.packages.ConstantRuleVisibility;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.NoSuchTargetException;
+import com.google.devtools.build.lib.packages.NoSuchThingException;
+import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
+import com.google.devtools.build.lib.packages.Preprocessor;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
+import com.google.devtools.build.lib.pkgcache.PackageManager;
+import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
+import com.google.devtools.build.lib.skyframe.DiffAwareness;
+import com.google.devtools.build.lib.skyframe.PrecomputedValue;
+import com.google.devtools.build.lib.skyframe.SequencedSkyframeExecutor;
+import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.Label.SyntaxException;
+import com.google.devtools.build.lib.testutil.FoundationTestCase;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+import com.google.devtools.build.lib.util.BlazeClock;
+import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
+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.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.common.options.OptionsParser;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * This is a specialization of {@link FoundationTestCase} that's useful for
+ * implementing tests of the "packages" library.
+ */
+public abstract class PackageLoadingTestCase extends FoundationTestCase {
+
+ protected ConfiguredRuleClassProvider ruleClassProvider;
+ private SkyframeExecutor skyframeExecutor;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ ruleClassProvider = TestRuleClassProvider.getRuleClassProvider();
+ skyframeExecutor = SequencedSkyframeExecutor.create(reporter,
+ new PackageFactory(ruleClassProvider, getEnvironmentExtensions()),
+ new TimestampGranularityMonitor(BlazeClock.instance()),
+ new BlazeDirectories(outputBase, outputBase, rootDirectory),
+ null, /* workspaceStatusActionFactory */
+ ruleClassProvider.getBuildInfoFactories(),
+ ImmutableSet.<Path>of(),
+ ImmutableList.<DiffAwareness.Factory>of(),
+ Predicates.<PathFragment>alwaysFalse(),
+ Preprocessor.Factory.Supplier.NullSupplier.INSTANCE,
+ ImmutableMap.<SkyFunctionName, SkyFunction>of(),
+ ImmutableList.<PrecomputedValue.Injected>of()
+ );
+ skyframeExecutor.preparePackageLoading(
+ new PathPackageLocator(rootDirectory), ConstantRuleVisibility.PUBLIC, true, "",
+ UUID.randomUUID());
+ setUpSkyframe(parsePackageCacheOptions());
+ }
+
+ protected Iterable<EnvironmentExtension> getEnvironmentExtensions() {
+ return ImmutableList.<EnvironmentExtension>of();
+ }
+
+ private void setUpSkyframe(PackageCacheOptions packageCacheOptions) {
+ PathPackageLocator pkgLocator = PathPackageLocator.create(
+ packageCacheOptions.packagePath, reporter, rootDirectory, rootDirectory);
+ skyframeExecutor.preparePackageLoading(pkgLocator,
+ packageCacheOptions.defaultVisibility, true,
+ ruleClassProvider.getDefaultsPackageContent(),
+ UUID.randomUUID());
+ skyframeExecutor.setDeletedPackages(ImmutableSet.copyOf(packageCacheOptions.deletedPackages));
+ }
+
+ private PackageCacheOptions parsePackageCacheOptions(String... options) throws Exception {
+ OptionsParser parser = OptionsParser.newOptionsParser(PackageCacheOptions.class);
+ parser.parse(new String[] { "--default_visibility=public" });
+ parser.parse(options);
+ return parser.getOptions(PackageCacheOptions.class);
+ }
+
+ protected void setPackageCacheOptions(String... options) throws Exception {
+ setUpSkyframe(parsePackageCacheOptions(options));
+ }
+
+ protected Target getTarget(String label)
+ throws NoSuchPackageException, NoSuchTargetException,
+ Label.SyntaxException, InterruptedException {
+ return getTarget(Label.parseAbsolute(label));
+ }
+
+ protected Target getTarget(Label label)
+ throws NoSuchPackageException, NoSuchTargetException, InterruptedException {
+ return getPackageManager().getTarget(reporter, label);
+ }
+
+ /**
+ * Create and return a scratch rule.
+ *
+ * @param packageName the package name of the rule.
+ * @param ruleName the name of the rule.
+ * @param lines the text of the rule.
+ * @return the rule instance for the created rule.
+ * @throws IOException
+ * @throws Exception
+ */
+ protected Rule scratchRule(String packageName, String ruleName, String... lines)
+ throws Exception {
+ scratch.file(packageName + "/BUILD", lines);
+ return (Rule) getTarget("//" + packageName + ":" + ruleName);
+ }
+
+ /**
+ * A Utility method that generates build file rules for tests.
+ * @param rule the name of the rule class.
+ * @param name the name of the rule instance.
+ * @param body an array of strings containing the contents of the rule.
+ * @return a string containing the build file rule.
+ */
+ protected String genRule(String rule, String name, String... body) {
+ StringBuilder buf = new StringBuilder();
+ buf.append(rule);
+ buf.append("(name='");
+ buf.append(name);
+ buf.append("',\n");
+ for (String line : body) {
+ buf.append(line);
+ }
+ buf.append(")\n");
+ return buf.toString();
+ }
+
+ /**
+ * A utility function which generates the "deps" clause for a build file
+ * rule from a list of targets.
+ * @param depTargets the list of targets.
+ * @return a string containing the deps clause
+ */
+ protected static String deps(String... depTargets) {
+ StringBuilder buf = new StringBuilder();
+ buf.append(" deps=[");
+ String sep = "'";
+ for (String dep : depTargets) {
+ buf.append(sep);
+ buf.append(dep);
+ buf.append("'");
+ sep = ", '";
+ }
+ buf.append("]");
+ return buf.toString();
+ }
+
+ /**
+ * Utility method for tests. Converts an array of strings into a set of labels.
+ *
+ * @param strings the set of strings to be converted to labels.
+ * @throws SyntaxException if there are any syntax errors in the strings.
+ */
+ public static Set<Label> asLabelSet(String... strings) throws SyntaxException {
+ return asLabelSet(ImmutableList.copyOf(strings));
+ }
+
+ /**
+ * Utility method for tests. Converts an array of strings into a set of labels.
+ *
+ * @param strings the set of strings to be converted to labels.
+ * @throws SyntaxException if there are any syntax errors in the strings.
+ */
+ public static Set<Label> asLabelSet(Iterable<String> strings) throws SyntaxException {
+ Set<Label> result = Sets.newTreeSet();
+ for (String s : strings) {
+ result.add(Label.parseAbsolute(s));
+ }
+ return result;
+ }
+
+ protected final Set<Target> asTargetSet(String... strLabels)
+ throws SyntaxException, NoSuchThingException, InterruptedException {
+ return asTargetSet(Arrays.asList(strLabels));
+ }
+
+ protected Set<Target> asTargetSet(Iterable<String> strLabels)
+ throws SyntaxException, NoSuchThingException, InterruptedException {
+ Set<Target> targets = new HashSet<>();
+ for (String strLabel : strLabels) {
+ targets.add(getTarget(strLabel));
+ }
+ return targets;
+ }
+
+ protected PackageManager getPackageManager() {
+ return skyframeExecutor.getPackageManager();
+ }
+
+ protected SkyframeExecutor getSkyframeExecutor() {
+ return skyframeExecutor;
+ }
+
+ /**
+ * Invalidates all existing packages below the usual rootDirectory. Must be called _after_ the
+ * files are modified.
+ *
+ * @throws InterruptedException
+ */
+ protected void invalidatePackages() throws InterruptedException {
+ skyframeExecutor.invalidateFilesUnderPathForTesting(ModifiedFileSet.EVERYTHING_MODIFIED,
+ rootDirectory);
+ }
+
+ protected String getErrorMsgNonEmptyList(String attrName, String ruleType, String ruleName) {
+ return "non empty attribute '" + attrName + "' in '" + ruleType
+ + "' rule '" + ruleName + "' has to have at least one value";
+ }
+}