blob: a8b1872d132c93f4fb3a364e20f6c764c5c18ea0 [file] [log] [blame]
// Copyright 2006 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.testutil.MoreAsserts.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
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.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.syntax.Depset;
import com.google.devtools.build.lib.syntax.SkylarkType;
import com.google.devtools.build.lib.syntax.StarlarkList;
import com.google.devtools.build.lib.syntax.Tuple;
import com.google.devtools.build.lib.testutil.MoreAsserts;
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 using Type.
*/
@RunWith(JUnit4.class)
public class TypeTest {
private Label currentRule;
@Before
public final void setCurrentRule() throws Exception {
this.currentRule = Label.parseAbsolute("//quux:baz", ImmutableMap.of());
}
@Test
public void testInteger() throws Exception {
Object x = 3;
assertThat(Type.INTEGER.convert(x, null)).isEqualTo(x);
assertThat(collectLabels(Type.INTEGER, x)).isEmpty();
}
@Test
public void testNonInteger() throws Exception {
Type.ConversionException e =
assertThrows(Type.ConversionException.class, () -> Type.INTEGER.convert("foo", null));
assertThat(e)
.hasMessageThat()
.isEqualTo("expected value of type 'int', but got \"foo\" (string)");
}
// Ensure that types are reported correctly.
@Test
public void testTypeErrorMessage() throws Exception {
Type.ConversionException e =
assertThrows(
Type.ConversionException.class,
() -> Type.STRING_LIST.convert("[(1,2), 3, 4]", "myexpr", null));
assertThat(e)
.hasMessageThat()
.isEqualTo(
"expected value of type 'list(string)' for myexpr, "
+ "but got \"[(1,2), 3, 4]\" (string)");
}
@Test
public void testString() throws Exception {
Object s = "foo";
assertThat(Type.STRING.convert(s, null)).isEqualTo(s);
assertThat(collectLabels(Type.STRING, s)).isEmpty();
}
@Test
public void testNonString() throws Exception {
Type.ConversionException e =
assertThrows(Type.ConversionException.class, () -> Type.STRING.convert(3, null));
assertThat(e).hasMessageThat().isEqualTo("expected value of type 'string', but got 3 (int)");
}
@Test
public void testBoolean() throws Exception {
Object myTrue = true;
Object myFalse = false;
assertThat(Type.BOOLEAN.convert(1, null)).isEqualTo(Boolean.TRUE);
assertThat(Type.BOOLEAN.convert(0, null)).isEqualTo(Boolean.FALSE);
assertThat(Type.BOOLEAN.convert(true, null)).isTrue();
assertThat(Type.BOOLEAN.convert(myTrue, null)).isTrue();
assertThat(Type.BOOLEAN.convert(false, null)).isFalse();
assertThat(Type.BOOLEAN.convert(myFalse, null)).isFalse();
assertThat(collectLabels(Type.BOOLEAN, myTrue)).isEmpty();
}
@Test
public void testNonBoolean() throws Exception {
Type.ConversionException e =
assertThrows(
Type.ConversionException.class, () -> Type.BOOLEAN.convert("unexpected", null));
assertThat(e)
.hasMessageThat()
.isEqualTo("expected value of type 'int', but got \"unexpected\" (string)");
// Integers other than [0, 1] should fail.
e = assertThrows(Type.ConversionException.class, () -> Type.BOOLEAN.convert(2, null));
assertThat(e).hasMessageThat().isEqualTo("boolean is not one of [0, 1]");
e = assertThrows(Type.ConversionException.class, () -> Type.BOOLEAN.convert(-1, null));
assertThat(e).hasMessageThat().isEqualTo("boolean is not one of [0, 1]");
}
@Test
public void testTriState() throws Exception {
assertThat(BuildType.TRISTATE.convert(1, null)).isEqualTo(TriState.YES);
assertThat(BuildType.TRISTATE.convert(0, null)).isEqualTo(TriState.NO);
assertThat(BuildType.TRISTATE.convert(-1, null)).isEqualTo(TriState.AUTO);
assertThat(BuildType.TRISTATE.convert(TriState.YES, null)).isEqualTo(TriState.YES);
assertThat(BuildType.TRISTATE.convert(TriState.NO, null)).isEqualTo(TriState.NO);
assertThat(BuildType.TRISTATE.convert(TriState.AUTO, null)).isEqualTo(TriState.AUTO);
assertThat(collectLabels(BuildType.TRISTATE, TriState.YES)).isEmpty();
// deprecated:
assertThat(BuildType.TRISTATE.convert(true, null)).isEqualTo(TriState.YES);
assertThat(BuildType.TRISTATE.convert(false, null)).isEqualTo(TriState.NO);
}
@Test
public void testTriStateDoesNotAcceptArbitraryIntegers() throws Exception {
List<Integer> listOfCases = Lists.newArrayList(2, 3, -5, -2, 20);
for (Object entry : listOfCases) {
assertThrows(Type.ConversionException.class, () -> BuildType.TRISTATE.convert(entry, null));
}
}
@Test
public void testTriStateDoesNotAcceptStrings() throws Exception {
List<?> listOfCases = Lists.newArrayList("bad", "true", "auto", "false");
// TODO(adonovan): add booleans true, false to this list; see b/116691720.
for (Object entry : listOfCases) {
assertThrows(Type.ConversionException.class, () -> BuildType.TRISTATE.convert(entry, null));
}
}
@Test
public void testTagConversion() throws Exception {
assertThat(Type.BOOLEAN.toTagSet(true, "attribute"))
.containsExactlyElementsIn(Sets.newHashSet("attribute"));
assertThat(Type.BOOLEAN.toTagSet(false, "attribute"))
.containsExactlyElementsIn(Sets.newHashSet("noattribute"));
assertThat(Type.STRING.toTagSet("whiskey", "preferred_cocktail"))
.containsExactlyElementsIn(Sets.newHashSet("whiskey"));
assertThat(
Type.STRING_LIST.toTagSet(
Lists.newArrayList("cheddar", "ementaler", "gruyere"), "cheeses"))
.containsExactlyElementsIn(Sets.newHashSet("cheddar", "ementaler", "gruyere"));
}
@Test
public void testIllegalTagConversionByType() throws Exception {
assertThrows(
UnsupportedOperationException.class,
() -> BuildType.TRISTATE.toTagSet(TriState.AUTO, "some_tristate"));
assertThrows(
UnsupportedOperationException.class,
() -> BuildType.LICENSE.toTagSet(License.NO_LICENSE, "output_license"));
}
@Test
public void testIllegalTagConversIonFromNullOnSupportedType() throws Exception {
assertThrows(IllegalStateException.class, () -> Type.BOOLEAN.toTagSet(null, "a_boolean"));
}
@Test
public void testLabel() throws Exception {
Label label = Label.parseAbsolute("//foo:bar", ImmutableMap.of());
assertThat(BuildType.LABEL.convert("//foo:bar", null, currentRule)).isEqualTo(label);
assertThat(collectLabels(BuildType.LABEL, label)).containsExactly(label);
}
@Test
public void testNodepLabel() throws Exception {
Label label = Label.parseAbsolute("//foo:bar", ImmutableMap.of());
assertThat(BuildType.NODEP_LABEL.convert("//foo:bar", null, currentRule)).isEqualTo(label);
assertThat(collectLabels(BuildType.NODEP_LABEL, label)).containsExactly(label);
}
@Test
public void testRelativeLabel() throws Exception {
assertThat(BuildType.LABEL.convert(":wiz", null, currentRule))
.isEqualTo(Label.parseAbsolute("//quux:wiz", ImmutableMap.of()));
assertThat(BuildType.LABEL.convert("wiz", null, currentRule))
.isEqualTo(Label.parseAbsolute("//quux:wiz", ImmutableMap.of()));
assertThrows(Type.ConversionException.class, () -> BuildType.LABEL.convert("wiz", null));
}
@Test
public void testInvalidLabel() throws Exception {
Type.ConversionException e =
assertThrows(
Type.ConversionException.class,
() -> BuildType.LABEL.convert("not//a label", null, currentRule));
MoreAsserts.assertContainsWordsWithQuotes(e.getMessage(), "not//a label");
}
@Test
public void testNonLabel() throws Exception {
Type.ConversionException e =
assertThrows(Type.ConversionException.class, () -> BuildType.LABEL.convert(3, null));
assertThat(e).hasMessageThat().isEqualTo("expected value of type 'string', but got 3 (int)");
}
@Test
public void testStringList() throws Exception {
Object input = Arrays.asList("foo", "bar", "wiz");
List<String> converted =
Type.STRING_LIST.convert(input, null);
assertThat(converted).isEqualTo(input);
assertThat(converted).isNotSameInstanceAs(input);
assertThat(collectLabels(Type.STRING_LIST, input)).isEmpty();
}
@Test
public void testStringDict() throws Exception {
Object input = ImmutableMap.of("foo", "bar",
"wiz", "bang");
Map<String, String> converted = Type.STRING_DICT.convert(input, null);
assertThat(converted).isEqualTo(input);
assertThat(converted).isNotSameInstanceAs(input);
assertThat(collectLabels(Type.STRING_DICT, converted)).isEmpty();
}
@Test
public void testStringDictBadElements() throws Exception {
Object input = ImmutableMap.of("foo", StarlarkList.of(null, "bar", "baz"), "wiz", "bang");
Type.ConversionException e =
assertThrows(Type.ConversionException.class, () -> Type.STRING_DICT.convert(input, null));
assertThat(e)
.hasMessageThat()
.isEqualTo(
"expected value of type 'string' for dict value element, "
+ "but got [\"bar\", \"baz\"] (list)");
}
@Test
public void testNonStringList() throws Exception {
Type.ConversionException e =
assertThrows(Type.ConversionException.class, () -> Type.STRING_LIST.convert(3, "blah"));
assertThat(e)
.hasMessageThat()
.isEqualTo("expected value of type 'list(string)' for blah, but got 3 (int)");
}
@Test
public void testStringListBadElements() throws Exception {
Object input = Arrays.<Object>asList("foo", "bar", 1);
Type.ConversionException e =
assertThrows(
Type.ConversionException.class, () -> Type.STRING_LIST.convert(input, "argument quux"));
assertThat(e)
.hasMessageThat()
.isEqualTo(
"expected value of type 'string' for element 2 of argument quux, but got 1 (int)");
}
@Test
public void testListDepsetConversion() throws Exception {
Object input =
Depset.of(SkylarkType.STRING, NestedSetBuilder.create(Order.STABLE_ORDER, "a", "b", "c"));
Type.STRING_LIST.convert(input, null);
}
@Test
public void testLabelList() throws Exception {
Object input = Arrays.asList("//foo:bar", ":wiz");
List<Label> converted =
BuildType.LABEL_LIST.convert(input , null, currentRule);
List<Label> expected =
Arrays.asList(
Label.parseAbsolute("//foo:bar", ImmutableMap.of()),
Label.parseAbsolute("//quux:wiz", ImmutableMap.of()));
assertThat(converted).isEqualTo(expected);
assertThat(converted).isNotSameInstanceAs(expected);
assertThat(collectLabels(BuildType.LABEL_LIST, converted)).containsExactlyElementsIn(expected);
}
@Test
public void testNonLabelList() throws Exception {
Type.ConversionException e =
assertThrows(
Type.ConversionException.class,
() -> BuildType.LABEL_LIST.convert(3, "foo", currentRule));
assertThat(e)
.hasMessageThat()
.isEqualTo("expected value of type 'list(label)' for foo, but got 3 (int)");
}
@Test
public void testLabelListBadElements() throws Exception {
Object list = Arrays.<Object>asList("//foo:bar", 2, "foo");
Type.ConversionException e =
assertThrows(
Type.ConversionException.class,
() -> BuildType.LABEL_LIST.convert(list, null, currentRule));
assertThat(e)
.hasMessageThat()
.isEqualTo("expected value of type 'string' for element 1 of null, but got 2 (int)");
}
@Test
public void testLabelListSyntaxError() throws Exception {
Object list = Arrays.<Object>asList("//foo:bar/..", "foo");
Type.ConversionException e =
assertThrows(
Type.ConversionException.class,
() -> BuildType.LABEL_LIST.convert(list, "myexpr", currentRule));
assertThat(e)
.hasMessageThat()
.isEqualTo(
"invalid label '//foo:bar/..' in element 0 of myexpr: "
+ "invalid target name 'bar/..': "
+ "target names may not contain up-level references '..'");
}
@Test
public void testStringListDict() throws Exception {
Object input = ImmutableMap.of("foo", Arrays.asList("foo", "bar"),
"wiz", Arrays.asList("bang"));
Map<String, List<String>> converted =
Type.STRING_LIST_DICT.convert(input, null, currentRule);
Map<?, ?> expected = ImmutableMap.<String, List<String>>of(
"foo", Arrays.asList("foo", "bar"),
"wiz", Arrays.asList("bang"));
assertThat(converted).isEqualTo(expected);
assertThat(converted).isNotSameInstanceAs(expected);
assertThat(collectLabels(Type.STRING_LIST_DICT, converted)).isEmpty();
}
@Test
public void testStringListDictBadFirstElement() throws Exception {
Object input = ImmutableMap.of(2, Arrays.asList("foo", "bar"),
"wiz", Arrays.asList("bang"));
Type.ConversionException e =
assertThrows(
Type.ConversionException.class,
() -> Type.STRING_LIST_DICT.convert(input, null, currentRule));
assertThat(e)
.hasMessageThat()
.isEqualTo("expected value of type 'string' for dict key element, but got 2 (int)");
}
@Test
public void testStringListDictBadSecondElement() throws Exception {
Object input = ImmutableMap.of("foo", "bar",
"wiz", Arrays.asList("bang"));
Type.ConversionException e =
assertThrows(
Type.ConversionException.class,
() -> Type.STRING_LIST_DICT.convert(input, null, currentRule));
assertThat(e)
.hasMessageThat()
.isEqualTo(
"expected value of type 'list(string)' for dict value element, "
+ "but got \"bar\" (string)");
}
@Test
public void testStringListDictBadElements1() throws Exception {
Object input = ImmutableMap.of(Tuple.of("foo"), Tuple.of("bang"), "wiz", Tuple.of("bang"));
Type.ConversionException e =
assertThrows(
Type.ConversionException.class, () -> Type.STRING_LIST_DICT.convert(input, null));
assertThat(e)
.hasMessageThat()
.isEqualTo(
"expected value of type 'string' for dict key element, but got "
+ "(\"foo\",) (tuple)");
}
@Test
public void testStringDictThrowsConversionException() throws Exception {
Type.ConversionException e =
assertThrows(
Type.ConversionException.class, () -> Type.STRING_DICT.convert("some string", null));
assertThat(e)
.hasMessageThat()
.isEqualTo(
"expected value of type 'dict(string, string)', but got \"some string\" (string)");
}
private static ImmutableList<Label> collectLabels(Type<?> type, Object value) {
final ImmutableList.Builder<Label> result = ImmutableList.builder();
type.visitLabels((label, dummy) -> result.add(label), value, /*context=*/ null);
return result.build();
}
}