blob: 6f5012fa8384c6d821491c69b8165f790a338b31 [file] [log] [blame]
// 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 static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.packages.BuildType.Selector;
import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.Printer;
import com.google.devtools.build.lib.syntax.SelectorList;
import com.google.devtools.build.lib.syntax.SelectorValue;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.syntax.Type.ConversionException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Test of type-conversions for build-specific types.
*/
@RunWith(JUnit4.class)
public class BuildTypeTest {
private Label currentRule;
@Before
public final void setCurrentRule() throws Exception {
this.currentRule = Label.parseAbsolute("//quux:baz");
}
@Test
public void testFilesetEntry() throws Exception {
Label srcDir = Label.create("foo", "src");
Label entryLabel = Label.create("foo", "entry");
FilesetEntry input =
new FilesetEntry(
/* srcLabel */ srcDir,
/* files */ ImmutableList.of(entryLabel),
/* excludes */ null,
/* destDir */ null,
/* symlinkBehavior */ null,
/* stripPrefix */ null);
assertEquals(input, BuildType.FILESET_ENTRY.convert(input, null, currentRule));
assertThat(collectLabels(BuildType.FILESET_ENTRY, input)).containsExactly(entryLabel);
}
@Test
public void testFilesetEntryList() throws Exception {
Label srcDir = Label.create("foo", "src");
Label entry1Label = Label.create("foo", "entry1");
Label entry2Label = Label.create("foo", "entry");
List<FilesetEntry> input = ImmutableList.of(
new FilesetEntry(
/* srcLabel */ srcDir,
/* files */ ImmutableList.of(entry1Label),
/* excludes */ null,
/* destDir */ null,
/* symlinkBehavior */ null,
/* stripPrefix */ null),
new FilesetEntry(
/* srcLabel */ srcDir,
/* files */ ImmutableList.of(entry2Label),
/* excludes */ null,
/* destDir */ null,
/* symlinkBehavior */ null,
/* stripPrefix */ null));
assertEquals(input, BuildType.FILESET_ENTRY_LIST.convert(input, null, currentRule));
assertThat(collectLabels(BuildType.FILESET_ENTRY_LIST, input)).containsExactly(
entry1Label, entry2Label);
}
/**
* Tests basic {@link Selector} functionality.
*/
@Test
public void testSelector() throws Exception {
ImmutableMap<String, String> input = ImmutableMap.of(
"//conditions:a", "//a:a",
"//conditions:b", "//b:b",
Selector.DEFAULT_CONDITION_KEY, "//d:d");
Selector<Label> selector = new Selector<>(input, null, currentRule, BuildType.LABEL);
assertEquals(BuildType.LABEL, selector.getOriginalType());
Map<Label, Label> expectedMap = ImmutableMap.of(
Label.parseAbsolute("//conditions:a"), Label.create("@//a", "a"),
Label.parseAbsolute("//conditions:b"), Label.create("@//b", "b"),
Label.parseAbsolute(BuildType.Selector.DEFAULT_CONDITION_KEY), Label.create("@//d", "d"));
assertThat(selector.getEntries().entrySet()).containsExactlyElementsIn(expectedMap.entrySet());
}
/**
* Tests that creating a {@link Selector} over a mismatching native type triggers an
* exception.
*/
@Test
public void testSelectorWrongType() throws Exception {
ImmutableMap<String, String> input = ImmutableMap.of(
"//conditions:a", "not a label",
"//conditions:b", "also not a label",
BuildType.Selector.DEFAULT_CONDITION_KEY, "whatever");
try {
new Selector<>(input, null, currentRule, BuildType.LABEL);
fail("Expected Selector instantiation to fail since the input isn't a selection of labels");
} catch (ConversionException e) {
assertThat(e.getMessage()).contains("invalid label 'not a label'");
}
}
/**
* Tests that non-label selector keys trigger an exception.
*/
@Test
public void testSelectorKeyIsNotALabel() throws Exception {
ImmutableMap<String, String> input = ImmutableMap.of(
"not a label", "//a:a",
BuildType.Selector.DEFAULT_CONDITION_KEY, "whatever");
try {
new Selector<>(input, null, currentRule, BuildType.LABEL);
fail("Expected Selector instantiation to fail since the key isn't a label");
} catch (ConversionException e) {
assertThat(e.getMessage()).contains("invalid label 'not a label'");
}
}
/**
* Tests that {@link Selector} correctly references its default value.
*/
@Test
public void testSelectorDefault() throws Exception {
ImmutableMap<String, String> input = ImmutableMap.of(
"//conditions:a", "//a:a",
"//conditions:b", "//b:b",
BuildType.Selector.DEFAULT_CONDITION_KEY, "//d:d");
assertEquals(
Label.create("@//d", "d"),
new Selector<>(input, null, currentRule, BuildType.LABEL).getDefault());
}
@Test
public void testSelectorList() throws Exception {
Object selector1 = new SelectorValue(ImmutableMap.of("//conditions:a",
ImmutableList.of("//a:a"), "//conditions:b", ImmutableList.of("//b:b")), "");
Object selector2 = new SelectorValue(ImmutableMap.of("//conditions:c",
ImmutableList.of("//c:c"), "//conditions:d", ImmutableList.of("//d:d")), "");
BuildType.SelectorList<List<Label>> selectorList = new BuildType.SelectorList<>(
ImmutableList.of(selector1, selector2), null, currentRule, BuildType.LABEL_LIST);
assertEquals(BuildType.LABEL_LIST, selectorList.getOriginalType());
assertThat(selectorList.getKeyLabels())
.containsExactlyElementsIn(
ImmutableSet.of(
Label.parseAbsolute("//conditions:a"), Label.parseAbsolute("//conditions:b"),
Label.parseAbsolute("//conditions:c"), Label.parseAbsolute("//conditions:d")));
List<Selector<List<Label>>> selectors = selectorList.getSelectors();
assertThat(selectors.get(0).getEntries().entrySet())
.containsExactlyElementsIn(
ImmutableMap.of(Label.parseAbsolute("//conditions:a"),
ImmutableList.of(Label.create("@//a", "a")), Label.parseAbsolute("//conditions:b"),
ImmutableList.of(Label.create("@//b", "b")))
.entrySet());
assertThat(selectors.get(1).getEntries().entrySet())
.containsExactlyElementsIn(
ImmutableMap.of(
Label.parseAbsolute("//conditions:c"), ImmutableList.of(Label.create("@//c", "c")),
Label.parseAbsolute("//conditions:d"), ImmutableList.of(Label.create("@//d", "d")))
.entrySet());
}
@Test
public void testSelectorListMixedTypes() throws Exception {
Object selector1 =
new SelectorValue(ImmutableMap.of("//conditions:a", ImmutableList.of("//a:a")), "");
Object selector2 =
new SelectorValue(ImmutableMap.of("//conditions:b", "//b:b"), "");
try {
new BuildType.SelectorList<>(ImmutableList.of(selector1, selector2), null, currentRule,
BuildType.LABEL_LIST);
fail("Expected SelectorList initialization to fail on mixed element types");
} catch (ConversionException e) {
assertThat(e.getMessage()).contains("expected value of type 'list(label)'");
}
}
/**
* Tests that {@link BuildType#selectableConvert} returns either the native type or a selector
* on that type, in accordance with the provided input.
*/
@SuppressWarnings("unchecked")
@Test
public void testSelectableConvert() throws Exception {
Object nativeInput = Arrays.asList("//a:a1", "//a:a2");
Object selectableInput =
SelectorList.of(new SelectorValue(ImmutableMap.of(
"//conditions:a", nativeInput,
BuildType.Selector.DEFAULT_CONDITION_KEY, nativeInput), ""));
List<Label> expectedLabels =
ImmutableList.of(Label.create("@//a", "a1"), Label.create("@//a", "a2"));
// Conversion to direct type:
Object converted = BuildType
.selectableConvert(BuildType.LABEL_LIST, nativeInput, null, currentRule);
assertTrue(converted instanceof List<?>);
assertThat((List<Label>) converted).containsExactlyElementsIn(expectedLabels);
// Conversion to selectable type:
converted = BuildType
.selectableConvert(BuildType.LABEL_LIST, selectableInput, null, currentRule);
BuildType.SelectorList<?> selectorList = (BuildType.SelectorList<?>) converted;
assertThat(((Selector<Label>) selectorList.getSelectors().get(0)).getEntries().entrySet())
.containsExactlyElementsIn(
ImmutableMap.of(
Label.parseAbsolute("//conditions:a"),
expectedLabels,
Label.parseAbsolute(BuildType.Selector.DEFAULT_CONDITION_KEY),
expectedLabels)
.entrySet());
}
/**
* Tests that {@link com.google.devtools.build.lib.syntax.Type#convert} fails on selector inputs.
*/
@Test
public void testConvertDoesNotAcceptSelectables() throws Exception {
Object selectableInput = SelectorList.of(
new SelectorValue(
ImmutableMap.of("//conditions:a", Arrays.asList("//a:a1", "//a:a2")), ""));
try {
BuildType.LABEL_LIST.convert(selectableInput, null, currentRule);
fail("Expected conversion to fail on a selectable input");
} catch (ConversionException e) {
assertThat(e.getMessage()).contains("expected value of type 'list(label)'");
}
}
/**
* Tests for "reserved" key labels (i.e. not intended to map to actual targets).
*/
@Test
public void testReservedKeyLabels() throws Exception {
assertFalse(BuildType.Selector.isReservedLabel(Label.parseAbsolute("//condition:a")));
assertTrue(BuildType.Selector.isReservedLabel(
Label.parseAbsolute(BuildType.Selector.DEFAULT_CONDITION_KEY)));
}
@Test
public void testUnconditionalSelects() throws Exception {
assertFalse(
new Selector<>(
ImmutableMap.of("//conditions:a", "//a:a"),
null, currentRule, BuildType.LABEL
).isUnconditional());
assertFalse(
new Selector<>(
ImmutableMap.of(
"//conditions:a", "//a:a",
BuildType.Selector.DEFAULT_CONDITION_KEY, "//b:b"),
null, currentRule, BuildType.LABEL
).isUnconditional());
assertTrue(
new Selector<>(
ImmutableMap.of(
BuildType.Selector.DEFAULT_CONDITION_KEY, "//b:b"),
null, currentRule, BuildType.LABEL
).isUnconditional());
}
private static FilesetEntry makeFilesetEntry() {
try {
return new FilesetEntry(
/* srcLabel */ Label.parseAbsolute("//foo:bar"),
/* files */ ImmutableList.<Label>of(),
/* excludes */ ImmutableSet.of("xyz"),
/* destDir */ null,
/* symlinkBehavior */ null,
/* stripPrefix */ null);
} catch (LabelSyntaxException e) {
throw new RuntimeException("Bad label: ", e);
}
}
private String createExpectedFilesetEntryString(
FilesetEntry.SymlinkBehavior symlinkBehavior, char quotationMark) {
return String.format(
"FilesetEntry(srcdir = %1$c//x:x%1$c,"
+ " files = [%1$c//x:x%1$c],"
+ " excludes = [],"
+ " destdir = %1$c%1$c,"
+ " strip_prefix = %1$c.%1$c,"
+ " symlinks = %1$c%2$s%1$c)",
quotationMark, symlinkBehavior.toString().toLowerCase());
}
private String createExpectedFilesetEntryString(char quotationMark) {
return createExpectedFilesetEntryString(FilesetEntry.SymlinkBehavior.COPY, quotationMark);
}
private FilesetEntry createTestFilesetEntry(
FilesetEntry.SymlinkBehavior symlinkBehavior)
throws LabelSyntaxException {
Label label = Label.parseAbsolute("//x");
return new FilesetEntry(
/* srcLabel */ label,
/* files */ Arrays.asList(label),
/* excludes */ null,
/* destDir */ null,
/* symlinkBehavior */ symlinkBehavior,
/* stripPrefix */ null);
}
private FilesetEntry createTestFilesetEntry() throws LabelSyntaxException {
return createTestFilesetEntry(FilesetEntry.SymlinkBehavior.COPY);
}
@Test
public void testRegressionCrashInPrettyPrintValue() throws Exception {
// Would cause crash in code such as this:
// Fileset(name='x', entries=[], out=[FilesetEntry(files=['a'])])
// While formatting the "expected x, got y" message for the 'out'
// attribute, prettyPrintValue(FilesetEntry) would be recursively called
// with a List<Label> even though this isn't a valid datatype in the
// interpreter.
// Fileset isn't part of bazel, even though FilesetEntry is.
assertEquals(createExpectedFilesetEntryString('"'), Printer.repr(createTestFilesetEntry()));
}
@Test
public void testSingleQuotes() throws Exception {
assertThat(Printer.repr(createTestFilesetEntry(), '\''))
.isEqualTo(createExpectedFilesetEntryString('\''));
}
@Test
public void testFilesetEntrySymlinkAttr() throws Exception {
FilesetEntry entryDereference =
createTestFilesetEntry(FilesetEntry.SymlinkBehavior.DEREFERENCE);
assertEquals(
createExpectedFilesetEntryString(FilesetEntry.SymlinkBehavior.DEREFERENCE, '"'),
Printer.repr(entryDereference));
}
private FilesetEntry createStripPrefixFilesetEntry(String stripPrefix) throws Exception {
Label label = Label.parseAbsolute("//x");
return new FilesetEntry(
/* srcLabel */ label,
/* files */ Arrays.asList(label),
/* excludes */ null,
/* destDir */ null,
/* symlinkBehavior */ FilesetEntry.SymlinkBehavior.DEREFERENCE,
/* stripPrefix */ stripPrefix);
}
@Test
public void testFilesetEntryStripPrefixAttr() throws Exception {
FilesetEntry withoutStripPrefix = createStripPrefixFilesetEntry(".");
FilesetEntry withStripPrefix = createStripPrefixFilesetEntry("orange");
String prettyWithout = Printer.repr(withoutStripPrefix);
String prettyWith = Printer.repr(withStripPrefix);
assertThat(prettyWithout).contains("strip_prefix = \".\"");
assertThat(prettyWith).contains("strip_prefix = \"orange\"");
}
@Test
public void testPrintFilesetEntry() throws Exception {
assertThat(
Printer.repr(
new FilesetEntry(
/* srcLabel */ Label.parseAbsolute("//foo:BUILD"),
/* files */ ImmutableList.of(Label.parseAbsolute("//foo:bar")),
/* excludes */ ImmutableSet.of("baz"),
/* destDir */ "qux",
/* symlinkBehavior */ FilesetEntry.SymlinkBehavior.DEREFERENCE,
/* stripPrefix */ "blah")))
.isEqualTo(
Joiner.on(" ").join(
ImmutableList.of(
"FilesetEntry(srcdir = \"//foo:BUILD\",",
"files = [\"//foo:bar\"],",
"excludes = [\"baz\"],",
"destdir = \"qux\",",
"strip_prefix = \"blah\",",
"symlinks = \"dereference\")")));
}
@Test
public void testFilesetTypeDefinition() throws Exception {
assertEquals("FilesetEntry", EvalUtils.getDataTypeName(makeFilesetEntry()));
assertFalse(EvalUtils.isImmutable(makeFilesetEntry()));
}
private static ImmutableList<Label> collectLabels(Type<?> type, Object value)
throws InterruptedException {
final ImmutableList.Builder<Label> result = ImmutableList.builder();
type.visitLabels(new Type.LabelVisitor() {
@SuppressWarnings("unchecked")
@Override
public void visit(Label label) throws InterruptedException {
result.add(label);
}
}, value);
return result.build();
}
}