diff options
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib')
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()); + } +} |