aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google/devtools/build/lib
diff options
context:
space:
mode:
authorGravatar Han-Wen Nienhuys <hanwen@google.com>2015-10-21 15:03:34 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2015-10-22 15:14:14 +0000
commit3428dc9e92a5422106ed1adf3c19304e62357c06 (patch)
tree7628b0aa25ecf9e0bbfc55158f84943dac750963 /src/test/java/com/google/devtools/build/lib
parent155f2195c50e4e5217c68e5643254a0d7f845621 (diff)
Open source more tests for packages/
Tested: bazel test on merge-to-os-blaze.sh checked total test method count on blaze test //javatests/com/google/devtools/build/lib:PackagesTests //third_party/bazel/src/test/java/com/google/devtools/build/lib:packages-tests -- MOS_MIGRATED_REVID=105963077
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib')
-rw-r--r--src/test/java/com/google/devtools/build/lib/buildtool/SubincludePreprocessorModule.java46
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/AttributeContainerTest.java102
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/AttributeTest.java8
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/EnvironmentGroupTest.java96
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/ExternalPackageTest.java86
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/ImplicitOutputsFunctionTest.java220
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/InputFileTest.java105
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/LicenseTest.java40
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/OutputFileTest.java206
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java95
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java143
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/ProtoUtilsTest.java54
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/RelativePackageNameResolverTest.java122
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/RuleClassBuilderTest.java177
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java245
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/SubincludePreprocessorTest.java128
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/TestSizeTest.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/TestTargetUtilsTest.java235
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/TestTimeoutTest.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/util/PackageLoadingTestCase.java2
-rw-r--r--src/test/java/com/google/devtools/build/lib/packages/util/SubincludePreprocessor.java156
25 files changed, 2267 insertions, 11 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/buildtool/SubincludePreprocessorModule.java b/src/test/java/com/google/devtools/build/lib/buildtool/SubincludePreprocessorModule.java
new file mode 100644
index 0000000000..0872184042
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/buildtool/SubincludePreprocessorModule.java
@@ -0,0 +1,46 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.buildtool;
+
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
+import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
+import com.google.devtools.build.lib.packages.Preprocessor;
+import com.google.devtools.build.lib.packages.util.SubincludePreprocessor;
+import com.google.devtools.build.lib.runtime.BlazeModule;
+import com.google.devtools.build.lib.util.AbruptExitException;
+import com.google.devtools.build.lib.util.Clock;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.common.options.OptionsProvider;
+
+import java.util.UUID;
+
+public class SubincludePreprocessorModule extends BlazeModule {
+ private FileSystem fileSystem;
+
+ @Override
+ public void blazeStartup(
+ OptionsProvider startupOptions,
+ BlazeVersionInfo versionInfo,
+ UUID instanceId,
+ BlazeDirectories directories,
+ Clock clock)
+ throws AbruptExitException {
+ this.fileSystem = directories.getFileSystem();
+ }
+
+ @Override
+ public Preprocessor.Factory.Supplier getPreprocessorFactorySupplier() {
+ return new SubincludePreprocessor.FactorySupplier(fileSystem);
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/AttributeContainerTest.java b/src/test/java/com/google/devtools/build/lib/packages/AttributeContainerTest.java
new file mode 100644
index 0000000000..a541bb867e
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/AttributeContainerTest.java
@@ -0,0 +1,102 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.events.Location.LineAndColumn;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for {@link AttributeContainer}.
+ */
+@RunWith(JUnit4.class)
+public class AttributeContainerTest {
+
+ private RuleClass ruleClass;
+ private AttributeContainer container;
+ private Attribute attribute1;
+ private Attribute attribute2;
+
+ @Before
+ public void setUp() throws Exception {
+ ruleClass =
+ TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("testing_dummy_rule");
+ attribute1 = ruleClass.getAttributeByName("srcs");
+ attribute2 = ruleClass.getAttributeByName("dummyinteger");
+ container = new AttributeContainer(ruleClass);
+ }
+
+ @Test
+ public void testAttributeSettingAndRetrievalByName() throws Exception {
+ Object someValue1 = new Object();
+ Object someValue2 = new Object();
+ container.setAttributeValueByName(attribute1.getName(), someValue1);
+ container.setAttributeValueByName(attribute2.getName(), someValue2);
+ assertEquals(someValue1, container.getAttr(attribute1.getName()));
+ assertEquals(someValue2, container.getAttr(attribute2.getName()));
+ assertNull(container.getAttr("nomatch"));
+ }
+
+ @Test
+ public void testAttributeSettingAndRetrievalByInstance() throws Exception {
+ Object someValue1 = new Object();
+ Object someValue2 = new Object();
+ container.setAttributeValue(attribute1, someValue1, true);
+ container.setAttributeValue(attribute2, someValue2, true);
+ assertEquals(someValue1, container.getAttr(attribute1));
+ assertEquals(someValue2, container.getAttr(attribute2));
+ }
+
+ @Test
+ public void testExplicitSpecificationsByName() throws Exception {
+ // Name-based setters are automatically considered explicit.
+ container.setAttributeValueByName(attribute1.getName(), new Object());
+ assertTrue(container.isAttributeValueExplicitlySpecified(attribute1));
+ assertFalse(container.isAttributeValueExplicitlySpecified("nomatch"));
+ }
+
+ @Test
+ public void testExplicitSpecificationsByInstance() throws Exception {
+ Object someValue = new Object();
+ container.setAttributeValue(attribute1, someValue, true);
+ container.setAttributeValue(attribute2, someValue, false);
+ assertTrue(container.isAttributeValueExplicitlySpecified(attribute1));
+ assertFalse(container.isAttributeValueExplicitlySpecified(attribute2));
+ }
+
+ private static Location newLocation() {
+ return Location.fromPathAndStartColumn(null, 0, 0, new LineAndColumn(0, 0));
+ }
+
+ @Test
+ public void testAttributeLocation() throws Exception {
+ Location location1 = newLocation();
+ Location location2 = newLocation();
+ container.setAttributeLocation(attribute1, location1);
+ container.setAttributeLocation(attribute2, location2);
+ assertEquals(location1, container.getAttributeLocation(attribute1.getName()));
+ assertEquals(location2, container.getAttributeLocation(attribute2.getName()));
+ assertNull(container.getAttributeLocation("nomatch"));
+ }
+}
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
index aefe88ad6d..40a7aa66e3 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/AttributeTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/AttributeTest.java
@@ -1,4 +1,4 @@
-// Copyright 2006 The Bazel Authors. All rights reserved.
+// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -206,14 +206,14 @@ public class AttributeTest {
@Test
public void testCloneBuilder() {
FileTypeSet txtFiles = FileTypeSet.of(FileType.of("txt"));
- RuleClass.Builder.RuleClassNamePredicate ruleClasses =
+ 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());
diff --git a/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java b/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java
index 85a7aa6440..9cec42f877 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java
@@ -1,4 +1,4 @@
-// Copyright 2006 The Bazel Authors. All rights reserved.
+// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/src/test/java/com/google/devtools/build/lib/packages/EnvironmentGroupTest.java b/src/test/java/com/google/devtools/build/lib/packages/EnvironmentGroupTest.java
new file mode 100644
index 0000000000..60629d806e
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/EnvironmentGroupTest.java
@@ -0,0 +1,96 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+import com.google.devtools.build.lib.vfs.Path;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link EnvironmentGroup}. Note input validation is handled in
+ * {@link PackageFactoryTest}.
+ */
+@RunWith(JUnit4.class)
+public class EnvironmentGroupTest extends PackageLoadingTestCase {
+
+ private Package pkg;
+ private EnvironmentGroup group;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ Path buildfile =
+ scratch.file(
+ "pkg/BUILD",
+ "environment(name='foo', fulfills = [':bar', ':baz'])",
+ "environment(name='bar', fulfills = [':baz'])",
+ "environment(name='baz')",
+ "environment(name='not_in_group')",
+ "environment_group(",
+ " name = 'group',",
+ " environments = [':foo', ':bar', ':baz'],",
+ " defaults = [':foo'],",
+ ")");
+ PackageFactory pkgFactory = new PackageFactory(TestRuleClassProvider.getRuleClassProvider());
+ pkg =
+ pkgFactory.createPackageForTesting(
+ PackageIdentifier.createInDefaultRepo("pkg"), buildfile, getPackageManager(), reporter);
+
+ group = (EnvironmentGroup) pkg.getTarget("group");
+ }
+
+ @Test
+ public void testGroupMembership() throws Exception {
+ assertEquals(
+ ImmutableSet.of(
+ Label.parseAbsolute("//pkg:foo"),
+ Label.parseAbsolute("//pkg:bar"),
+ Label.parseAbsolute("//pkg:baz")),
+ group.getEnvironments());
+ }
+
+ @Test
+ public void testDefaultsMembership() throws Exception {
+ assertEquals(ImmutableSet.of(Label.parseAbsolute("//pkg:foo")), group.getDefaults());
+ }
+
+ @Test
+ public void testIsDefault() throws Exception {
+ assertTrue(group.isDefault(Label.parseAbsolute("//pkg:foo")));
+ assertFalse(group.isDefault(Label.parseAbsolute("//pkg:bar")));
+ assertFalse(group.isDefault(Label.parseAbsolute("//pkg:baz")));
+ assertFalse(group.isDefault(Label.parseAbsolute("//pkg:not_in_group")));
+ }
+
+ @Test
+ public void testFulfillers() throws Exception {
+ assertThat(group.getFulfillers(Label.parseAbsolute("//pkg:baz")))
+ .containsExactly(Label.parseAbsolute("//pkg:foo"), Label.parseAbsolute("//pkg:bar"));
+ assertThat(group.getFulfillers(Label.parseAbsolute("//pkg:bar")))
+ .containsExactly(Label.parseAbsolute("//pkg:foo"));
+ assertThat(group.getFulfillers(Label.parseAbsolute("//pkg:foo"))).isEmpty();
+ }
+}
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
index 5910397261..75124fbcac 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/ExportsFilesTest.java
@@ -1,4 +1,4 @@
-// Copyright 2006 The Bazel Authors. All rights reserved.
+// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/src/test/java/com/google/devtools/build/lib/packages/ExternalPackageTest.java b/src/test/java/com/google/devtools/build/lib/packages/ExternalPackageTest.java
new file mode 100644
index 0000000000..3d83246b52
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/ExternalPackageTest.java
@@ -0,0 +1,86 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.Package.Builder;
+import com.google.devtools.build.lib.syntax.Argument;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
+import com.google.devtools.build.lib.syntax.Identifier;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+import com.google.devtools.build.lib.vfs.Path;
+
+import java.util.Map;
+
+/**
+ * Test for building external packages.
+ */
+public class ExternalPackageTest extends BuildViewTestCase {
+
+ private Path workspacePath;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ workspacePath = getOutputPath().getRelative("WORKSPACE");
+ }
+
+ public void testWorkspaceName() {
+ Builder builder = Package.newExternalPackageBuilder(workspacePath, "TESTING");
+ builder.setWorkspaceName("foo");
+ assertEquals("foo", builder.build().getWorkspaceName());
+ }
+
+ public void testMultipleRulesWithSameName() throws Exception {
+ Builder builder = Package.newExternalPackageBuilder(workspacePath, "TESTING");
+
+ // The WORKSPACE file allows rules to be overridden, but the TestRuleClassProvider doesn't
+ // provide WORKSPACE rules (new_local_repo et al). So for the test, we create an
+ // ExternalPackage with BUILD rules, even though these rules wouldn't ordinarily be added to
+ // ExternalPackage.
+ Location buildFile = Location.fromFile(getOutputPath().getRelative("BUILD"));
+
+ // Add first rule.
+ RuleClass ruleClass =
+ TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("cc_library");
+ RuleClass bindRuleClass =
+ TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("bind");
+
+ Map<String, Object> kwargs = ImmutableMap.of("name", (Object) "my-rule");
+ FuncallExpression ast =
+ new FuncallExpression(
+ new Identifier(ruleClass.getName()), Lists.<Argument.Passed>newArrayList());
+ ast.setLocation(buildFile);
+ builder
+ .externalPackageData()
+ .createAndAddRepositoryRule(builder, ruleClass, bindRuleClass, kwargs, ast);
+
+ // Add another rule with the same name.
+ ruleClass = TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("sh_test");
+ ast =
+ new FuncallExpression(
+ new Identifier(ruleClass.getName()), Lists.<Argument.Passed>newArrayList());
+ ast.setLocation(buildFile);
+ builder
+ .externalPackageData()
+ .createAndAddRepositoryRule(builder, ruleClass, bindRuleClass, kwargs, ast);
+ Package pkg = builder.build();
+
+ // Make sure the second rule "wins."
+ assertEquals("sh_test rule", pkg.getTarget("my-rule").getTargetKind());
+ }
+}
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
index 4e451e385d..64e028d457 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/GlobCacheTest.java
@@ -1,4 +1,4 @@
-// Copyright 2008 The Bazel Authors. All rights reserved.
+// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/src/test/java/com/google/devtools/build/lib/packages/ImplicitOutputsFunctionTest.java b/src/test/java/com/google/devtools/build/lib/packages/ImplicitOutputsFunctionTest.java
new file mode 100644
index 0000000000..32719dfb10
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/ImplicitOutputsFunctionTest.java
@@ -0,0 +1,220 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.AttributeValueGetter;
+import com.google.devtools.build.lib.testutil.Suite;
+import com.google.devtools.build.lib.testutil.TestSpec;
+
+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.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for {@link ImplicitOutputsFunction}.
+ */
+@TestSpec(size = Suite.SMALL_TESTS)
+@RunWith(JUnit4.class)
+public final class ImplicitOutputsFunctionTest {
+ private void assertPlaceholderCollection(
+ String template, String expectedTemplate, String... expectedPlaceholders) throws Exception {
+ List<String> actualPlaceholders = new ArrayList<>();
+ assertEquals(
+ expectedTemplate,
+ ImplicitOutputsFunction.createPlaceholderSubstitutionFormatString(
+ template, actualPlaceholders));
+ assertThat(actualPlaceholders)
+ .containsExactlyElementsIn(Arrays.asList(expectedPlaceholders))
+ .inOrder();
+ }
+
+ @Test
+ public void testNoPlaceholder() throws Exception {
+ assertPlaceholderCollection("foo", "foo");
+ }
+
+ @Test
+ public void testJustPlaceholder() throws Exception {
+ assertPlaceholderCollection("%{foo}", "%s", "foo");
+ }
+
+ @Test
+ public void testPrefixedPlaceholder() throws Exception {
+ assertPlaceholderCollection("foo%{bar}", "foo%s", "bar");
+ }
+
+ @Test
+ public void testSuffixedPlaceholder() throws Exception {
+ assertPlaceholderCollection("%{foo}bar", "%sbar", "foo");
+ }
+
+ @Test
+ public void testMultiplePlaceholdersPrefixed() throws Exception {
+ assertPlaceholderCollection("foo%{bar}baz%{qux}", "foo%sbaz%s", "bar", "qux");
+ }
+
+ @Test
+ public void testMultiplePlaceholdersSuffixed() throws Exception {
+ assertPlaceholderCollection("%{foo}bar%{baz}qux", "%sbar%squx", "foo", "baz");
+ }
+
+ @Test
+ public void testTightlyPackedPlaceholders() throws Exception {
+ assertPlaceholderCollection("%{foo}%{bar}%{baz}", "%s%s%s", "foo", "bar", "baz");
+ }
+
+ @Test
+ public void testIncompletePlaceholder() throws Exception {
+ assertPlaceholderCollection("%{foo", "%%{foo");
+ }
+
+ @Test
+ public void testCompleteAndIncompletePlaceholder() throws Exception {
+ assertPlaceholderCollection("%{foo}%{bar", "%s%%{bar", "foo");
+ }
+
+ @Test
+ public void testPlaceholderLooksLikeNestedIncompletePlaceholder() throws Exception {
+ assertPlaceholderCollection("%{%{foo", "%%{%%{foo");
+ }
+
+ @Test
+ public void testPlaceholderLooksLikeNestedPlaceholder() throws Exception {
+ assertPlaceholderCollection("%{%{foo}", "%s", "%{foo");
+ }
+
+ @Test
+ public void testEscapesJustPercentSign() throws Exception {
+ assertPlaceholderCollection("%", "%%");
+ }
+
+ @Test
+ public void testEscapesPrintfPlaceholder() throws Exception {
+ assertPlaceholderCollection("%{x}%s%{y}", "%s%%s%s", "x", "y");
+ }
+
+ @Test
+ public void testEscapesPercentSign() throws Exception {
+ assertPlaceholderCollection("foo%{bar}%baz", "foo%s%%baz", "bar");
+ }
+
+ private static AttributeValueGetter attrs(
+ final Map<String, ? extends Collection<String>> values) {
+ return new AttributeValueGetter() {
+ @Override
+ public Set<String> get(AttributeMap ignored, String attr) {
+ return new LinkedHashSet<>(Preconditions.checkNotNull(values.get(attr)));
+ }
+ };
+ }
+
+ private void assertPlaceholderSubtitution(
+ String template,
+ AttributeValueGetter attrValues,
+ String[] expectedSubstitutions,
+ String[] expectedFoundPlaceholders)
+ throws Exception {
+ List<String> foundAttributes = new ArrayList<>();
+ List<String> substitutions =
+ ImplicitOutputsFunction.substitutePlaceholderIntoTemplate(
+ template, null, attrValues, foundAttributes);
+ assertThat(foundAttributes)
+ .containsExactlyElementsIn(Arrays.asList(expectedFoundPlaceholders))
+ .inOrder();
+ assertThat(substitutions).containsExactlyElementsIn(Arrays.asList(expectedSubstitutions));
+ }
+
+ @Test
+ public void testSingleScalarElementSubstitution() throws Exception {
+ assertPlaceholderSubtitution(
+ "%{x}",
+ attrs(ImmutableMap.of("x", ImmutableList.of("a"))),
+ new String[] {"a"},
+ new String[] {"x"});
+ }
+
+ @Test
+ public void testSingleVectorElementSubstitution() throws Exception {
+ assertPlaceholderSubtitution(
+ "%{x}",
+ attrs(ImmutableMap.of("x", ImmutableList.of("a", "b", "c"))),
+ new String[] {"a", "b", "c"},
+ new String[] {"x"});
+ }
+
+ @Test
+ public void testMultipleElementsSubstitution() throws Exception {
+ assertPlaceholderSubtitution(
+ "%{x}-%{y}-%{z}",
+ attrs(
+ ImmutableMap.of(
+ "x", ImmutableList.of("foo", "bar", "baz"),
+ "y", ImmutableList.of("meow"),
+ "z", ImmutableList.of("1", "2"))),
+ new String[] {
+ "foo-meow-1", "foo-meow-2", "bar-meow-1", "bar-meow-2", "baz-meow-1", "baz-meow-2"
+ },
+ new String[] {"x", "y", "z"});
+ }
+
+ @Test
+ public void testEmptyElementSubstitution() throws Exception {
+ assertPlaceholderSubtitution(
+ "a-%{x}",
+ attrs(ImmutableMap.of("x", ImmutableList.<String>of())),
+ new String[0],
+ new String[] {"x"});
+ }
+
+ @Test
+ public void testSamePlaceholderMultipleTimes() throws Exception {
+ assertPlaceholderSubtitution(
+ "%{x}-%{y}-%{x}",
+ attrs(ImmutableMap.of("x", ImmutableList.of("a", "b"), "y", ImmutableList.of("1", "2"))),
+ new String[] {"a-1-a", "a-1-b", "a-2-a", "a-2-b", "b-1-a", "b-1-b", "b-2-a", "b-2-b"},
+ new String[] {"x", "y", "x"});
+ }
+
+ @Test
+ public void testRepeatingPlaceholderValue() throws Exception {
+ assertPlaceholderSubtitution(
+ "%{x}",
+ attrs(ImmutableMap.of("x", ImmutableList.of("a", "a"))),
+ new String[] {"a"},
+ new String[] {"x"});
+ }
+
+ @Test
+ public void testIncompletePlaceholderTreatedAsText() throws Exception {
+ assertPlaceholderSubtitution(
+ "%{x}-%{y-%{z",
+ attrs(ImmutableMap.of("x", ImmutableList.of("a", "b"))),
+ new String[] {"a-%{y-%{z", "b-%{y-%{z"},
+ new String[] {"x"});
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/InputFileTest.java b/src/test/java/com/google/devtools/build/lib/packages/InputFileTest.java
new file mode 100644
index 0000000000..e56a2128ee
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/InputFileTest.java
@@ -0,0 +1,105 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+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.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * A test for {@link InputFile}.
+ */
+@RunWith(JUnit4.class)
+public class InputFileTest {
+
+ private Path pathX;
+ private Path pathY;
+ private Package pkg;
+
+ private EventCollectionApparatus events = new EventCollectionApparatus();
+ private Scratch scratch = new Scratch("/workspace");
+ private PackageFactoryApparatus packages = new PackageFactoryApparatus(events.reporter());
+
+ @Before
+ public void setUp() throws Exception {
+ Path buildfile =
+ scratch.file(
+ "pkg/BUILD",
+ "genrule(name = 'dummy', ",
+ " cmd = '', ",
+ " outs = [], ",
+ " srcs = ['x', 'subdir/y'])");
+ pkg = packages.createPackage("pkg", buildfile);
+ events.assertNoEvents();
+
+ this.pathX = scratch.file("pkg/x", "blah");
+ this.pathY = scratch.file("pkg/subdir/y", "blah blah");
+ }
+
+ private static void checkPathMatches(InputFile input, Path expectedPath) {
+ assertEquals(expectedPath, input.getPath());
+ }
+
+ private static void checkName(InputFile input, String expectedName) {
+ assertEquals(expectedName, input.getName());
+ }
+
+ private static void checkLabel(InputFile input, String expectedLabelString) {
+ assertEquals(expectedLabelString, input.getLabel().toString());
+ }
+
+ @Test
+ public void testGetAssociatedRule() throws Exception {
+ assertNull(null, pkg.getTarget("x").getAssociatedRule());
+ }
+
+ @Test
+ public void testInputFileInPackageDirectory() throws NoSuchTargetException {
+ InputFile inputFileX = (InputFile) pkg.getTarget("x");
+ checkPathMatches(inputFileX, pathX);
+ checkName(inputFileX, "x");
+ checkLabel(inputFileX, "//pkg:x");
+ assertEquals("source file", inputFileX.getTargetKind());
+ }
+
+ @Test
+ public void testInputFileInSubdirectory() throws NoSuchTargetException {
+ InputFile inputFileY = (InputFile) pkg.getTarget("subdir/y");
+ checkPathMatches(inputFileY, pathY);
+ checkName(inputFileY, "subdir/y");
+ checkLabel(inputFileY, "//pkg:subdir/y");
+ }
+
+ @Test
+ public void testEquivalenceRelation() throws NoSuchTargetException {
+ InputFile inputFileX = (InputFile) pkg.getTarget("x");
+ assertSame(pkg.getTarget("x"), inputFileX);
+ InputFile inputFileY = (InputFile) pkg.getTarget("subdir/y");
+ assertSame(pkg.getTarget("subdir/y"), inputFileY);
+ assertEquals(inputFileX, inputFileX);
+ assertFalse(inputFileX.equals(inputFileY));
+ assertFalse(inputFileY.equals(inputFileX));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/LicenseTest.java b/src/test/java/com/google/devtools/build/lib/packages/LicenseTest.java
new file mode 100644
index 0000000000..0032dc8ba9
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/LicenseTest.java
@@ -0,0 +1,40 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.devtools.build.lib.packages.License.LicenseType;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+@RunWith(JUnit4.class)
+public class LicenseTest {
+
+ @Test
+ public void testLeastRestrictive() {
+ assertEquals(
+ LicenseType.RESTRICTED, License.leastRestrictive(Arrays.asList(LicenseType.RESTRICTED)));
+ assertEquals(
+ LicenseType.RESTRICTED,
+ License.leastRestrictive(
+ Arrays.asList(LicenseType.RESTRICTED, LicenseType.BY_EXCEPTION_ONLY)));
+ assertEquals(
+ LicenseType.BY_EXCEPTION_ONLY, License.leastRestrictive(Arrays.<LicenseType>asList()));
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/OutputFileTest.java b/src/test/java/com/google/devtools/build/lib/packages/OutputFileTest.java
new file mode 100644
index 0000000000..1e26e8aeb2
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/OutputFileTest.java
@@ -0,0 +1,206 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+import com.google.devtools.build.lib.vfs.Path;
+
+public class OutputFileTest extends PackageLoadingTestCase {
+
+ private PackageFactory packageFactory;
+ private Package pkg;
+ private Rule rule;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ packageFactory = new PackageFactory(TestRuleClassProvider.getRuleClassProvider());
+
+ Path buildfile =
+ scratch.file(
+ "pkg/BUILD",
+ "genrule(name='foo', ",
+ " srcs=[], ",
+ " cmd='', ",
+ " outs=['x', 'subdir/y'])");
+ this.pkg =
+ packageFactory.createPackageForTesting(
+ PackageIdentifier.createInDefaultRepo("pkg"), buildfile, getPackageManager(), reporter);
+ assertNoEvents();
+
+ this.rule = (Rule) pkg.getTarget("foo");
+ }
+
+ private void checkTargetRetainsGeneratingRule(OutputFile output) throws Exception {
+ assertSame(rule, output.getGeneratingRule());
+ }
+
+ private void checkName(OutputFile output, String expectedName) throws Exception {
+ assertEquals(expectedName, output.getName());
+ }
+
+ private void checkLabel(OutputFile output, String expectedLabelString) throws Exception {
+ assertEquals(expectedLabelString, output.getLabel().toString());
+ }
+
+ public void testGetAssociatedRule() throws Exception {
+ assertSame(rule, pkg.getTarget("x").getAssociatedRule());
+ }
+
+ public void testOutputFileInPackageDir() throws Exception {
+ OutputFile outputFileX = (OutputFile) pkg.getTarget("x");
+ checkTargetRetainsGeneratingRule(outputFileX);
+ checkName(outputFileX, "x");
+ checkLabel(outputFileX, "//pkg:x");
+ assertEquals("generated file", outputFileX.getTargetKind());
+ }
+
+ public void testOutputFileInSubdirectory() throws Exception {
+ OutputFile outputFileY = (OutputFile) pkg.getTarget("subdir/y");
+ checkTargetRetainsGeneratingRule(outputFileY);
+ checkName(outputFileY, "subdir/y");
+ checkLabel(outputFileY, "//pkg:subdir/y");
+ }
+
+ public void testEquivalenceRelation() throws Exception {
+ OutputFile outputFileX1 = (OutputFile) pkg.getTarget("x");
+ OutputFile outputFileX2 = (OutputFile) pkg.getTarget("x");
+ OutputFile outputFileY1 = (OutputFile) pkg.getTarget("subdir/y");
+ OutputFile outputFileY2 = (OutputFile) pkg.getTarget("subdir/y");
+ assertSame(outputFileX1, outputFileX2);
+ assertSame(outputFileY1, outputFileY2);
+ assertEquals(outputFileX1, outputFileX2);
+ assertEquals(outputFileX2, outputFileX1);
+ assertEquals(outputFileY1, outputFileY2);
+ assertEquals(outputFileY2, outputFileY1);
+ assertFalse(outputFileX1.equals(outputFileY1));
+ assertFalse(outputFileY1.equals(outputFileX1));
+ assertEquals(outputFileX1.hashCode(), outputFileX2.hashCode());
+ assertEquals(outputFileY1.hashCode(), outputFileY2.hashCode());
+ }
+
+ public void testDuplicateOutputFilesInDifferentRules() throws Exception {
+ Path buildfile =
+ scratch.file(
+ "two_outs/BUILD",
+ "genrule(name='a',",
+ " cmd='ls >$(location out)',",
+ " outs=['out'])",
+ "",
+ "genrule(name='b',",
+ " cmd='ls >$(location out)',",
+ " outs=['out'])");
+
+ reporter.removeHandler(failFastHandler);
+ packageFactory.createPackageForTesting(
+ PackageIdentifier.createInDefaultRepo("two_outs"),
+ buildfile,
+ getPackageManager(),
+ reporter);
+ assertContainsEvent(
+ "generated file 'out' in rule 'b' conflicts with "
+ + "existing generated file from rule 'a'");
+ }
+
+ public void testOutputFileNameConflictsWithExistingRule() throws Exception {
+ Path buildfile =
+ scratch.file(
+ "out_is_rule/BUILD",
+ "genrule(name='a',",
+ " cmd='ls >$(location out)',",
+ " outs=['out'])",
+ "",
+ "genrule(name='b',",
+ " cmd='ls >$(location out)',",
+ " outs=['a'])");
+
+ reporter.removeHandler(failFastHandler);
+ packageFactory.createPackageForTesting(
+ PackageIdentifier.createInDefaultRepo("out_is_rule"),
+ buildfile,
+ getPackageManager(),
+ reporter);
+ assertContainsEvent("generated file 'a' in rule 'b' conflicts with existing genrule rule");
+ }
+
+ public void testDuplicateOutputFilesInSameRule() throws Exception {
+ Path buildfile =
+ scratch.file(
+ "two_outs/BUILD",
+ "genrule(name='a',",
+ " cmd='ls >$(location out)',",
+ " outs=['out', 'out'])");
+
+ reporter.removeHandler(failFastHandler);
+ packageFactory.createPackageForTesting(
+ PackageIdentifier.createInDefaultRepo("two_outs"),
+ buildfile,
+ getPackageManager(),
+ reporter);
+ assertContainsEvent(
+ "generated file 'out' in rule 'a' conflicts with "
+ + "existing generated file from rule 'a'");
+ }
+
+ public void testOutputFileWithIllegalName() throws Exception {
+ Path buildfile =
+ scratch.file(
+ "bad_out_name/BUILD",
+ "genrule(name='a',",
+ " cmd='ls',",
+ " outs=['!@#'])");
+
+ reporter.removeHandler(failFastHandler);
+ packageFactory.createPackageForTesting(
+ PackageIdentifier.createInDefaultRepo("bad_out_name"),
+ buildfile,
+ getPackageManager(),
+ reporter);
+ assertContainsEvent("illegal output file name '!@#' in rule //bad_out_name:a");
+ }
+
+ public void testOutputFileWithCrossPackageLabel() throws Exception {
+ Path buildfile =
+ scratch.file(
+ "cross_package_out/BUILD",
+ "genrule(name='a',",
+ " cmd='ls',",
+ " outs=['//foo:bar'])");
+
+ reporter.removeHandler(failFastHandler);
+ packageFactory.createPackageForTesting(
+ PackageIdentifier.createInDefaultRepo("cross_package_out"),
+ buildfile,
+ getPackageManager(),
+ reporter);
+ assertContainsEvent("label '//foo:bar' is not in the current package");
+ }
+
+ public void testOutputFileNamedBUILD() throws Exception {
+ Path buildfile =
+ scratch.file(
+ "output_called_build/BUILD",
+ "genrule(name='a',",
+ " cmd='ls',",
+ " outs=['BUILD'])");
+
+ reporter.removeHandler(failFastHandler);
+ packageFactory.createPackageForTesting(
+ PackageIdentifier.createInDefaultRepo("output_called_build"), buildfile,
+ getPackageManager(), reporter);
+ assertContainsEvent("generated file 'BUILD' in rule 'a' conflicts with existing source file");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java
new file mode 100644
index 0000000000..c527a106f5
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java
@@ -0,0 +1,95 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static org.junit.Assert.assertFalse;
+
+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 com.google.devtools.build.lib.vfs.PathFragment;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.SynchronousQueue;
+
+/**
+ * Checks against a class initialization deadlock. "query sometimes hangs".
+ *
+ * <p>This requires static initialization of PackageGroup and PackageSpecification
+ * to occur in a multithreaded context, and therefore must be in its own class.
+ */
+@RunWith(JUnit4.class)
+public class PackageGroupStaticInitializationTest {
+ private Scratch scratch = new Scratch("/workspace");
+ private EventCollectionApparatus events = new EventCollectionApparatus();
+ private PackageFactoryApparatus packages = new PackageFactoryApparatus(events.reporter());
+
+ @Test
+ public void testNoDeadlockOnPackageGroupCreation() throws Exception {
+ scratch.file("fruits/BUILD", "package_group(name = 'mango', packages = ['//...'])");
+
+ final SynchronousQueue<PackageSpecification> groupQueue = new SynchronousQueue<>();
+ Thread producingThread =
+ new Thread(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ groupQueue.put(PackageSpecification.fromString("//fruits/..."));
+ } catch (Exception e) {
+ // Can't throw from Runnable, but this will cause the test to timeout
+ // when the consumer can't take the object.
+ e.printStackTrace();
+ }
+ }
+ });
+
+ Thread consumingThread =
+ new Thread(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ getPackageGroup("fruits", "mango");
+ groupQueue.take();
+ } catch (Exception e) {
+ // Can't throw from Runnable, but this will cause the test to timeout
+ // when the producer can't put the object.
+ e.printStackTrace();
+ }
+ }
+ });
+
+ consumingThread.start();
+ producingThread.start();
+ producingThread.join(3000);
+ consumingThread.join(3000);
+ assertFalse(producingThread.isAlive());
+ assertFalse(consumingThread.isAlive());
+ }
+
+ private Package getPackage(String packageName) throws Exception {
+ PathFragment buildFileFragment = new PathFragment(packageName).getRelative("BUILD");
+ Path buildFile = scratch.resolve(buildFileFragment.getPathString());
+ return packages.createPackage(packageName, buildFile);
+ }
+
+ private PackageGroup getPackageGroup(String pkg, String name) throws Exception {
+ return (PackageGroup) getPackage(pkg).getTarget(name);
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java
new file mode 100644
index 0000000000..9d501fcdad
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java
@@ -0,0 +1,143 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+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.MoreAsserts;
+import com.google.devtools.build.lib.testutil.Scratch;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for PackageGroup.
+ */
+@RunWith(JUnit4.class)
+public class PackageGroupTest {
+ private Scratch scratch = new Scratch("/workspace");
+ private EventCollectionApparatus events = new EventCollectionApparatus();
+ private PackageFactoryApparatus packages = new PackageFactoryApparatus(events.reporter());
+
+ @Test
+ public void testDoesNotFailHorribly() throws Exception {
+ scratch.file("fruits/BUILD", "package_group(name = 'apple', packages = ['//random'])");
+
+ getPackageGroup("fruits", "apple");
+ }
+
+ // Regression test for: "Package group with empty name causes Blaze exception"
+ @Test
+ public void testEmptyPackageGroupNameDoesNotThrow() throws Exception {
+ scratch.file("strawberry/BUILD", "package_group(name = '', packages=[])");
+
+ events.setFailFast(false);
+ getPackage("strawberry");
+ events.assertContainsEvent("package group has invalid name");
+ }
+
+ @Test
+ public void testAbsolutePackagesWork() throws Exception {
+ scratch.file(
+ "fruits/BUILD",
+ "package_group(name = 'apple',",
+ " packages = ['//vegetables'])");
+
+ scratch.file("vegetables/BUILD");
+ scratch.file("fruits/vegetables/BUILD");
+
+ PackageGroup grp = getPackageGroup("fruits", "apple");
+ assertTrue(grp.contains(getPackage("vegetables")));
+ assertFalse(grp.contains(getPackage("fruits/vegetables")));
+ }
+
+ @Test
+ public void testPackagesWithoutDoubleSlashDoNotWork() throws Exception {
+ scratch.file(
+ "fruits/BUILD",
+ "package_group(name = 'apple',",
+ " packages = ['vegetables'])");
+
+ scratch.file("vegetables/BUILD");
+ scratch.file("fruits/vegetables/BUILD");
+
+ events.setFailFast(false);
+ getPackageGroup("fruits", "apple");
+ events.assertContainsEvent("invalid package label: vegetables");
+ }
+
+ @Test
+ public void testTargetNameAsPackageDoesNotWork1() throws Exception {
+ scratch.file(
+ "fruits/BUILD",
+ "package_group(name = 'apple',",
+ " packages = ['//vegetables:carrot'])");
+
+ scratch.file("vegetables/BUILD");
+ scratch.file("fruits/vegetables/BUILD");
+
+ events.setFailFast(false);
+ getPackageGroup("fruits", "apple");
+ events.assertContainsEvent("invalid package label: //vegetables:carrot");
+ }
+
+ @Test
+ public void testTargetNameAsPackageDoesNotWork2() throws Exception {
+ scratch.file(
+ "fruits/BUILD", "package_group(name = 'apple',", " packages = [':carrot'])");
+
+ scratch.file("vegetables/BUILD");
+ scratch.file("fruits/vegetables/BUILD");
+
+ events.setFailFast(false);
+ getPackageGroup("fruits", "apple");
+ events.assertContainsEvent("invalid package label: :carrot");
+ }
+
+ @Test
+ public void testAllBeneathSpecificationWorks() throws Exception {
+ scratch.file(
+ "fruits/BUILD",
+ "package_group(name = 'maracuja',",
+ " packages = ['//tropics/...'])");
+
+ getPackageGroup("fruits", "maracuja");
+ }
+
+ @Test
+ public void testEverythingSpecificationWorks() throws Exception {
+ scratch.file("fruits/BUILD", "package_group(name = 'mango', packages = ['//...'])");
+ PackageGroup packageGroup = getPackageGroup("fruits", "mango");
+ MoreAsserts.assertSameContents(
+ ImmutableList.of(PackageSpecification.EVERYTHING), packageGroup.getPackageSpecifications());
+ }
+
+ private Package getPackage(String packageName) throws Exception {
+ PathFragment buildFileFragment = new PathFragment(packageName).getRelative("BUILD");
+
+ Path buildFile = scratch.resolve(buildFileFragment.getPathString());
+ return packages.createPackage(packageName, buildFile);
+ }
+
+ private PackageGroup getPackageGroup(String pkg, String name) throws Exception {
+ return (PackageGroup) getPackage(pkg).getTarget(name);
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/ProtoUtilsTest.java b/src/test/java/com/google/devtools/build/lib/packages/ProtoUtilsTest.java
new file mode 100644
index 0000000000..a18d662c58
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/ProtoUtilsTest.java
@@ -0,0 +1,54 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.query2.proto.proto2api.Build.Attribute.Discriminator;
+import com.google.devtools.build.lib.syntax.Type;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Collection;
+import java.util.Map.Entry;
+
+/** Tests for values and functions in ProtoUtils. */
+@RunWith(JUnit4.class)
+public class ProtoUtilsTest {
+
+ @Test
+ public void testTypeMap() throws Exception {
+ // The ProtoUtils TYPE_MAP (and its inverse, INVERSE_TYPE_MAP) are used to translate between
+ // rule attribute types and Discriminator values used to encode them in GPB messages. For each
+ // discriminator value there must be exactly one type, or there must be exactly two types, one
+ // which is a nodep type and the other which is not.
+ ImmutableSet<Entry<Discriminator, Collection<Type<?>>>> inverseMapEntries =
+ ProtoUtils.INVERSE_TYPE_MAP.asMap().entrySet();
+ for (Entry<Discriminator, Collection<Type<?>>> entry : inverseMapEntries) {
+ ImmutableSet<Type<?>> types = ImmutableSet.copyOf(entry.getValue());
+ String assertionMessage =
+ String.format(
+ "Cannot map from discriminator \"%s\" to exactly one Type.",
+ entry.getKey().toString());
+ boolean exactlyOneType = types.size() == 1;
+ boolean twoTypesDistinguishableUsingNodepHint =
+ types.size() == 2 && Sets.difference(types, ProtoUtils.NODEP_TYPES).size() == 1;
+ assertTrue(assertionMessage, exactlyOneType || twoTypesDistinguishableUsingNodepHint);
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RelativePackageNameResolverTest.java b/src/test/java/com/google/devtools/build/lib/packages/RelativePackageNameResolverTest.java
new file mode 100644
index 0000000000..9f2f9b6931
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/RelativePackageNameResolverTest.java
@@ -0,0 +1,122 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Unit tests for {@link RelativePackageNameResolver}.
+ */
+@RunWith(JUnit4.class)
+public class RelativePackageNameResolverTest {
+ private RelativePackageNameResolver resolver;
+
+ @Test
+ public void testRelativePackagesBelowOneLevelWork() throws Exception {
+ createResolver("foo", true);
+ assertResolvesTo("bar", "foo/bar");
+
+ createResolver("foo/bar", true);
+ assertResolvesTo("pear", "foo/bar/pear");
+ }
+
+ @Test
+ public void testRelativePackagesBelowTwoLevelsWork() throws Exception {
+ createResolver("foo/bar", true);
+ assertResolvesTo("pear", "foo/bar/pear");
+ }
+
+ @Test
+ public void testRelativePackagesAboveOneLevelWork() throws Exception {
+ createResolver("foo", true);
+ assertResolvesTo("../bar", "bar");
+ }
+
+ @Test
+ public void testRelativePackagesAboveTwoLevelsWork() throws Exception {
+ createResolver("foo/bar", true);
+ assertResolvesTo("../../apple", "apple");
+ }
+
+ @Test
+ public void testSimpleAbsolutePackagesWork() throws Exception {
+ createResolver("foo", true);
+
+ assertResolvesTo("//foo", "foo");
+ assertResolvesTo("//foo/bar", "foo/bar");
+ }
+
+ @Test
+ public void testBuildNotRemoved() throws Exception {
+ createResolver("foo", false);
+
+ assertResolvesTo("bar/BUILD", "foo/bar/BUILD");
+ }
+
+ @Test
+ public void testBuildRemoved() throws Exception {
+ createResolver("foo", true);
+
+ assertResolvesTo("bar/BUILD", "foo/bar");
+ }
+
+ @Test
+ public void testEmptyOffset() throws Exception {
+ createResolver("", true);
+
+ assertResolvesTo("bar", "bar");
+ assertResolvesTo("bar/qux", "bar/qux");
+ }
+
+ @Test
+ public void testTooFarUpwardsOneLevelThrows() throws Exception {
+ createResolver("foo", true);
+
+ try {
+ resolver.resolve("../../bar");
+ fail("InvalidPackageNameException expected");
+ } catch (InvalidPackageNameException e) {
+ // good
+ }
+ }
+
+ @Test
+ public void testTooFarUpwardsTwoLevelsThrows() throws Exception {
+ createResolver("foo/bar", true);
+ assertResolvesTo("../../orange", "orange");
+
+ try {
+ resolver.resolve("../../../orange");
+ fail("InvalidPackageNameException expected");
+ } catch (InvalidPackageNameException e) {
+ // good
+ }
+ }
+
+ private void createResolver(String offset, boolean discardBuild) {
+ resolver = new RelativePackageNameResolver(new PathFragment(offset), discardBuild);
+ }
+
+ private void assertResolvesTo(String relative, String expectedAbsolute) throws Exception {
+ String result = resolver.resolve(relative);
+ assertEquals(expectedAbsolute, result);
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleClassBuilderTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleClassBuilderTest.java
new file mode 100644
index 0000000000..67375e3e3c
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleClassBuilderTest.java
@@ -0,0 +1,177 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
+import static com.google.devtools.build.lib.syntax.Type.INTEGER;
+import static com.google.devtools.build.lib.syntax.Type.STRING;
+import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
+
+import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+
+/**
+ * Tests for the {@link RuleClass.Builder}.
+ */
+public class RuleClassBuilderTest 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();
+ }
+ };
+
+ public void testRuleClassBuilderBasics() throws Exception {
+ RuleClass ruleClassA =
+ new RuleClass.Builder("ruleA", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("srcs", BuildType.LABEL_LIST).legacyAllowAnyFileType())
+ .add(attr("tags", STRING_LIST))
+ .add(attr("X", com.google.devtools.build.lib.syntax.Type.INTEGER).mandatory())
+ .build();
+
+ assertEquals("ruleA", ruleClassA.getName());
+ assertEquals(3, ruleClassA.getAttributeCount());
+ assertTrue(ruleClassA.hasBinaryOutput());
+
+ assertEquals(0, (int) ruleClassA.getAttributeIndex("srcs"));
+ assertEquals(ruleClassA.getAttribute(0), ruleClassA.getAttributeByName("srcs"));
+
+ assertEquals(1, (int) ruleClassA.getAttributeIndex("tags"));
+ assertEquals(ruleClassA.getAttribute(1), ruleClassA.getAttributeByName("tags"));
+
+ assertEquals(2, (int) ruleClassA.getAttributeIndex("X"));
+ assertEquals(ruleClassA.getAttribute(2), ruleClassA.getAttributeByName("X"));
+ }
+
+ public void testRuleClassBuilderTestIsBinary() throws Exception {
+ RuleClass ruleClassA =
+ new RuleClass.Builder("rule_test", RuleClassType.TEST, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("tags", STRING_LIST))
+ .add(attr("size", STRING).value("medium"))
+ .add(attr("timeout", STRING))
+ .add(attr("flaky", BOOLEAN).value(false))
+ .add(attr("shard_count", INTEGER).value(-1))
+ .add(attr("local", BOOLEAN))
+ .build();
+ assertTrue(ruleClassA.hasBinaryOutput());
+ }
+
+ public void testRuleClassBuilderGenruleIsNotBinary() throws Exception {
+ RuleClass ruleClassA =
+ new RuleClass.Builder("ruleA", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .setOutputToGenfiles()
+ .add(attr("tags", STRING_LIST))
+ .build();
+ assertFalse(ruleClassA.hasBinaryOutput());
+ }
+
+ public void testRuleClassTestNameValidity() throws Exception {
+ try {
+ new RuleClass.Builder("ruleA", RuleClassType.TEST, false).build();
+ fail();
+ } catch (IllegalArgumentException e) {
+ // Expected exception.
+ }
+ }
+
+ public void testRuleClassNormalNameValidity() throws Exception {
+ try {
+ new RuleClass.Builder("ruleA_test", RuleClassType.NORMAL, false).build();
+ fail();
+ } catch (IllegalArgumentException e) {
+ // Expected exception.
+ }
+ }
+
+ public void testDuplicateAttribute() throws Exception {
+ RuleClass.Builder builder =
+ new RuleClass.Builder("ruleA", RuleClassType.NORMAL, false).add(attr("a", STRING));
+ try {
+ builder.add(attr("a", STRING));
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected exception.
+ }
+ }
+
+ public void testPropertiesOfAbstractRuleClass() throws Exception {
+ try {
+ new RuleClass.Builder("$ruleA", RuleClassType.ABSTRACT, false).setOutputToGenfiles();
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected exception.
+ }
+
+ try {
+ new RuleClass.Builder("$ruleB", RuleClassType.ABSTRACT, false)
+ .setImplicitOutputsFunction(null);
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected exception.
+ }
+ }
+
+ public void testDuplicateInheritedAttribute() throws Exception {
+ RuleClass a =
+ new RuleClass.Builder("ruleA", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("a", STRING).value("A"))
+ .add(attr("tags", STRING_LIST))
+ .build();
+ RuleClass b =
+ new RuleClass.Builder("ruleB", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("a", STRING).value("B"))
+ .add(attr("tags", STRING_LIST))
+ .build();
+ try {
+ // In case of multiple attribute inheritance the attributes must equal
+ new RuleClass.Builder("ruleC", RuleClassType.NORMAL, false, a, b).build();
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Attribute a is inherited multiple times in ruleC ruleclass");
+ }
+ }
+
+ public void testRemoveAttribute() throws Exception {
+ RuleClass a =
+ new RuleClass.Builder("rule", RuleClassType.NORMAL, false)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY)
+ .add(attr("a", STRING))
+ .add(attr("b", STRING))
+ .add(attr("tags", STRING_LIST))
+ .build();
+ RuleClass.Builder builder =
+ new RuleClass.Builder("c", RuleClassType.NORMAL, false, a)
+ .factory(DUMMY_CONFIGURED_TARGET_FACTORY);
+ RuleClass c = builder.removeAttribute("a").add(attr("a", INTEGER)).removeAttribute("b").build();
+ assertFalse(c.hasAttr("a", STRING));
+ assertTrue(c.hasAttr("a", INTEGER));
+ assertFalse(c.hasAttr("b", STRING));
+
+ try {
+ builder.removeAttribute("c");
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected exception.
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java
new file mode 100644
index 0000000000..a495ea026f
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java
@@ -0,0 +1,245 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.events.Reporter;
+import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+import com.google.devtools.build.lib.vfs.Path;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RuleFactoryTest extends PackageLoadingTestCase {
+
+ private ConfiguredRuleClassProvider provider = TestRuleClassProvider.getRuleClassProvider();
+ private RuleFactory ruleFactory = new RuleFactory(provider);
+
+ public static final Location LOCATION_42 = Location.fromFileAndOffsets(null, 42, 42);
+
+ public void testCreateRule() throws Exception {
+ Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD");
+ Package.Builder pkgBuilder =
+ new Package.Builder(PackageIdentifier.createInDefaultRepo("mypkg"), "TESTING")
+ .setFilename(myPkgPath)
+ .setMakeEnv(new MakeEnvironment.Builder());
+
+ Map<String, Object> attributeValues = new HashMap<>();
+ attributeValues.put("name", "foo");
+ attributeValues.put("alwayslink", true);
+
+ Rule rule =
+ RuleFactory.createAndAddRule(
+ pkgBuilder,
+ provider.getRuleClassMap().get("cc_library"),
+ attributeValues,
+ new Reporter(),
+ /*ast=*/ null,
+ LOCATION_42,
+ /*env=*/ null);
+
+ assertSame(rule, rule.getAssociatedRule());
+
+ // pkg.getRules() = [rule]
+ Package pkg = pkgBuilder.build();
+ assertThat(Sets.newHashSet(pkg.getTargets(Rule.class))).hasSize(1);
+ assertEquals(rule, pkg.getTargets(Rule.class).iterator().next());
+
+ assertSame(rule, pkg.getTarget("foo"));
+
+ assertEquals(Label.parseAbsolute("//mypkg:foo"), rule.getLabel());
+ assertEquals("foo", rule.getName());
+
+ assertEquals("cc_library", rule.getRuleClass());
+ assertEquals("cc_library rule", rule.getTargetKind());
+ assertEquals(42, rule.getLocation().getStartOffset());
+ assertNull(rule.getSyntaxTree());
+ assertFalse(rule.containsErrors());
+
+ // Attr with explicitly-supplied value:
+ AttributeMap attributes = RawAttributeMapper.of(rule);
+ assertTrue(attributes.get("alwayslink", Type.BOOLEAN));
+ try {
+ attributes.get("alwayslink", Type.STRING); // type error: boolean, not string!
+ fail();
+ } catch (Exception e) {
+ /* Class of exception and error message are not specified by API. */
+ }
+ try {
+ attributes.get("nosuchattr", Type.STRING); // no such attribute
+ fail();
+ } catch (Exception e) {
+ /* Class of exception and error message are not specified by API. */
+ }
+
+ // Attrs with default values:
+ // cc_library linkstatic default=0 according to build encyc.
+ assertFalse(attributes.get("linkstatic", Type.BOOLEAN));
+ assertFalse(attributes.get("testonly", Type.BOOLEAN));
+ assertThat(attributes.get("srcs", BuildType.LABEL_LIST)).isEmpty();
+ }
+
+ public void testCreateWorkspaceRule() throws Exception {
+ Path myPkgPath = scratch.resolve("/foo/workspace/WORKSPACE");
+ Package.Builder pkgBuilder = Package.newExternalPackageBuilder(myPkgPath, "TESTING");
+
+ Map<String, Object> attributeValues = new HashMap<>();
+ attributeValues.put("name", "foo");
+ attributeValues.put("actual", "//foo:bar");
+
+ Rule rule =
+ RuleFactory.createAndAddRule(
+ pkgBuilder,
+ provider.getRuleClassMap().get("bind"),
+ attributeValues,
+ new Reporter(),
+ /*ast=*/ null,
+ Location.fromFileAndOffsets(myPkgPath.asFragment(), 42, 42),
+ /*env=*/ null);
+ assertFalse(rule.containsErrors());
+ }
+
+ public void testWorkspaceRuleFailsInBuildFile() throws Exception {
+ Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD");
+ Package.Builder pkgBuilder =
+ new Package.Builder(PackageIdentifier.createInDefaultRepo("mypkg"), "TESTING")
+ .setFilename(myPkgPath)
+ .setMakeEnv(new MakeEnvironment.Builder());
+
+ Map<String, Object> attributeValues = new HashMap<>();
+ attributeValues.put("name", "foo");
+ attributeValues.put("actual", "//bar:baz");
+
+ try {
+ RuleFactory.createAndAddRule(
+ pkgBuilder,
+ provider.getRuleClassMap().get("bind"),
+ attributeValues,
+ new Reporter(),
+ /*ast=*/ null,
+ LOCATION_42,
+ /*env=*/ null);
+ fail();
+ } catch (RuleFactory.InvalidRuleException e) {
+ assertThat(e.getMessage()).contains("must be in the WORKSPACE file");
+ }
+ }
+
+ public void testBuildRuleFailsInWorkspaceFile() throws Exception {
+ Path myPkgPath = scratch.resolve("/foo/workspace/WORKSPACE");
+ Package.Builder pkgBuilder =
+ new Package.Builder(PackageIdentifier.createInDefaultRepo("mypkg"), "TESTING")
+ .setFilename(myPkgPath)
+ .setMakeEnv(new MakeEnvironment.Builder());
+
+ Map<String, Object> attributeValues = new HashMap<>();
+ attributeValues.put("name", "foo");
+ attributeValues.put("alwayslink", true);
+
+ try {
+ RuleFactory.createAndAddRule(
+ pkgBuilder,
+ provider.getRuleClassMap().get("cc_library"),
+ attributeValues,
+ new Reporter(),
+ /*ast=*/ null,
+ Location.fromFileAndOffsets(myPkgPath.asFragment(), 42, 42),
+ /*env=*/ null);
+ fail();
+ } catch (RuleFactory.InvalidRuleException e) {
+ assertThat(e.getMessage()).contains("cannot be in the WORKSPACE file");
+ }
+ }
+
+ private void assertAttr(RuleClass ruleClass, String attrName, Type<?> type) throws Exception {
+ assertTrue(
+ "Rule class '"
+ + ruleClass.getName()
+ + "' should have attribute '"
+ + attrName
+ + "' of type '"
+ + type
+ + "'",
+ ruleClass.hasAttr(attrName, type));
+ }
+
+ public void testOutputFileNotEqualDot() throws Exception {
+ Path myPkgPath = scratch.resolve("/foo");
+ Package.Builder pkgBuilder =
+ new Package.Builder(PackageIdentifier.createInDefaultRepo("mypkg"), "TESTING")
+ .setFilename(myPkgPath)
+ .setMakeEnv(new MakeEnvironment.Builder());
+
+ Map<String, Object> attributeValues = new HashMap<>();
+ attributeValues.put("outs", Lists.newArrayList("."));
+ attributeValues.put("name", "some");
+ try {
+ RuleFactory.createAndAddRule(
+ pkgBuilder,
+ provider.getRuleClassMap().get("genrule"),
+ attributeValues,
+ new Reporter(),
+ /*ast=*/ null,
+ Location.fromFileAndOffsets(myPkgPath.asFragment(), 42, 42),
+ /*env=*/ null);
+ fail();
+ } catch (RuleFactory.InvalidRuleException e) {
+ assertTrue(e.getMessage(), e.getMessage().contains("output file name can't be equal '.'"));
+ }
+ }
+
+ /**
+ * Tests mandatory attribute definitions for test rules.
+ */
+ // TODO(ulfjack): Remove this check when we switch over to the builder
+ // pattern, which will always guarantee that these attributes are present.
+ public void testTestRules() throws Exception {
+ Path myPkgPath = scratch.resolve("/foo/workspace/mypkg/BUILD");
+ Package pkg =
+ new Package.Builder(PackageIdentifier.createInDefaultRepo("mypkg"), "TESTING")
+ .setFilename(myPkgPath)
+ .setMakeEnv(new MakeEnvironment.Builder())
+ .build();
+
+ for (String name : ruleFactory.getRuleClassNames()) {
+ // Create rule instance directly so we'll avoid mandatory attribute check yet will be able
+ // to use TargetUtils.isTestRule() method to identify test rules.
+ RuleClass ruleClass = ruleFactory.getRuleClass(name);
+ Rule rule =
+ new Rule(
+ pkg,
+ pkg.createLabel("myrule"),
+ ruleClass,
+ null,
+ Location.fromFile(myPkgPath),
+ new AttributeContainer(ruleClass));
+ if (TargetUtils.isTestRule(rule)) {
+ assertAttr(ruleClass, "tags", Type.STRING_LIST);
+ assertAttr(ruleClass, "size", Type.STRING);
+ assertAttr(ruleClass, "flaky", Type.BOOLEAN);
+ assertAttr(ruleClass, "shard_count", Type.INTEGER);
+ assertAttr(ruleClass, "local", Type.BOOLEAN);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/SubincludePreprocessorTest.java b/src/test/java/com/google/devtools/build/lib/packages/SubincludePreprocessorTest.java
new file mode 100644
index 0000000000..5a945a7851
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/SubincludePreprocessorTest.java
@@ -0,0 +1,128 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertContainsRegex;
+import static com.google.devtools.build.lib.vfs.FileSystemUtils.writeIsoLatin1;
+
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+import com.google.devtools.build.lib.packages.util.SubincludePreprocessor;
+import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.Mutability;
+import com.google.devtools.build.lib.syntax.ParserInputSource;
+import com.google.devtools.build.lib.testutil.Suite;
+import com.google.devtools.build.lib.testutil.TestSpec;
+import com.google.devtools.build.lib.vfs.Path;
+
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+
+@TestSpec(size = Suite.MEDIUM_TESTS)
+public class SubincludePreprocessorTest extends PackageLoadingTestCase {
+ private Path packageRoot;
+ protected SubincludePreprocessor preprocessor;
+ protected Environment globalEnv =
+ Environment.builder(Mutability.create("test"))
+ .setGlobals(Environment.BUILD)
+ .setEventHandler(reporter)
+ .build();
+
+ public SubincludePreprocessorTest() {}
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ preprocessor = new SubincludePreprocessor(scratch.getFileSystem(), getPackageManager());
+ packageRoot = rootDirectory.getChild("preprocessing");
+ assertTrue(packageRoot.createDirectory());
+ reporter.removeHandler(failFastHandler);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ preprocessor = null;
+ super.tearDown();
+ }
+
+ private ParserInputSource createInputSource(String... lines) throws Exception {
+ Path buildFile = packageRoot.getChild("BUILD");
+ writeIsoLatin1(buildFile, lines);
+ ParserInputSource in = ParserInputSource.create(buildFile);
+ return in;
+ }
+
+ protected Preprocessor.Result preprocess(ParserInputSource in, String packageName)
+ throws IOException, InterruptedException {
+ Path buildFilePath = packageRoot.getRelative(in.getPath());
+ byte[] buildFileBytes =
+ StandardCharsets.ISO_8859_1.encode(CharBuffer.wrap(in.getContent())).array();
+ Preprocessor.Result result =
+ preprocessor.preprocess(
+ buildFilePath,
+ buildFileBytes,
+ packageName, /*globber=*/
+ null,
+ globalEnv.getGlobals(),
+ /*ruleNames=*/ null);
+ Event.replayEventsOn(reporter, result.events);
+ return result;
+ }
+
+ public void testPreprocessingInclude() throws Exception {
+ ParserInputSource in = createInputSource("subinclude('//foo:bar')");
+
+ scratch.file("foo/BUILD");
+ scratch.file("foo/bar", "genrule('turtle1')", "subinclude('//foo:baz')");
+ scratch.file("foo/baz", "genrule('turtle2')");
+
+ String out = assertPreprocessingSucceeds(in);
+ assertContainsRegex("turtle1", out);
+ assertContainsRegex("turtle2", out);
+ assertContainsRegex("mocksubinclude\\('//foo:bar', *'/workspace/foo/bar'\\)", out);
+ assertContainsRegex("mocksubinclude\\('//foo:baz', *'/workspace/foo/baz'\\)", out);
+ }
+
+ public void testSubincludeNotFound() throws Exception {
+ ParserInputSource in = createInputSource("subinclude('//nonexistent:bar')");
+ scratch.file("foo/BUILD");
+ String out = assertPreprocessingSucceeds(in);
+ assertContainsRegex("mocksubinclude\\('//nonexistent:bar', *''\\)", out);
+ assertContainsEvent("Cannot find subincluded file");
+ }
+
+ public void testError() throws Exception {
+ ParserInputSource in = createInputSource("subinclude('//foo:bar')");
+ scratch.file("foo/BUILD");
+ scratch.file("foo/bar", SubincludePreprocessor.TRANSIENT_ERROR);
+ try {
+ preprocess(in, "path/to/package");
+ fail();
+ } catch (IOException e) {
+ // Expected.
+ }
+ }
+
+ public String assertPreprocessingSucceeds(ParserInputSource in) throws Exception {
+ Preprocessor.Result out = preprocess(in, "path/to/package");
+ assertTrue(out.preprocessed);
+
+ // Check that the preprocessed file looks plausible:
+ assertEquals(in.getPath(), out.result.getPath());
+ String outString = new String(out.result.getContent());
+ outString = outString.replaceAll("[\n \t]", ""); // for easier regexps
+ return outString;
+ }
+}
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
index 196b4ab0f7..3c852ab1b9 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/TestSizeTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/TestSizeTest.java
@@ -1,4 +1,4 @@
-// Copyright 2012 The Bazel Authors. All rights reserved.
+// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/src/test/java/com/google/devtools/build/lib/packages/TestTargetUtilsTest.java b/src/test/java/com/google/devtools/build/lib/packages/TestTargetUtilsTest.java
new file mode 100644
index 0000000000..e6b68f95ce
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/TestTargetUtilsTest.java
@@ -0,0 +1,235 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.ResolvedTargets;
+import com.google.devtools.build.lib.cmdline.TargetParsingException;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.packages.util.PackageLoadingTestCase;
+import com.google.devtools.build.lib.pkgcache.TargetProvider;
+import com.google.devtools.build.lib.skyframe.TestSuiteExpansionValue;
+import com.google.devtools.build.skyframe.EvaluationResult;
+import com.google.devtools.build.skyframe.SkyKey;
+
+import java.util.Collection;
+import java.util.EnumSet;
+
+public class TestTargetUtilsTest extends PackageLoadingTestCase {
+ private Target test1;
+ private Target test2;
+ private Target test1b;
+ private Target suite;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ scratch.file(
+ "tests/BUILD",
+ "py_test(name = 'small_test_1',",
+ " srcs = ['small_test_1.py'],",
+ " data = [':xUnit'],",
+ " size = 'small',",
+ " tags = ['tag1'])",
+ "",
+ "sh_test(name = 'small_test_2',",
+ " srcs = ['small_test_2.sh'],",
+ " data = ['//testing/shbase:googletest.sh'],",
+ " size = 'small',",
+ " tags = ['tag2'])",
+ "",
+ "sh_test(name = 'large_test_1',",
+ " srcs = ['large_test_1.sh'],",
+ " data = ['//testing/shbase:googletest.sh', ':xUnit'],",
+ " size = 'large',",
+ " tags = ['tag1'])",
+ "",
+ "py_binary(name = 'notest',",
+ " srcs = ['notest.py'])",
+ "cc_library(name = 'xUnit', data = ['//tools:test_sharding_compliant'])",
+ "",
+ "test_suite( name = 'smallTests', tags=['small'])");
+
+ test1 = getTarget("//tests:small_test_1");
+ test2 = getTarget("//tests:small_test_2");
+ test1b = getTarget("//tests:large_test_1");
+ suite = getTarget("//tests:smallTests");
+ }
+
+ public void testFilterBySize() throws Exception {
+ Predicate<Target> sizeFilter =
+ TestTargetUtils.testSizeFilter(EnumSet.of(TestSize.SMALL, TestSize.LARGE));
+ assertTrue(sizeFilter.apply(test1));
+ assertTrue(sizeFilter.apply(test2));
+ assertTrue(sizeFilter.apply(test1b));
+ sizeFilter = TestTargetUtils.testSizeFilter(EnumSet.of(TestSize.SMALL));
+ assertTrue(sizeFilter.apply(test1));
+ assertTrue(sizeFilter.apply(test2));
+ assertFalse(sizeFilter.apply(test1b));
+ }
+
+ public void testFilterByTimeout() throws Exception {
+ scratch.file(
+ "timeouts/BUILD",
+ "sh_test(name = 'long_timeout',",
+ " srcs = ['a.sh'],",
+ " size = 'small',",
+ " timeout = 'long')",
+ "sh_test(name = 'short_timeout',",
+ " srcs = ['b.sh'],",
+ " size = 'small')",
+ "sh_test(name = 'moderate_timeout',",
+ " srcs = ['c.sh'],",
+ " size = 'small',",
+ " timeout = 'moderate')");
+ Target longTest = getTarget("//timeouts:long_timeout");
+ Target shortTest = getTarget("//timeouts:short_timeout");
+ Target moderateTest = getTarget("//timeouts:moderate_timeout");
+
+ Predicate<Target> timeoutFilter =
+ TestTargetUtils.testTimeoutFilter(EnumSet.of(TestTimeout.SHORT, TestTimeout.LONG));
+ assertTrue(timeoutFilter.apply(longTest));
+ assertTrue(timeoutFilter.apply(shortTest));
+ assertFalse(timeoutFilter.apply(moderateTest));
+ }
+
+ public void testFilterByTag() throws Exception {
+ Predicate<Target> tagFilter = TestTargetUtils.tagFilter(Lists.<String>newArrayList());
+ assertTrue(tagFilter.apply(test1));
+ assertTrue(tagFilter.apply(test2));
+ assertTrue(tagFilter.apply(test1b));
+ tagFilter = TestTargetUtils.tagFilter(Lists.newArrayList("tag1", "tag2"));
+ assertTrue(tagFilter.apply(test1));
+ assertTrue(tagFilter.apply(test2));
+ assertTrue(tagFilter.apply(test1b));
+ tagFilter = TestTargetUtils.tagFilter(Lists.newArrayList("tag1"));
+ assertTrue(tagFilter.apply(test1));
+ assertFalse(tagFilter.apply(test2));
+ assertTrue(tagFilter.apply(test1b));
+ tagFilter = TestTargetUtils.tagFilter(Lists.newArrayList("-tag2"));
+ assertTrue(tagFilter.apply(test1));
+ assertFalse(tagFilter.apply(test2));
+ assertTrue(tagFilter.apply(test1b));
+ // Applying same tag as positive and negative filter produces an empty
+ // result because the negative filter is applied first and positive filter will
+ // not match anything.
+ tagFilter = TestTargetUtils.tagFilter(Lists.newArrayList("tag2", "-tag2"));
+ assertFalse(tagFilter.apply(test1));
+ assertFalse(tagFilter.apply(test2));
+ assertFalse(tagFilter.apply(test1b));
+ tagFilter = TestTargetUtils.tagFilter(Lists.newArrayList("tag2", "-tag1"));
+ assertFalse(tagFilter.apply(test1));
+ assertTrue(tagFilter.apply(test2));
+ assertFalse(tagFilter.apply(test1b));
+ }
+
+ public void testExpandTestSuites() throws Exception {
+ assertExpandedSuites(Sets.newHashSet(test1, test2), Sets.newHashSet(test1, test2));
+ assertExpandedSuites(Sets.newHashSet(test1, test2), Sets.newHashSet(suite));
+ assertExpandedSuites(
+ Sets.newHashSet(test1, test2, test1b), Sets.newHashSet(test1, suite, test1b));
+ // The large test if returned as filtered from the test_suite rule, but should still be in the
+ // result set as it's explicitly added.
+ assertExpandedSuites(
+ Sets.newHashSet(test1, test2, test1b), ImmutableSet.<Target>of(test1b, suite));
+ }
+
+ public void testSkyframeExpandTestSuites() throws Exception {
+ assertExpandedSuitesSkyframe(
+ Sets.newHashSet(test1, test2), ImmutableSet.<Target>of(test1, test2));
+ assertExpandedSuitesSkyframe(Sets.newHashSet(test1, test2), ImmutableSet.<Target>of(suite));
+ assertExpandedSuitesSkyframe(
+ Sets.newHashSet(test1, test2, test1b), ImmutableSet.<Target>of(test1, suite, test1b));
+ // The large test if returned as filtered from the test_suite rule, but should still be in the
+ // result set as it's explicitly added.
+ assertExpandedSuitesSkyframe(
+ Sets.newHashSet(test1, test2, test1b), ImmutableSet.<Target>of(test1b, suite));
+ }
+
+ public void testExpandTestSuitesKeepGoing() throws Exception {
+ reporter.removeHandler(failFastHandler);
+ scratch.file("broken/BUILD", "test_suite(name = 'broken', tests = ['//missing:missing_test'])");
+ ResolvedTargets<Target> actual =
+ TestTargetUtils.expandTestSuites(
+ getPackageManager(),
+ reporter,
+ Sets.newHashSet(getTarget("//broken")), /*strict=*/
+ false, /*keep_going=*/
+ true);
+ assertTrue(actual.hasError());
+ assertThat(actual.getTargets()).isEmpty();
+ }
+
+ private void assertExpandedSuites(Iterable<Target> expected, Collection<Target> suites)
+ throws Exception {
+ ResolvedTargets<Target> actual =
+ TestTargetUtils.expandTestSuites(
+ getPackageManager(), reporter, suites, /*strict=*/ false, /*keep_going=*/ true);
+ assertFalse(actual.hasError());
+ assertThat(actual.getTargets()).containsExactlyElementsIn(expected);
+ }
+
+ private static final Function<Target, Label> TO_LABEL =
+ new Function<Target, Label>() {
+ @Override
+ public Label apply(Target input) {
+ return input.getLabel();
+ }
+ };
+
+ private void assertExpandedSuitesSkyframe(Iterable<Target> expected, Collection<Target> suites)
+ throws Exception {
+ ImmutableSet<Label> suiteLabels = ImmutableSet.copyOf(Iterables.transform(suites, TO_LABEL));
+ SkyKey key = TestSuiteExpansionValue.key(suiteLabels);
+ EvaluationResult<TestSuiteExpansionValue> result =
+ getSkyframeExecutor()
+ .getDriverForTesting()
+ .evaluate(ImmutableList.of(key), false, 1, reporter);
+ ResolvedTargets<Target> actual = result.get(key).getTargets();
+ assertFalse(actual.hasError());
+ assertThat(actual.getTargets()).containsExactlyElementsIn(expected);
+ }
+
+ public void testExpandTestSuitesInterrupted() throws Exception {
+ reporter.removeHandler(failFastHandler);
+ scratch.file("broken/BUILD", "test_suite(name = 'broken', tests = ['//missing:missing_test'])");
+ try {
+ TestTargetUtils.expandTestSuites(
+ new TargetProvider() {
+ @Override
+ public Target getTarget(EventHandler eventHandler, Label label)
+ throws InterruptedException {
+ throw new InterruptedException();
+ }
+ },
+ reporter,
+ Sets.newHashSet(getTarget("//broken")), /*strict=*/
+ false, /*keep_going=*/
+ true);
+ } catch (TargetParsingException e) {
+ assertNotNull(e.getMessage());
+ }
+ assertTrue(Thread.currentThread().isInterrupted());
+ }
+}
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
index 9134ce1635..8b6201c88e 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/TestTimeoutTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/TestTimeoutTest.java
@@ -1,4 +1,4 @@
-// Copyright 2009 The Bazel Authors. All rights reserved.
+// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
index 4b1283e517..a9e9a18bc6 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryApparatus.java
@@ -1,4 +1,4 @@
-// Copyright 2007 The Bazel Authors. All rights reserved.
+// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
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
index 576ec8dda3..97898229bb 100644
--- 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
@@ -1,4 +1,4 @@
-// Copyright 2006 The Bazel Authors. All rights reserved.
+// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/SubincludePreprocessor.java b/src/test/java/com/google/devtools/build/lib/packages/util/SubincludePreprocessor.java
new file mode 100644
index 0000000000..b192237d99
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/SubincludePreprocessor.java
@@ -0,0 +1,156 @@
+// Copyright 2015 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.packages.util;
+
+import com.google.common.primitives.Chars;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.events.StoredEventHandler;
+import com.google.devtools.build.lib.packages.CachingPackageLocator;
+import com.google.devtools.build.lib.packages.PackageFactory.Globber;
+import com.google.devtools.build.lib.packages.Preprocessor;
+import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.ParserInputSource;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Expands subinclude() statements, and returns an error if ERROR is
+ * present in the end-result. It does not run python, and is intended
+ * for testing
+ */
+public class SubincludePreprocessor implements Preprocessor {
+ /** Creates SubincludePreprocessor factories. */
+ public static class FactorySupplier implements Preprocessor.Factory.Supplier {
+ private final FileSystem fileSystem;
+
+ public FactorySupplier(FileSystem fileSystem) {
+ this.fileSystem = fileSystem;
+ }
+
+ @Override
+ public Factory getFactory(final CachingPackageLocator loc) {
+ return new Factory() {
+ @Override
+ public boolean isStillValid() {
+ return true;
+ }
+
+ @Override
+ public Preprocessor getPreprocessor() {
+ return new SubincludePreprocessor(fileSystem, loc);
+ }
+ };
+ }
+ }
+
+ private final FileSystem fileSystem;
+ private final CachingPackageLocator packageLocator;
+
+ private static final Pattern SUBINCLUDE_REGEX =
+ Pattern.compile("\\bsubinclude\\(['\"]([^'\"=]*)['\"]\\)", Pattern.MULTILINE);
+ public static final String TRANSIENT_ERROR = "TRANSIENT_ERROR";
+
+ /**
+ * Constructs a SubincludePreprocessor using the specified package
+ * path for resolving subincludes.
+ */
+ public SubincludePreprocessor(FileSystem fileSystem, CachingPackageLocator packageLocator) {
+ this.fileSystem = fileSystem;
+ this.packageLocator = packageLocator;
+ }
+
+ // Cut & paste from PythonPreprocessor#resolveSubinclude.
+ public String resolveSubinclude(String labelString) throws IOException {
+ Label label;
+ try {
+ label = Label.parseAbsolute(labelString);
+ } catch (LabelSyntaxException e) {
+ throw new IOException("Cannot parse label: '" + labelString + "'");
+ }
+
+ Path buildFile = packageLocator.getBuildFileForPackage(label.getPackageIdentifier());
+ if (buildFile == null) {
+ return "";
+ }
+
+ Path subinclude = buildFile.getParentDirectory().getRelative(new PathFragment(label.getName()));
+ return subinclude.getPathString();
+ }
+
+ @Override
+ public Preprocessor.Result preprocess(
+ Path buildFilePath,
+ byte[] buildFileBytes,
+ String packageName,
+ Globber globber,
+ Environment.Frame globals,
+ Set<String> ruleNames)
+ throws IOException, InterruptedException {
+ StoredEventHandler eventHandler = new StoredEventHandler();
+ char content[] = FileSystemUtils.convertFromLatin1(buildFileBytes);
+ while (true) {
+ Matcher matcher = SUBINCLUDE_REGEX.matcher(CharBuffer.wrap(content));
+ if (!matcher.find()) {
+ break;
+ }
+ String name = matcher.group(1);
+ String path = resolveSubinclude(name);
+
+ char subContent[];
+ if (path.isEmpty()) {
+ // This location is not correct, but will do for testing purposes.
+ eventHandler.handle(
+ Event.error(
+ Location.fromFile(buildFilePath), "Cannot find subincluded file \'" + name + "\'"));
+ // Emit a mocksubinclude(), so we know to preprocess again if the file becomes
+ // visible. We cannot fail the preprocess here, as it would drop the content.
+ subContent = new char[0];
+ } else {
+ // TODO(bazel-team): figure out the correct behavior for a non-existent file from an
+ // existent package.
+ subContent = FileSystemUtils.readContentAsLatin1(fileSystem.getPath(path));
+ }
+
+ String mock = "\nmocksubinclude('" + name + "', '" + path + "')\n";
+
+ content =
+ Chars.concat(
+ Arrays.copyOf(content, matcher.start()),
+ mock.toCharArray(),
+ subContent,
+ Arrays.copyOfRange(content, matcher.end(), content.length));
+ }
+
+ if (Chars.indexOf(content, TRANSIENT_ERROR.toCharArray()) >= 0) {
+ throw new IOException("transient error requested in " + buildFilePath.asFragment());
+ }
+
+ return Preprocessor.Result.success(
+ ParserInputSource.create(content, buildFilePath.asFragment()),
+ eventHandler.hasErrors(),
+ eventHandler.getEvents());
+ }
+}