blob: 579268fe68f29f555a9f28d5e821dbd955db3cc5 [file] [log] [blame]
// Copyright 2015 Google Inc. 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.analysis.FileConfiguredTarget;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.rules.SkylarkModules;
import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Evaluation tests with Skylark Environment.
*/
@RunWith(JUnit4.class)
public class SkylarkEvaluationTest extends EvaluationTest {
// Restoring defaults overridden by EvaluationTest
@Override
public EvaluationContext newEvaluationContext() {
return SkylarkModules.newEvaluationContext(getEventHandler());
}
@SkylarkModule(name = "Mock", doc = "")
static class Mock {
@SkylarkCallable(doc = "")
public static Integer valueOf(String str) {
return Integer.valueOf(str);
}
@SkylarkCallable(doc = "")
public Boolean isEmpty(String str) {
return str.isEmpty();
}
public void value() {}
@SkylarkCallable(doc = "")
public Mock returnMutable() {
return new Mock();
}
@SkylarkCallable(name = "struct_field", doc = "", structField = true)
public String structField() {
return "a";
}
@SkylarkCallable(name = "function", doc = "", structField = false)
public String function() {
return "a";
}
@SuppressWarnings("unused")
@SkylarkCallable(name = "nullfunc_failing", doc = "", allowReturnNones = false)
public Object nullfuncFailing(String p1, Integer p2) {
return null;
}
@SkylarkCallable(name = "nullfunc_working", doc = "", allowReturnNones = true)
public Object nullfuncWorking() {
return null;
}
@SkylarkCallable(name = "voidfunc", doc = "")
public void voidfunc() {}
@SkylarkCallable(name = "string_list", doc = "")
public ImmutableList<String> stringList() {
return ImmutableList.<String>of("a", "b");
}
@SkylarkCallable(name = "string", doc = "")
public String string() {
return "a";
}
}
@SkylarkModule(name = "MockInterface", doc = "")
static interface MockInterface {
@SkylarkCallable(doc = "")
public Boolean isEmptyInterface(String str);
}
static final class MockSubClass extends Mock implements MockInterface {
@Override
public Boolean isEmpty(String str) {
return str.isEmpty();
}
@Override
public Boolean isEmptyInterface(String str) {
return str.isEmpty();
}
@SkylarkCallable(doc = "")
public Boolean isEmptyClassNotAnnotated(String str) {
return str.isEmpty();
}
}
static final class MockClassObject implements ClassObject {
@Override
public Object getValue(String name) {
switch (name) {
case "field": return "a";
case "nset": return NestedSetBuilder.stableOrder().build();
}
throw new IllegalStateException();
}
@Override
public ImmutableCollection<String> getKeys() {
return ImmutableList.of("field", "nset");
}
@Override
public String errorMessage(String name) {
return null;
}
}
@SkylarkModule(name = "MockMultipleMethodClass", doc = "")
static final class MockMultipleMethodClass {
@SuppressWarnings("unused")
@SkylarkCallable(doc = "")
public void method(Object o) {}
@SuppressWarnings("unused")
@SkylarkCallable(doc = "")
public void method(String i) {}
}
@Test
public void testSimpleIf() throws Exception {
eval("def foo():",
" a = 0",
" x = 0",
" if x: a = 5",
" return a",
"a = foo()");
assertEquals(0, lookup("a"));
}
@Test
public void testIfPass() throws Exception {
eval("def foo():",
" a = 1",
" x = True",
" if x: pass",
" return a",
"a = foo()");
assertEquals(1, lookup("a"));
}
@Test
public void testNestedIf() throws Exception {
executeNestedIf(0, 0);
assertEquals(0, lookup("x"));
setUp();
executeNestedIf(1, 0);
assertEquals(3, lookup("x"));
setUp();
executeNestedIf(1, 1);
assertEquals(5, lookup("x"));
}
private void executeNestedIf(int x, int y) throws Exception {
String fun = String.format("foo%s%s", x, y);
eval("def " + fun + "():",
" x = " + x,
" y = " + y,
" a = 0",
" b = 0",
" if x:",
" if y:",
" a = 2",
" b = 3",
" return a + b",
"x = " + fun + "()");
}
@Test
public void testIfElse() throws Exception {
executeIfElse("foo", "something", 2);
executeIfElse("bar", "", 3);
}
private void executeIfElse(String fun, String y, int expected) throws Exception {
eval("def " + fun + "():",
" y = '" + y + "'",
" x = 5",
" if x:",
" if y: a = 2",
" else: a = 3",
" return a\n");
assertEquals(expected, eval(fun + "()"));
}
@Test
public void testIfElifElse_IfExecutes() throws Exception {
execIfElifElse(1, 0, 1);
}
@Test
public void testIfElifElse_ElifExecutes() throws Exception {
execIfElifElse(0, 1, 2);
}
@Test
public void testIfElifElse_ElseExecutes() throws Exception {
execIfElifElse(0, 0, 3);
}
private void execIfElifElse(int x, int y, int v) throws Exception {
eval("def foo():",
" x = " + x + "",
" y = " + y + "",
" if x:",
" return 1",
" elif y:",
" return 2",
" else:",
" return 3",
"v = foo()");
assertEquals(v, lookup("v"));
}
@Test
public void testForOnList() throws Exception {
eval("def foo():",
" s = ''",
" for i in ['hello', ' ', 'world']:",
" s = s + i",
" return s",
"s = foo()\n");
assertEquals("hello world", lookup("s"));
}
@SuppressWarnings("unchecked")
@Test
public void testForOnString() throws Exception {
eval("def foo():",
" s = []",
" for i in 'abc':",
" s = s + [i]",
" return s",
"s = foo()\n");
assertThat((Iterable<Object>) lookup("s")).containsExactly("a", "b", "c").inOrder();
}
@Test
public void testForAssignmentList() throws Exception {
eval("def foo():",
" d = ['a', 'b', 'c']",
" s = ''",
" for i in d:",
" s = s + i",
" d = ['d', 'e', 'f']", // check that we use the old list
" return s",
"s = foo()\n");
assertEquals("abc", lookup("s"));
}
@Test
public void testForAssignmentDict() throws Exception {
eval("def func():",
" d = {'a' : 1, 'b' : 2, 'c' : 3}",
" s = ''",
" for i in d:",
" s = s + i",
" d = {'d' : 1, 'e' : 2, 'f' : 3}",
" return s",
"s = func()");
assertEquals("abc", lookup("s"));
}
@Test
public void testForNotIterable() throws Exception {
update("mock", new Mock());
checkEvalError("type 'int' is not iterable",
"def func():",
" for i in mock.value_of('1'): a = i",
"func()\n");
}
@Test
public void testForOnDictionary() throws Exception {
eval("def foo():",
" d = {1: 'a', 2: 'b', 3: 'c'}",
" s = ''",
" for i in d: s = s + d[i]",
" return s",
"s = foo()");
assertEquals("abc", lookup("s"));
}
@Test
public void testForLoopReuseVariable() throws Exception {
eval("def foo():",
" s = ''",
" for i in ['a', 'b']:",
" for i in ['c', 'd']: s = s + i",
" return s",
"s = foo()");
assertEquals("cdcd", lookup("s"));
}
@Test
public void testForLoopMultipleVariables() throws Exception {
eval("def foo():",
" s = ''",
" for [i, j] in [[1, 2], [3, 4]]:",
" s = s + str(i) + str(j) + '.'",
" return s",
"s = foo()");
assertEquals("12.34.", lookup("s"));
}
@Test
public void testNoneAssignment() throws Exception {
eval("def foo(x=None):",
" x = 1",
" x = None",
" return 2",
"s = foo()");
assertEquals(2, lookup("s"));
}
@Test
public void testJavaCalls() throws Exception {
update("mock", new Mock());
eval("b = mock.is_empty('a')");
assertEquals(Boolean.FALSE, lookup("b"));
}
@Test
public void testJavaCallsOnSubClass() throws Exception {
update("mock", new MockSubClass());
eval("b = mock.is_empty('a')");
assertEquals(Boolean.FALSE, lookup("b"));
}
@Test
public void testJavaCallsOnInterface() throws Exception {
update("mock", new MockSubClass());
eval("b = mock.is_empty_interface('a')");
assertEquals(Boolean.FALSE, lookup("b"));
}
@Test
public void testJavaCallsNotSkylarkCallable() throws Exception {
update("mock", new Mock());
checkEvalError("No matching method found for value() in Mock", "mock.value()");
}
@Test
public void testJavaCallsNoMethod() throws Exception {
checkEvalError("No matching method found for bad() in int", "s = 3.bad()");
}
@Test
public void testJavaCallsNoMethodErrorMsg() throws Exception {
checkEvalError("No matching method found for bad(string, string, string) in int",
"s = 3.bad('a', 'b', 'c')");
}
@Test
public void testJavaCallsMultipleMethod() throws Exception {
update("mock", new MockMultipleMethodClass());
checkEvalError("Multiple matching methods for method(string) in MockMultipleMethodClass",
"s = mock.method('string')");
}
@Test
public void testJavaCallWithKwargs() throws Exception {
checkEvalError("Keyword arguments are not allowed when calling a java method"
+ "\nwhile calling method 'compare_to' on object 3 of type int",
"comp = 3.compare_to(x = 4)");
}
@Test
public void testNoJavaCallsWithoutSkylark() throws Exception {
checkEvalError("No matching method found for to_string() in int", "s = 3.to_string()\n");
}
@Test
public void testNoJavaCallsIfClassNotAnnotated() throws Exception {
update("mock", new MockSubClass());
checkEvalError(
"No matching method found for is_empty_class_not_annotated(string) in MockSubClass",
"b = mock.is_empty_class_not_annotated('a')");
}
@Test
public void testStructAccess() throws Exception {
update("mock", new Mock());
eval("v = mock.struct_field");
assertEquals("a", lookup("v"));
}
@Test
public void testStructAccessAsFuncall() throws Exception {
update("mock", new Mock());
checkEvalError("No matching method found for struct_field() in Mock",
"v = mock.struct_field()");
}
@Test
public void testStructAccessOfMethod() throws Exception {
update("mock", new Mock());
checkEvalError("Object of type 'Mock' has no field \"function\"",
"v = mock.function");
}
@Test
public void testConditionalStructConcatenation() throws Exception {
eval("def func():",
" x = struct(a = 1, b = 2)",
" if True:",
" x += struct(c = 1, d = 2)",
" return x",
"x = func()\n");
SkylarkClassObject x = (SkylarkClassObject) lookup("x");
assertEquals(1, x.getValue("a"));
assertEquals(2, x.getValue("b"));
assertEquals(1, x.getValue("c"));
assertEquals(2, x.getValue("d"));
}
@Test
public void testJavaFunctionReturnsMutableObject() throws Exception {
update("mock", new Mock());
checkEvalError("Method 'return_mutable' returns a mutable object (type of Mock)",
"mock.return_mutable()");
}
@Test
public void testJavaFunctionReturnsNullFails() throws Exception {
update("mock", new Mock());
checkEvalError("Method invocation returned None,"
+ " please contact Skylark developers: nullfunc_failing(\"abc\", 1)",
"mock.nullfunc_failing('abc', 1)");
}
@Test
public void testClassObjectAccess() throws Exception {
update("mock", new MockClassObject());
eval("v = mock.field");
assertEquals("a", lookup("v"));
}
@Test
public void testInSet() throws Exception {
assertEquals(Boolean.TRUE, eval("'b' in set(['a', 'b'])"));
assertEquals(Boolean.FALSE, eval("'c' in set(['a', 'b'])"));
assertEquals(Boolean.FALSE, eval("1 in set(['a', 'b'])"));
}
@Test
public void testClassObjectCannotAccessNestedSet() throws Exception {
update("mock", new MockClassObject());
checkEvalError("Type is not allowed in Skylark: EmptyNestedSet", "v = mock.nset");
}
@Test
public void testJavaFunctionReturnsNone() throws Exception {
update("mock", new Mock());
eval("v = mock.nullfunc_working()");
assertSame(Environment.NONE, lookup("v"));
}
@Test
public void testVoidJavaFunctionReturnsNone() throws Exception {
update("mock", new Mock());
eval("v = mock.voidfunc()");
assertSame(Environment.NONE, lookup("v"));
}
@Test
public void testAugmentedAssignment() throws Exception {
eval("def f1(x):",
" x += 1",
" return x",
"",
"foo = f1(41)\n");
assertEquals(42, lookup("foo"));
}
@Test
public void testStaticDirectJavaCall() throws Exception {
update("Mock", Mock.class);
eval("val = Mock.value_of('8')");
assertEquals(8, lookup("val"));
}
@Test
public void testStaticDirectJavaCallMethodIsNonStatic() throws Exception {
update("Mock", Mock.class);
checkEvalError("Method 'is_empty' is not static", "val = Mock.is_empty('a')");
}
@Test
public void testDictComprehensions_IterationOrder() throws Exception {
eval("def foo():",
" d = {x : x for x in ['c', 'a', 'b']}",
" s = ''",
" for a in d:",
" s += a",
" return s",
"s = foo()");
assertEquals("abc", lookup("s"));
}
@Test
public void testStructCreation() throws Exception {
eval("x = struct(a = 1, b = 2)");
assertThat(lookup("x")).isInstanceOf(ClassObject.class);
}
@Test
public void testStructFields() throws Exception {
eval("x = struct(a = 1, b = 2)");
ClassObject x = (ClassObject) lookup("x");
assertEquals(1, x.getValue("a"));
assertEquals(2, x.getValue("b"));
}
@Test
public void testStructAccessingFieldsFromSkylark() throws Exception {
eval("x = struct(a = 1, b = 2)",
"x1 = x.a",
"x2 = x.b\n");
assertEquals(1, lookup("x1"));
assertEquals(2, lookup("x2"));
}
@Test
public void testStructAccessingUnknownField() throws Exception {
checkEvalError("Object of type 'struct' has no field \"c\"",
"x = struct(a = 1, b = 2)",
"y = x.c\n");
}
@Test
public void testStructAccessingFieldsWithArgs() throws Exception {
checkEvalError("No matching method found for a(int) in struct",
"x = struct(a = 1, b = 2)",
"x1 = x.a(1)\n");
}
@Test
public void testStructPosArgs() throws Exception {
checkEvalError("struct(**kwargs) does not accept positional arguments, but got 1",
"x = struct(1, b = 2)\n");
}
@Test
public void testStructConcatenationFieldNames() throws Exception {
eval("x = struct(a = 1, b = 2)",
"y = struct(c = 1, d = 2)",
"z = x + y\n");
SkylarkClassObject z = (SkylarkClassObject) lookup("z");
assertEquals(ImmutableSet.of("a", "b", "c", "d"), z.getKeys());
}
@Test
public void testStructConcatenationFieldValues() throws Exception {
eval("x = struct(a = 1, b = 2)",
"y = struct(c = 1, d = 2)",
"z = x + y\n");
SkylarkClassObject z = (SkylarkClassObject) lookup("z");
assertEquals(1, z.getValue("a"));
assertEquals(2, z.getValue("b"));
assertEquals(1, z.getValue("c"));
assertEquals(2, z.getValue("d"));
}
@Test
public void testStructConcatenationCommonFields() throws Exception {
checkEvalError("Cannot concat structs with common field(s): a",
"x = struct(a = 1, b = 2)",
"y = struct(c = 1, a = 2)",
"z = x + y\n");
}
@Test
public void testDotExpressionOnNonStructObject() throws Exception {
checkEvalError("Object of type 'string' has no field \"field\"", "x = 'a'.field");
}
@Test
public void testPlusEqualsOnDict() throws Exception {
eval("def func():",
" d = {'a' : 1}",
" d += {'b' : 2}",
" return d",
"d = func()");
assertEquals(ImmutableMap.of("a", 1, "b", 2), lookup("d"));
}
@Test
public void testDictAssignmentAsLValue() throws Exception {
eval("def func():",
" d = {'a' : 1}",
" d['b'] = 2",
" return d",
"d = func()");
assertEquals(ImmutableMap.of("a", 1, "b", 2), lookup("d"));
}
@Test
public void testDictAssignmentAsLValueNoSideEffects() throws Exception {
eval("def func(d):",
" d['b'] = 2",
"d = {'a' : 1}",
"func(d)");
assertEquals(ImmutableMap.of("a", 1), lookup("d"));
}
@Test
public void testListIndexAsLValueAsLValue() throws Exception {
checkEvalError("unsupported operand type(s) for +: 'list' and 'dict'",
"def id(l):",
" return l",
"def func():",
" l = id([1])",
" l[0] = 2",
" return l",
"l = func()");
}
@Test
public void testTopLevelDict() throws Exception {
eval("if 1:",
" v = 'a'",
"else:",
" v = 'b'");
assertEquals("a", lookup("v"));
}
@Test
public void testUserFunctionKeywordArgs() throws Exception {
eval("def foo(a, b, c):",
" return a + b + c",
"s = foo(1, c=2, b=3)");
assertEquals(6, lookup("s"));
}
@Test
public void testNoneTrueFalseInSkylark() throws Exception {
eval("a = None",
"b = True",
"c = False");
assertSame(Environment.NONE, lookup("a"));
assertTrue((Boolean) lookup("b"));
assertFalse((Boolean) lookup("c"));
}
@Test
public void testHasattr() throws Exception {
eval("s = struct(a=1)",
"x = hasattr(s, 'a')",
"y = hasattr(s, 'b')\n");
assertTrue((Boolean) lookup("x"));
assertFalse((Boolean) lookup("y"));
}
@Test
public void testHasattrMethods() throws Exception {
update("mock", new Mock());
eval("a = hasattr(mock, 'struct_field')",
"b = hasattr(mock, 'function')",
"c = hasattr(mock, 'is_empty')",
"d = hasattr('str', 'replace')",
"e = hasattr(mock, 'other')\n");
assertTrue((Boolean) lookup("a"));
assertTrue((Boolean) lookup("b"));
assertTrue((Boolean) lookup("c"));
assertTrue((Boolean) lookup("d"));
assertFalse((Boolean) lookup("e"));
}
@Test
public void testGetattr() throws Exception {
eval("s = struct(a='val')",
"x = getattr(s, 'a')",
"y = getattr(s, 'b', 'def')",
"z = getattr(s, 'b', default = 'def')",
"w = getattr(s, 'a', default='ignored')");
assertEquals("val", lookup("x"));
assertEquals("def", lookup("y"));
assertEquals("def", lookup("z"));
assertEquals("val", lookup("w"));
}
@Test
public void testGetattrNoAttr() throws Exception {
checkEvalError("Object of type 'struct' has no field \"b\"",
"s = struct(a='val')",
"getattr(s, 'b')");
}
@SuppressWarnings("unchecked")
@Test
public void testListAnTupleConcatenationDoesNotWorkInSkylark() throws Exception {
checkEvalError("cannot concatenate lists and tuples", "[1, 2] + (3, 4)");
}
@Test
public void testCannotCreateMixedListInSkylark() throws Exception {
update("mock", new Mock());
checkEvalError(
"Incompatible types in list: found a int but the previous elements were strings",
"[mock.string(), 1, 2]");
}
@Test
public void testCannotConcatListInSkylarkWithDifferentGenericTypes() throws Exception {
update("mock", new Mock());
checkEvalError("cannot concatenate list of string with list of int",
"mock.string_list() + [1, 2]");
}
@SuppressWarnings("unchecked")
@Test
public void testConcatEmptyListWithNonEmptyWorks() throws Exception {
eval("l = [] + ['a', 'b']");
assertThat((Iterable<Object>) lookup("l")).containsExactly("a", "b").inOrder();
}
@Test
public void testFormatStringWithTuple() throws Exception {
eval("v = '%s%s' % ('a', 1)");
assertEquals("a1", lookup("v"));
}
@Test
public void testSingletonTuple() throws Exception {
eval("v = (1,)");
assertEquals("(1,)", lookup("v").toString());
}
@SuppressWarnings("unchecked")
@Test
public void testDirFindsClassObjectFields() throws Exception {
update("mock", new MockClassObject());
eval("v = dir(mock)");
assertThat((Iterable<String>) lookup("v")).containsExactly("field", "nset").inOrder();
}
@SuppressWarnings("unchecked")
@Test
public void testDirFindsJavaObjectStructFieldsAndMethods() throws Exception {
update("mock", new Mock());
eval("v = dir(mock)");
assertThat((Iterable<String>) lookup("v")).containsExactly("function", "is_empty",
"nullfunc_failing", "nullfunc_working", "return_mutable", "string", "string_list",
"struct_field", "value_of", "voidfunc").inOrder();
}
@Test
public void testPrint() throws Exception {
setFailFast(false);
eval("print('hello')");
assertContainsEvent("hello");
eval("print('a', 'b')");
assertContainsEvent("a b");
eval("print('a', 'b', sep='x')");
assertContainsEvent("axb");
}
@Test
public void testPrintBadKwargs() throws Exception {
checkEvalError(
"unexpected keywords 'end', 'other' in call to print(*args, sep: string = \" \")",
"print(end='x', other='y')");
}
@Test
public void testSkylarkTypes() {
assertEquals(TransitiveInfoCollection.class,
EvalUtils.getSkylarkType(FileConfiguredTarget.class));
assertEquals(TransitiveInfoCollection.class,
EvalUtils.getSkylarkType(RuleConfiguredTarget.class));
assertEquals(Artifact.class, EvalUtils.getSkylarkType(SpecialArtifact.class));
}
// Override tests in EvaluationTest incompatible with Skylark
@SuppressWarnings("unchecked")
@Override
@Test
public void testConcatLists() throws Exception {
// list
Object x = eval("[1,2] + [3,4]");
assertThat((Iterable<Object>) x).containsExactly(1, 2, 3, 4).inOrder();
// tuple
x = eval("(1,2)");
assertThat((Iterable<Object>) x).containsExactly(1, 2).inOrder();
assertTrue(((SkylarkList) x).isTuple());
x = eval("(1,2) + (3,4)");
assertThat((Iterable<Object>) x).containsExactly(1, 2, 3, 4).inOrder();
assertTrue(((SkylarkList) x).isTuple());
}
@Override
@Test
public void testListConcatenation() throws Exception {}
@Test
public void testInFail() throws Exception {
checkEvalError("in operator only works on strings if the left operand is also a string",
"1 in '123'");
checkEvalError("in operator only works on lists, tuples, sets, dicts and strings",
"'a' in 1");
}
@Override
@Test
public void testCompareStringInt() throws Exception {
checkEvalError("Cannot compare string with int", "'a' >= 1");
}
@Override
@Test
public void testListComprehensionsMultipleVariablesFail() throws Exception {
checkEvalError("lvalue has length 3, but rvalue has has length 2",
"def foo (): return [x + y for x, y, z in [(1, 2), (3, 4)]]",
"foo()");
checkEvalError("type 'int' is not a collection",
"def bar (): return [x + y for x, y in (1, 2)]",
"bar()");
checkEvalError("lvalue has length 3, but rvalue has has length 2",
"[x + y for x, y, z in [(1, 2), (3, 4)]]");
// can't reuse the same local variable twice(!)
checkEvalError("ERROR 1:1: Variable x is read only", "[x + y for x, y in (1, 2)]");
checkEvalError("type 'int' is not a collection", "[x2 + y2 for x2, y2 in (1, 2)]");
}
@Override
@Test
public void testNotCallInt() throws Exception {
eval("sum = 123456");
assertEquals(123456, lookup("sum"));
checkEvalError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)");
assertEquals(123456, eval("sum"));
}
@Test
public void testConditionalExpressionAtToplevel() throws Exception {
eval("x = 1 if 2 else 3");
assertEquals(1, lookup("x"));
}
@Test
public void testConditionalExpressionInFunction() throws Exception {
eval("def foo(a, b, c): return a+b if c else a-b\n");
assertEquals(18, eval("foo(23, 5, 0)"));
}
}