// Copyright 2014 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.ImmutableMap;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
import com.google.devtools.build.lib.testutil.TestMode;
import java.util.Collections;
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 evaluation behavior. (Implicitly uses lexer + parser.)
*/
@RunWith(JUnit4.class)
public class EvaluationTest extends EvaluationTestCase {
@Before
public final void setBuildMode() throws Exception {
super.setMode(TestMode.BUILD);
}
/**
* Creates a new instance of {@code ModalTestCase}.
*
*
If a test uses this method, it allows potential subclasses to run the very same test in a
* different mode in subclasses
*/
protected ModalTestCase newTest(String... skylarkOptions) {
return new BuildTest(skylarkOptions);
}
@Test
public void testExprs() throws Exception {
newTest()
.testStatement("'%sx' % 'foo' + 'bar1'", "fooxbar1")
.testStatement("('%sx' % 'foo') + 'bar2'", "fooxbar2")
.testStatement("'%sx' % ('foo' + 'bar3')", "foobar3x")
.testStatement("123 + 456", 579)
.testStatement("456 - 123", 333)
.testStatement("8 % 3", 2)
.testIfErrorContains("unsupported operand type(s) for %: 'int' and 'string'", "3 % 'foo'")
.testStatement("-5", -5)
.testIfErrorContains("unsupported operand type for -: 'string'", "-'foo'");
}
@Test
public void testListExprs() throws Exception {
newTest().testExactOrder("[1, 2, 3]", 1, 2, 3).testExactOrder("(1, 2, 3)", 1, 2, 3);
}
@Test
public void testStringFormatMultipleArgs() throws Exception {
newTest().testStatement("'%sY%s' % ('X', 'Z')", "XYZ");
}
@Test
public void testConditionalExpressions() throws Exception {
newTest()
.testStatement("1 if True else 2", 1)
.testStatement("1 if False else 2", 2)
.testStatement("1 + 2 if 3 + 4 else 5 + 6", 3);
setFailFast(false);
parseExpression("1 if 2");
assertContainsError(
"missing else clause in conditional expression or semicolon before if");
}
@Test
public void testListComparison() throws Exception {
newTest()
.testStatement("[] < [1]", true)
.testStatement("[1] < [1, 1]", true)
.testStatement("[1, 1] < [1, 2]", true)
.testStatement("[1, 2] < [1, 2, 3]", true)
.testStatement("[1, 2, 3] <= [1, 2, 3]", true)
.testStatement("['a', 'b'] > ['a']", true)
.testStatement("['a', 'b'] >= ['a']", true)
.testStatement("['a', 'b'] < ['a']", false)
.testStatement("['a', 'b'] <= ['a']", false)
.testStatement("('a', 'b') > ('a', 'b')", false)
.testStatement("('a', 'b') >= ('a', 'b')", true)
.testStatement("('a', 'b') < ('a', 'b')", false)
.testStatement("('a', 'b') <= ('a', 'b')", true)
.testStatement("[[1, 1]] > [[1, 1], []]", false)
.testStatement("[[1, 1]] < [[1, 1], []]", true);
}
@Test
public void testSetComparison() throws Exception {
newTest().testIfExactError("Cannot compare depsets", "depset([1, 2]) < depset([3, 4])");
}
@Test
public void testSumFunction() throws Exception {
BaseFunction sum = new BaseFunction("sum") {
@Override
public Object call(List args, Map kwargs,
FuncallExpression ast, Environment env) {
int sum = 0;
for (Object arg : args) {
sum += (Integer) arg;
}
return sum;
}
};
newTest().update(sum.getName(), sum).testStatement("sum(1, 2, 3, 4, 5, 6)", 21)
.testStatement("sum", sum).testStatement("sum(a=1, b=2)", 0);
}
@Test
public void testNotCallInt() throws Exception {
newTest().setUp("sum = 123456").testLookup("sum", 123456)
.testIfExactError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)")
.testStatement("sum", 123456);
}
@Test
public void testKeywordArgs() throws Exception {
// This function returns the map of keyword arguments passed to it.
BaseFunction kwargs = new BaseFunction("kwargs") {
@Override
public Object call(List args,
final Map kwargs,
FuncallExpression ast,
Environment env) {
return SkylarkDict.copyOf(env, kwargs);
}
};
newTest()
.update(kwargs.getName(), kwargs)
.testEval(
"kwargs(foo=1, bar='bar', wiz=[1,2,3]).items()",
"[('foo', 1), ('bar', 'bar'), ('wiz', [1, 2, 3])]")
.testEval(
"kwargs(wiz=[1,2,3], bar='bar', foo=1).items()",
"[('wiz', [1, 2, 3]), ('bar', 'bar'), ('foo', 1)]");
}
@Test
public void testModulo() throws Exception {
newTest()
.testStatement("6 % 2", 0)
.testStatement("6 % 4", 2)
.testStatement("3 % 6", 3)
.testStatement("7 % -4", -1)
.testStatement("-7 % 4", 1)
.testStatement("-7 % -4", -3)
.testIfExactError("integer modulo by zero", "5 % 0");
}
@Test
public void testMult() throws Exception {
newTest()
.testStatement("6 * 7", 42)
.testStatement("3 * 'ab'", "ababab")
.testStatement("0 * 'ab'", "")
.testStatement("'1' + '0' * 5", "100000")
.testStatement("'ab' * -4", "")
.testStatement("-1 * ''", "");
}
@Test
public void testDivision() throws Exception {
newTest()
.testStatement("6 / 2", 3)
.testStatement("6 / 4", 1)
.testStatement("3 / 6", 0)
.testStatement("7 / -2", -4)
.testStatement("-7 / 2", -4)
.testStatement("-7 / -2", 3)
.testStatement("2147483647 / 2", 1073741823)
.testIfErrorContains("unsupported operand type(s) for /: 'string' and 'int'", "'str' / 2")
.testIfExactError("integer division by zero", "5 / 0");
}
@Test
public void testFloorDivision() throws Exception {
newTest()
.testStatement("6 // 2", 3)
.testStatement("6 // 4", 1)
.testStatement("3 // 6", 0)
.testStatement("7 // -2", -4)
.testStatement("-7 // 2", -4)
.testStatement("-7 // -2", 3)
.testStatement("2147483647 // 2", 1073741823)
.testIfErrorContains("unsupported operand type(s) for /: 'string' and 'int'", "'str' / 2")
.testIfExactError("integer division by zero", "5 // 0");
}
@Test
public void testCheckedArithmetic() throws Exception {
new SkylarkTest("--incompatible_checked_arithmetic=true")
.testIfErrorContains("integer overflow", "2000000000 + 2000000000")
.testIfErrorContains("integer overflow", "1234567890 * 987654321")
.testIfErrorContains("integer overflow", "- 2000000000 - 2000000000")
// literal 2147483648 is not allowed, so we compute it
.setUp("minint = - 2147483647 - 1")
.testIfErrorContains("integer overflow", "-minint");
}
@Test
public void testOperatorPrecedence() throws Exception {
newTest()
.testStatement("2 + 3 * 4", 14)
.testStatement("2 + 3 / 4", 2)
.testStatement("2 * 3 + 4 / -2", 4);
}
@Test
public void testConcatStrings() throws Exception {
newTest().testStatement("'foo' + 'bar'", "foobar");
}
@SuppressWarnings("unchecked")
@Test
public void testConcatLists() throws Exception {
// TODO(fwe): cannot be handled by current testing suite
// list
Object x = eval("[1,2] + [3,4]");
assertThat((Iterable) x).containsExactly(1, 2, 3, 4).inOrder();
assertThat(x).isEqualTo(MutableList.of(env, 1, 2, 3, 4));
assertThat(EvalUtils.isImmutable(x)).isFalse();
// tuple
x = eval("(1,2) + (3,4)");
assertThat(x).isEqualTo(Tuple.of(1, 2, 3, 4));
assertThat(EvalUtils.isImmutable(x)).isTrue();
checkEvalError("unsupported operand type(s) for +: 'tuple' and 'list'",
"(1,2) + [3,4]"); // list + tuple
}
@Test
public void testListComprehensions() throws Exception {
newTest()
.testExactOrder("['foo/%s.java' % x for x in []]")
.testExactOrder("['foo/%s.java' % y for y in ['bar', 'wiz', 'quux']]", "foo/bar.java",
"foo/wiz.java", "foo/quux.java")
.testExactOrder("['%s/%s.java' % (z, t) for z in ['foo', 'bar'] "
+ "for t in ['baz', 'wiz', 'quux']]",
"foo/baz.java",
"foo/wiz.java",
"foo/quux.java",
"bar/baz.java",
"bar/wiz.java",
"bar/quux.java")
.testExactOrder("['%s/%s.java' % (b, b) for a in ['foo', 'bar'] "
+ "for b in ['baz', 'wiz', 'quux']]",
"baz/baz.java",
"wiz/wiz.java",
"quux/quux.java",
"baz/baz.java",
"wiz/wiz.java",
"quux/quux.java")
.testExactOrder("['%s/%s.%s' % (c, d, e) for c in ['foo', 'bar'] "
+ "for d in ['baz', 'wiz', 'quux'] for e in ['java', 'cc']]",
"foo/baz.java",
"foo/baz.cc",
"foo/wiz.java",
"foo/wiz.cc",
"foo/quux.java",
"foo/quux.cc",
"bar/baz.java",
"bar/baz.cc",
"bar/wiz.java",
"bar/wiz.cc",
"bar/quux.java",
"bar/quux.cc")
.testExactOrder("[i for i in (1, 2)]", 1, 2)
.testExactOrder("[i for i in [2, 3] or [1, 2]]", 2, 3);
}
@Test
public void testNestedListComprehensions() throws Exception {
newTest().testExactOrder("li = [[1, 2], [3, 4]]\n" + "[j for i in li for j in i]", 1, 2,
3, 4).testExactOrder("input = [['abc'], ['def', 'ghi']]\n"
+ "['%s %s' % (b, c) for a in input for b in a for c in b]",
"abc a",
"abc b",
"abc c",
"def d",
"def e",
"def f",
"ghi g",
"ghi h",
"ghi i");
}
@Test
public void testListComprehensionsMultipleVariables() throws Exception {
newTest().testEval("[x + y for x, y in [(1, 2), (3, 4)]]", "[3, 7]")
.testEval("[z + t for (z, t) in [[1, 2], [3, 4]]]", "[3, 7]");
}
@Test
public void testListComprehensionsMultipleVariablesFail() throws Exception {
newTest().testIfErrorContains(
"assignment length mismatch: left-hand side has length 3, but right-hand side evaluates to "
+ "value of length 2",
"[x + y for x, y, z in [(1, 2), (3, 4)]]").testIfExactError(
"type 'int' is not a collection", "[x + y for x, y in (1, 2)]");
}
@Test
public void testListComprehensionsWithFiltering() throws Exception {
newTest()
.setUp("range3 = [0, 1, 2]")
.testEval("[a for a in (4, None, 2, None, 1) if a != None]", "[4, 2, 1]")
.testEval("[b+c for b in [0, 1, 2] for c in [0, 1, 2] if b + c > 2]", "[3, 3, 4]")
.testEval("[d+e for d in range3 if d % 2 == 1 for e in range3]", "[1, 2, 3]")
.testEval("[[f,g] for f in [0, 1, 2, 3, 4] if f for g in [5, 6, 7, 8] if f * g % 12 == 0 ]",
"[[2, 6], [3, 8], [4, 6]]")
.testEval("[h for h in [4, 2, 0, 1] if h]", "[4, 2, 1]");
}
@Test
public void testListComprehensionDefinitionOrder() throws Exception {
newTest().testIfErrorContains("name 'y' is not defined",
"[x for x in (1, 2) if y for y in (3, 4)]");
}
@Test
public void testTupleDestructuring() throws Exception {
newTest()
.setUp("a, b = 1, 2")
.testLookup("a", 1)
.testLookup("b", 2)
.setUp("c, d = {'key1':2, 'key2':3}")
.testLookup("c", "key1")
.testLookup("d", "key2");
}
@Test
public void testSingleTuple() throws Exception {
newTest().setUp("(a,) = [1]").testLookup("a", 1);
}
@Test
public void testHeterogeneousDict() throws Exception {
newTest().setUp("d = {'str': 1, 2: 3}", "a = d['str']", "b = d[2]").testLookup("a", 1)
.testLookup("b", 3);
}
@Test
public void testAccessDictWithATupleKey() throws Exception {
newTest().setUp("x = {(1, 2): 3}[1, 2]").testLookup("x", 3);
}
@Test
public void testDictWithDuplicatedKey() throws Exception {
new SkylarkTest("--incompatible_dict_literal_has_no_duplicates=true")
.testIfErrorContains(
"Duplicated key \"str\" when creating dictionary", "{'str': 1, 'x': 2, 'str': 3}");
}
@Test
public void testDictAllowDuplicatedKey() throws Exception {
new SkylarkTest("--incompatible_dict_literal_has_no_duplicates=false")
.testStatement("{'str': 1, 'x': 2, 'str': 3}", ImmutableMap.of("str", 3, "x", 2));
}
@Test
public void testRecursiveTupleDestructuring() throws Exception {
newTest()
.setUp("((a, b), (c, d)) = [(1, 2), (3, 4)]")
.testLookup("a", 1)
.testLookup("b", 2)
.testLookup("c", 3)
.testLookup("d", 4);
}
@Test
public void testListComprehensionModifiesGlobalEnv() throws Exception {
new SkylarkTest()
.update("x", 42)
.testIfErrorContains("Variable x is read only", "[x + 1 for x in [1,2,3]]");
new BuildTest().update("x", 42).setUp("y =[x + 1 for x in [1,2,3]]")
.testExactOrder("y", 2, 3, 4).testLookup("x", 3); // (x is global)
}
@Test
public void testDictComprehensions() throws Exception {
newTest()
.testStatement("{a : a for a in []}", Collections.emptyMap())
.testStatement("{b : b for b in [1, 2]}", ImmutableMap.of(1, 1, 2, 2))
.testStatement("{c : 'v_' + c for c in ['a', 'b']}",
ImmutableMap.of("a", "v_a", "b", "v_b"))
.testStatement("{'k_' + d : d for d in ['a', 'b']}",
ImmutableMap.of("k_a", "a", "k_b", "b"))
.testStatement("{'k_' + e : 'v_' + e for e in ['a', 'b']}",
ImmutableMap.of("k_a", "v_a", "k_b", "v_b"))
.testStatement("{x+y : x*y for x, y in [[2, 3]]}", ImmutableMap.of(5, 6));
}
@Test
public void testDictComprehensionOnNonIterable() throws Exception {
newTest().testIfExactError("type 'int' is not iterable", "{k : k for k in 3}");
}
@Test
public void testDictComprehension_ManyClauses() throws Exception {
new SkylarkTest().testStatement(
"{x : x * y for x in range(1, 10) if x % 2 == 0 for y in range(1, 10) if y == x}",
ImmutableMap.of(2, 4, 4, 16, 6, 36, 8, 64));
}
@Test
public void testDictComprehensions_MultipleKey() throws Exception {
newTest().testStatement("{x : x for x in [1, 2, 1]}", ImmutableMap.of(1, 1, 2, 2))
.testStatement("{y : y for y in ['ab', 'c', 'a' + 'b']}",
ImmutableMap.of("ab", "ab", "c", "c"));
}
@Test
public void testListConcatenation() throws Exception {
newTest()
.testStatement("[1, 2] + [3, 4]", MutableList.of(env, 1, 2, 3, 4))
.testStatement("(1, 2) + (3, 4)", Tuple.of(1, 2, 3, 4))
.testIfExactError("unsupported operand type(s) for +: 'list' and 'tuple'",
"[1, 2] + (3, 4)")
.testIfExactError("unsupported operand type(s) for +: 'tuple' and 'list'",
"(1, 2) + [3, 4]");
}
@Test
public void testListMultiply() throws Exception {
newTest()
.testStatement("[1, 2, 3] * 1", MutableList.of(env, 1, 2, 3))
.testStatement("[1, 2] * 2", MutableList.of(env, 1, 2, 1, 2))
.testStatement("[1, 2] * 3", MutableList.of(env, 1, 2, 1, 2, 1, 2))
.testStatement("[1, 2] * 4", MutableList.of(env, 1, 2, 1, 2, 1, 2, 1, 2))
.testStatement("[8] * 5", MutableList.of(env, 8, 8, 8, 8, 8))
.testStatement("[ ] * 10", MutableList.empty())
.testStatement("[1, 2] * 0", MutableList.empty())
.testStatement("[1, 2] * -4", MutableList.empty())
.testStatement(" 2 * [1, 2]", MutableList.of(env, 1, 2, 1, 2))
.testStatement("10 * []", MutableList.empty())
.testStatement(" 0 * [1, 2]", MutableList.empty())
.testStatement("-4 * [1, 2]", MutableList.empty());
}
@Test
public void testTupleMultiply() throws Exception {
newTest()
.testStatement("(1, 2, 3) * 1", Tuple.of(1, 2, 3))
.testStatement("(1, 2) * 2", Tuple.of(1, 2, 1, 2))
.testStatement("(1, 2) * 3", Tuple.of(1, 2, 1, 2, 1, 2))
.testStatement("(1, 2) * 4", Tuple.of(1, 2, 1, 2, 1, 2, 1, 2))
.testStatement("(8,) * 5", Tuple.of(8, 8, 8, 8, 8))
.testStatement("( ) * 10", Tuple.empty())
.testStatement("(1, 2) * 0", Tuple.empty())
.testStatement("(1, 2) * -4", Tuple.empty())
.testStatement(" 2 * (1, 2)", Tuple.of(1, 2, 1, 2))
.testStatement("10 * ()", Tuple.empty())
.testStatement(" 0 * (1, 2)", Tuple.empty())
.testStatement("-4 * (1, 2)", Tuple.empty());
}
@SuppressWarnings("unchecked")
@Test
public void testSelectorListConcatenation() throws Exception {
// TODO(fwe): cannot be handled by current testing suite
SelectorList x = (SelectorList) eval("select({'foo': ['FOO'], 'bar': ['BAR']}) + []");
List elements = x.getElements();
assertThat(elements).hasSize(2);
assertThat(elements.get(0)).isInstanceOf(SelectorValue.class);
assertThat((Iterable) elements.get(1)).isEmpty();
}
@Test
public void testAddSelectIncompatibleType() throws Exception {
newTest()
.testIfErrorContains(
"'+' operator applied to incompatible types (select of list, int)",
"select({'foo': ['FOO'], 'bar': ['BAR']}) + 1");
}
@Test
public void testAddSelectIncompatibleType2() throws Exception {
newTest()
.testIfErrorContains(
"'+' operator applied to incompatible types (select of list, select of int)",
"select({'foo': ['FOO']}) + select({'bar': 2})");
}
@Test
public void testListComprehensionFailsOnNonSequence() throws Exception {
newTest().testIfErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]");
}
@Test
public void testListComprehensionOnString() throws Exception {
newTest("--incompatible_string_is_not_iterable=false")
.testExactOrder("[x for x in 'abc']", "a", "b", "c");
}
@Test
public void testListComprehensionOnStringIsForbidden() throws Exception {
newTest("--incompatible_string_is_not_iterable=true")
.testIfErrorContains("type 'string' is not iterable", "[x for x in 'abc']");
}
@Test
public void testInvalidAssignment() throws Exception {
newTest().testIfErrorContains(
"cannot assign to 'x + 1'", "x + 1 = 2");
}
@Test
public void testListComprehensionOnDictionary() throws Exception {
newTest().testExactOrder("val = ['var_' + n for n in {'a':1,'b':2}] ; val", "var_a", "var_b");
}
@Test
public void testListComprehensionOnDictionaryCompositeExpression() throws Exception {
new BuildTest()
.setUp("d = {1:'a',2:'b'}", "l = [d[x] for x in d]")
.testLookup("l", MutableList.of(env, "a", "b"));
}
@Test
public void testListComprehensionUpdate() throws Exception {
new BuildTest()
.setUp("xs = [1, 2, 3]")
.testIfErrorContains("trying to mutate a locked object",
"[xs.append(4) for x in xs]");
}
@Test
public void testNestedListComprehensionUpdate() throws Exception {
new BuildTest()
.setUp("xs = [1, 2, 3]")
.testIfErrorContains("trying to mutate a locked object",
"[xs.append(4) for x in xs for y in xs]");
}
@Test
public void testListComprehensionUpdateInClause() throws Exception {
new BuildTest()
.setUp("xs = [1, 2, 3]")
.testIfErrorContains("trying to mutate a locked object",
// Use short-circuiting to produce valid output in the event
// the exception is not raised.
"[y for x in xs for y in (xs.append(4) or xs)]");
}
@Test
public void testDictComprehensionUpdate() throws Exception {
new BuildTest()
.setUp("xs = {1:1, 2:2, 3:3}")
.testIfErrorContains("trying to mutate a locked object",
"[xs.popitem() for x in xs]");
}
@Test
public void testInOperator() throws Exception {
newTest()
.testStatement("'b' in ['a', 'b']", Boolean.TRUE)
.testStatement("'c' in ['a', 'b']", Boolean.FALSE)
.testStatement("'b' in ('a', 'b')", Boolean.TRUE)
.testStatement("'c' in ('a', 'b')", Boolean.FALSE)
.testStatement("'b' in {'a' : 1, 'b' : 2}", Boolean.TRUE)
.testStatement("'c' in {'a' : 1, 'b' : 2}", Boolean.FALSE)
.testStatement("1 in {'a' : 1, 'b' : 2}", Boolean.FALSE)
.testStatement("'b' in 'abc'", Boolean.TRUE)
.testStatement("'d' in 'abc'", Boolean.FALSE);
}
@Test
public void testNotInOperator() throws Exception {
newTest()
.testStatement("'b' not in ['a', 'b']", Boolean.FALSE)
.testStatement("'c' not in ['a', 'b']", Boolean.TRUE)
.testStatement("'b' not in ('a', 'b')", Boolean.FALSE)
.testStatement("'c' not in ('a', 'b')", Boolean.TRUE)
.testStatement("'b' not in {'a' : 1, 'b' : 2}", Boolean.FALSE)
.testStatement("'c' not in {'a' : 1, 'b' : 2}", Boolean.TRUE)
.testStatement("1 not in {'a' : 1, 'b' : 2}", Boolean.TRUE)
.testStatement("'b' not in 'abc'", Boolean.FALSE)
.testStatement("'d' not in 'abc'", Boolean.TRUE);
}
@Test
public void testInFail() throws Exception {
newTest()
.testIfErrorContains(
"'in ' requires string as left operand, not 'int'", "1 in '123'")
.testIfErrorContains("'int' is not iterable. in operator only works on ", "'a' in 1");
}
@Test
public void testInCompositeForPrecedence() throws Exception {
newTest().testStatement("not 'a' in ['a'] or 0", 0);
}
private SkylarkValue createObjWithStr() {
return new SkylarkValue() {
@Override
public void repr(SkylarkPrinter printer) {
printer.append("");
}
@Override
public void reprLegacy(SkylarkPrinter printer) {
printer.append("");
}
};
}
@Test
public void testPercOnObject() throws Exception {
newTest("--incompatible_descriptive_string_representations=true")
.update("obj", createObjWithStr())
.testStatement("'%s' % obj", "");
newTest("--incompatible_descriptive_string_representations=false")
.update("obj", createObjWithStr())
.testStatement("'%s' % obj", "");
newTest()
.update("unknown", new Object())
.testStatement("'%s' % unknown", "");
}
@Test
public void testPercOnObjectList() throws Exception {
newTest("--incompatible_descriptive_string_representations=true")
.update("obj", createObjWithStr())
.testStatement("'%s %s' % (obj, obj)", " ");
newTest("--incompatible_descriptive_string_representations=false")
.update("obj", createObjWithStr())
.testStatement("'%s %s' % (obj, obj)", " ");
newTest()
.update("unknown", new Object())
.testStatement(
"'%s %s' % (unknown, unknown)",
" ");
}
@Test
public void testPercOnObjectInvalidFormat() throws Exception {
newTest("--incompatible_descriptive_string_representations=true")
.update("obj", createObjWithStr())
.testIfExactError("invalid argument for format pattern %d", "'%d' % obj");
newTest("--incompatible_descriptive_string_representations=false")
.update("obj", createObjWithStr())
.testIfExactError("invalid argument for format pattern %d", "'%d' % obj");
}
@Test
public void testDictKeys() throws Exception {
newTest().testExactOrder("v = {'a': 1}.keys() + ['b', 'c'] ; v", "a", "b", "c");
}
@Test
public void testDictKeysTooManyArgs() throws Exception {
newTest().testIfExactError(
"too many (2) positional arguments in call to keys(self: dict)", "{'a': 1}.keys('abc')");
}
@Test
public void testDictKeysTooManyKeyArgs() throws Exception {
newTest().testIfExactError("unexpected keyword 'arg' in call to keys(self: dict)",
"{'a': 1}.keys(arg='abc')");
}
@Test
public void testDictKeysDuplicateKeyArgs() throws Exception {
newTest().testIfExactError("duplicate keywords 'arg', 'k' in call to keys",
"{'a': 1}.keys(arg='abc', arg='def', k=1, k=2)");
}
@Test
public void testArgBothPosKey() throws Exception {
newTest().testIfErrorContains(
"arguments 'old', 'new' passed both by position and by name "
+ "in call to replace(self: string, ",
"'banana'.replace('a', 'o', 3, old='a', new=4)");
}
}