blob: 3b155a744a0dbaafefa2d6940438b56ab51a76ea [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.syntax;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests for SkylarkNestedSet.
*/
@RunWith(JUnit4.class)
public class SkylarkNestedSetTest extends EvaluationTestCase {
@Test
public void testLegacySetConstructor() throws Exception {
eval("ds = set([1, 2, 3], order='compile')");
SkylarkNestedSet ds = get("ds");
assertThat(ds.getOrder().getName()).isEqualTo("compile");
assertThat(ds.expandedSet()).isEqualTo(ImmutableSet.of(1, 2, 3));
}
@Test
public void testNsetBuilder() throws Exception {
eval("n = depset(order='stable')");
assertThat(lookup("n")).isInstanceOf(SkylarkNestedSet.class);
}
@Test
public void testNsetOrder() throws Exception {
eval("n = depset(['a', 'b'], order='compile')");
assertThat(get("n").getSet(String.class).getOrder()).isEqualTo(Order.COMPILE_ORDER);
}
@Test
public void testEmptyNsetGenericType() throws Exception {
eval("n = depset()");
assertThat(get("n").getContentType()).isEqualTo(SkylarkType.TOP);
}
@Test
public void testFunctionReturnsNset() throws Exception {
eval(
"def func():",
" n = depset()",
" n += ['a']",
" return n",
"s = func()");
assertThat(get("s")).isInstanceOf(SkylarkNestedSet.class);
assertThat(get("s").toCollection()).containsExactly("a");
}
@Test
public void testNsetTwoReferences() throws Exception {
eval(
"def func():",
" n1 = depset()",
" n1 += ['a']",
" n2 = n1",
" n2 += ['b']",
" return n1",
"n = func()");
assertThat(get("n").toCollection()).containsExactly("a");
}
@Test
public void testNsetUnionOrder() throws Exception {
// -----o----- []
// / \
// o ['a', 'b'] o ['b', 'c']
// | |
// o [] o []
eval(
"def func():",
" n1 = depset()",
" n2 = depset()",
" n1 += ['a', 'b']",
" n2 += ['b', 'c']",
" n1 += n2",
" return n1",
"n = func()");
// Under old behavior, the left operand was made a parent of the right operand. If that had
// happened, then the post-order traversal would yield [b, c, a] rather than [a, b, c].
assertThat(get("n").toCollection()).containsExactly("a", "b", "c").inOrder();
}
@Test
public void testNsetMultiUnionOrder() throws Exception {
// --------o-------- []
// / \
// ---o--- [] ---o--- []
// / \ / \
// o ['a'] o ['b'] o ['c'] o ['d']
eval(
"def func():",
" na = depset(['a'], order='compile')",
" nb = depset(['b'], order='compile')",
" nc = depset(['c'], order='compile')",
" nd = depset(['d'], order='compile')",
" return na + nb + (nc + nd)",
"n = func()");
// Under old behavior, in a chain of three or more unions s1 + s2 + ... + sn (left-
// associative), the post-order traversal yielded in order the elements of s2, s3, ..., sn, s1.
// Now it should just be s1, ..., sn.
assertThat(get("n").toCollection()).containsExactly("a", "b", "c", "d").inOrder();
}
@Test
public void testNsetTransitiveOrder() throws Exception {
// ---o--- []
// / \
// o ['a'] o ['b']
// |
// o ['c']
eval(
"def func():",
" na = depset(['a'])",
" nc = depset(['c'])",
" nb = nc + ['b']",
" return na + nb",
"n = func()");
assertThat(get("n").toCollection()).containsExactly("a", "c", "b").inOrder();
}
private Collection<Object> getDiamondTraversal(String order) throws Exception {
// o ['a']
// |
// --o--
// / \
// o ['b'] o ['c']
// \ /
// --o-- ['d']
initialize();
eval(
"def func():",
" nd = depset(['d'], order='" + order + "')",
" nb = nd + ['b']",
" nc = nd + ['c']",
" na = nb + nc + ['a']",
" return na",
"n = func()");
return get("n").toCollection();
}
@Test
public void testNsetDiamond() throws Exception {
assertThat(getDiamondTraversal("compile")).containsExactly("d", "b", "c", "a").inOrder();
assertThat(getDiamondTraversal("naive_link")).containsExactly("a", "b", "d", "c").inOrder();
assertThat(getDiamondTraversal("link")).containsExactly("a", "b", "c", "d").inOrder();
}
@Test
public void testNsetNestedItemBadOrder() throws Exception {
checkEvalError(
"LINK_ORDER != COMPILE_ORDER",
"depset(['a', 'b'], order='compile') + depset(['c', 'd'], order='link')");
}
@Test
public void testNsetItemList() throws Exception {
eval(
"def func():",
" n = depset()",
" n += ['a', 'b']",
" return n",
"n = func()");
assertThat(get("n").toCollection()).containsExactly("a", "b").inOrder();
}
@Test
public void testNsetFuncParamNoSideEffects() throws Exception {
eval(
"def func1(n):",
" n += ['b']",
"def func2():",
" n = depset()",
" n += ['a']",
" func1(n)",
" return n",
"n = func2()");
assertThat(get("n").toCollection()).containsExactly("a");
}
@Test
public void testNsetOrdering() throws Exception {
eval(
"def func():",
" na = depset()",
" na += [4]",
" na += [2, 4]",
" na += [3, 4, 5]",
" return na",
"n = func()");
// The iterator lists the Transitive sets first
assertThat(get("n").toCollection()).containsExactly(4, 2, 3, 5).inOrder();
}
@Test
public void testNsetBadOrder() throws Exception {
checkEvalError("Invalid order: non_existing", "depset(order='non_existing')");
}
@Test
public void testNsetBadRightOperand() throws Exception {
checkEvalError("cannot add value of type 'string' to a depset", "l = ['a']", "depset() + l[0]");
}
@Test
public void testNsetToString() throws Exception {
// o [3, 4, 5]
// |
// o [2, 4, 6]
// |
// o []
eval(
"s = depset() + [2, 4, 6] + [3, 4, 5]",
"x = str(s)");
assertThat(lookup("x")).isEqualTo("set([2, 4, 6, 3, 5])");
}
@Test
public void testNsetToStringWithOrder() throws Exception {
// o [3, 4, 5]
// |
// o [2, 4, 6]
// |
// o []
eval(
"s = depset(order = 'link') + [2, 4, 6] + [3, 4, 5]",
"x = str(s)");
assertThat(lookup("x")).isEqualTo("set([3, 5, 2, 4, 6], order = \"link\")");
}
@SuppressWarnings("unchecked")
private SkylarkNestedSet get(String varname) throws Exception {
return (SkylarkNestedSet) lookup(varname);
}
@Test
public void testSetOrderCompatibility() throws Exception {
// Two sets are compatible if
// (a) both have the same order or
// (b) at least one order is "stable"
for (Order first : Order.values()) {
SkylarkNestedSet s1 = new SkylarkNestedSet(first, Tuple.of("1", "11"), null);
for (Order second : Order.values()) {
SkylarkNestedSet s2 = new SkylarkNestedSet(second, Tuple.of("2", "22"), null);
boolean compatible = true;
try {
new SkylarkNestedSet(s1, s2, null);
} catch (Exception ex) {
compatible = false;
}
assertThat(compatible).isEqualTo(areOrdersCompatible(first, second));
}
}
}
private boolean areOrdersCompatible(Order first, Order second) {
return first == Order.STABLE_ORDER || second == Order.STABLE_ORDER || first == second;
}
}