blob: ff3c70076051bea85eee4fcf0a0e8ae99d69d25c [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 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.events.EventKind;
import com.google.devtools.build.lib.events.util.EventCollectionApparatus;
import com.google.devtools.build.lib.packages.MethodLibrary;
import com.google.devtools.build.lib.rules.SkylarkModules;
import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
import java.util.List;
/**
* Evaluation tests with Skylark Environment.
*/
public class SkylarkEvaluationTest extends EvaluationTest {
@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) {}
}
private static final ImmutableMap<String, SkylarkType> MOCK_TYPES = ImmutableMap
.<String, SkylarkType>of("mock", SkylarkType.UNKNOWN, "Mock", SkylarkType.UNKNOWN);
@Override
public void setUp() throws Exception {
super.setUp();
syntaxEvents = new EventCollectionApparatus(EventKind.ALL_EVENTS);
env = new SkylarkEnvironment(syntaxEvents.collector());
MethodLibrary.setupMethodEnvironment(env);
}
@Override
public Environment singletonEnv(String id, Object value) {
SkylarkEnvironment env = new SkylarkEnvironment(syntaxEvents.collector());
env.update(id, value);
return env;
}
public void testSimpleIf() throws Exception {
exec(parseFileForSkylark(
"def foo():\n"
+ " a = 0\n"
+ " x = 0\n"
+ " if x: a = 5\n"
+ " return a\n"
+ "a = foo()"), env);
assertEquals(0, env.lookup("a"));
}
public void testNestedIf() throws Exception {
executeNestedIf(0, 0, env);
assertEquals(0, env.lookup("x"));
executeNestedIf(1, 0, env);
assertEquals(3, env.lookup("x"));
executeNestedIf(1, 1, env);
assertEquals(5, env.lookup("x"));
}
private void executeNestedIf(int x, int y, Environment env) throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " x = " + x + "\n"
+ " y = " + y + "\n"
+ " a = 0\n"
+ " b = 0\n"
+ " if x:\n"
+ " if y:\n"
+ " a = 2\n"
+ " b = 3\n"
+ " return a + b\n"
+ "x = foo()");
exec(input, env);
}
public void testIfElse() throws Exception {
executeIfElse("something", 2);
executeIfElse("", 3);
}
private void executeIfElse(String y, int expectedA) throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " y = '" + y + "'\n"
+ " x = 5\n"
+ " if x:\n"
+ " if y: a = 2\n"
+ " else: a = 3\n"
+ " return a\n"
+ "a = foo()");
exec(input, env);
assertEquals(expectedA, env.lookup("a"));
}
public void testIfElifElse_IfExecutes() throws Exception {
execIfElifElse(1, 0, 1);
}
public void testIfElifElse_ElifExecutes() throws Exception {
execIfElifElse(0, 1, 2);
}
public void testIfElifElse_ElseExecutes() throws Exception {
execIfElifElse(0, 0, 3);
}
private void execIfElifElse(int x, int y, int v) throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " x = " + x + "\n"
+ " y = " + y + "\n"
+ " if x:\n"
+ " return 1\n"
+ " elif y:\n"
+ " return 2\n"
+ " else:\n"
+ " return 3\n"
+ "v = foo()");
exec(input, env);
assertEquals(v, env.lookup("v"));
}
public void testForOnList() throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " s = ''\n"
+ " for i in ['hello', ' ', 'world']:\n"
+ " s = s + i\n"
+ " return s\n"
+ "s = foo()\n");
exec(input, env);
assertEquals("hello world", env.lookup("s"));
}
@SuppressWarnings("unchecked")
public void testForOnString() throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " s = []\n"
+ " for i in 'abc':\n"
+ " s = s + [i]\n"
+ " return s\n"
+ "s = foo()\n");
exec(input, env);
assertThat((Iterable<Object>) env.lookup("s")).containsExactly("a", "b", "c").inOrder();
}
public void testForAssignmentList() throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " d = ['a', 'b', 'c']\n"
+ " s = ''\n"
+ " for i in d:\n"
+ " s = s + i\n"
+ " d = ['d', 'e', 'f']\n" // check that we use the old list
+ " return s\n"
+ "s = foo()\n");
exec(input, env);
assertEquals("abc", env.lookup("s"));
}
public void testForAssignmentDict() throws Exception {
List<Statement> input = parseFileForSkylark(
"def func():\n"
+ " d = {'a' : 1, 'b' : 2, 'c' : 3}\n"
+ " s = ''\n"
+ " for i in d:\n"
+ " s = s + i\n"
+ " d = {'d' : 1, 'e' : 2, 'f' : 3}\n"
+ " return s\n"
+ "s = func()");
exec(input, env);
assertEquals("abc", env.lookup("s"));
}
public void testForNotIterable() throws Exception {
env.update("mock", new Mock());
List<Statement> input = parseFileForSkylark(
"def func():\n"
+ " for i in mock.value_of('1'): a = i\n"
+ "func()\n", MOCK_TYPES);
checkEvalError(input, env, "type 'int' is not an iterable");
}
public void testForOnDictionary() throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " d = {1: 'a', 2: 'b', 3: 'c'}\n"
+ " s = ''\n"
+ " for i in d: s = s + d[i]\n"
+ " return s\n"
+ "s = foo()");
exec(input, env);
assertEquals("abc", env.lookup("s"));
}
public void testForLoopReuseVariable() throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " s = ''\n"
+ " for i in ['a', 'b']:\n"
+ " for i in ['c', 'd']: s = s + i\n"
+ " return s\n"
+ "s = foo()");
exec(input, env);
assertEquals("cdcd", env.lookup("s"));
}
public void testNoneAssignment() throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo(x=None):\n"
+ " x = 1\n"
+ " x = None\n"
+ " return 2\n"
+ "s = foo()");
exec(input, env);
assertEquals(2, env.lookup("s"));
}
public void testJavaCalls() throws Exception {
env.update("mock", new Mock());
List<Statement> input = parseFileForSkylark(
"b = mock.is_empty('a')", MOCK_TYPES);
exec(input, env);
assertEquals(Boolean.FALSE, env.lookup("b"));
}
public void testJavaCallsOnSubClass() throws Exception {
env.update("mock", new MockSubClass());
List<Statement> input = parseFileForSkylark(
"b = mock.is_empty('a')", MOCK_TYPES);
exec(input, env);
assertEquals(Boolean.FALSE, env.lookup("b"));
}
public void testJavaCallsOnInterface() throws Exception {
env.update("mock", new MockSubClass());
List<Statement> input = parseFileForSkylark(
"b = mock.is_empty_interface('a')", MOCK_TYPES);
exec(input, env);
assertEquals(Boolean.FALSE, env.lookup("b"));
}
public void testJavaCallsNotSkylarkCallable() throws Exception {
env.update("mock", new Mock());
List<Statement> input = parseFileForSkylark("mock.value()", MOCK_TYPES);
checkEvalError(input, env, "No matching method found for value() in Mock");
}
public void testJavaCallsNoMethod() throws Exception {
List<Statement> input = parseFileForSkylark(
"s = 3.bad()");
checkEvalError(input, env, "No matching method found for bad() in int");
}
public void testJavaCallsNoMethodErrorMsg() throws Exception {
List<Statement> input = parseFileForSkylark(
"s = 3.bad('a', 'b', 'c')");
checkEvalError(input, env,
"No matching method found for bad(string, string, string) in int");
}
public void testJavaCallsMultipleMethod() throws Exception {
env.update("mock", new MockMultipleMethodClass());
List<Statement> input = parseFileForSkylark(
"s = mock.method('string')", MOCK_TYPES);
checkEvalError(input, env,
"Multiple matching methods for method(string) in MockMultipleMethodClass");
}
public void testJavaCallWithKwargs() throws Exception {
List<Statement> input = parseFileForSkylark("comp = 3.compare_to(x = 4)");
checkEvalError(input, env, "Keyword arguments are not allowed when calling a java method"
+ "\nwhile calling method 'compare_to' on object 3 of type int");
}
public void testNoJavaCallsWithoutSkylark() throws Exception {
List<Statement> input = parseFileForSkylark("s = 3.to_string()\n");
checkEvalError(input, env, "No matching method found for to_string() in int");
}
public void testNoJavaCallsIfClassNotAnnotated() throws Exception {
env.update("mock", new MockSubClass());
List<Statement> input = parseFileForSkylark(
"b = mock.is_empty_class_not_annotated('a')", MOCK_TYPES);
checkEvalError(input, env,
"No matching method found for is_empty_class_not_annotated(string) in MockSubClass");
}
public void testStructAccess() throws Exception {
env.update("mock", new Mock());
List<Statement> input = parseFileForSkylark(
"v = mock.struct_field", MOCK_TYPES);
exec(input, env);
assertEquals("a", env.lookup("v"));
}
public void testStructAccessAsFuncall() throws Exception {
env.update("mock", new Mock());
checkEvalError(parseFileForSkylark("v = mock.struct_field()", MOCK_TYPES), env,
"No matching method found for struct_field() in Mock");
}
public void testStructAccessOfMethod() throws Exception {
env.update("mock", new Mock());
checkEvalError(parseFileForSkylark(
"v = mock.function", MOCK_TYPES), env, "Object of type 'Mock' has no field 'function'");
}
public void testJavaFunctionReturnsMutableObject() throws Exception {
env.update("mock", new Mock());
List<Statement> input = parseFileForSkylark("mock.return_mutable()", MOCK_TYPES);
checkEvalError(input, env, "Method 'return_mutable' returns a mutable object (type of Mock)");
}
public void testJavaFunctionReturnsNullFails() throws Exception {
env.update("mock", new Mock());
List<Statement> input = parseFileForSkylark("mock.nullfunc_failing('abc', 1)", MOCK_TYPES);
checkEvalError(input, env, "Method invocation returned None,"
+ " please contact Skylark developers: nullfunc_failing(\"abc\", 1)");
}
public void testClassObjectAccess() throws Exception {
env.update("mock", new MockClassObject());
exec(parseFileForSkylark("v = mock.field", MOCK_TYPES), env);
assertEquals("a", env.lookup("v"));
}
public void testClassObjectCannotAccessNestedSet() throws Exception {
env.update("mock", new MockClassObject());
checkEvalError(parseFileForSkylark("v = mock.nset", MOCK_TYPES), env,
"Type is not allowed in Skylark: EmptyNestedSet");
}
public void testJavaFunctionReturnsNone() throws Exception {
env.update("mock", new Mock());
exec(parseFileForSkylark("v = mock.nullfunc_working()", MOCK_TYPES), env);
assertSame(Environment.NONE, env.lookup("v"));
}
public void testVoidJavaFunctionReturnsNone() throws Exception {
env.update("mock", new Mock());
exec(parseFileForSkylark("v = mock.voidfunc()", MOCK_TYPES), env);
assertSame(Environment.NONE, env.lookup("v"));
}
public void testAugmentedAssignment() throws Exception {
exec(parseFileForSkylark(
"def f1(x):\n"
+ " x += 1\n"
+ " return x\n"
+ "\n"
+ "foo = f1(41)\n"), env);
assertEquals(42, env.lookup("foo"));
}
public void testStaticDirectJavaCall() throws Exception {
List<Statement> input = parseFileForSkylark(
"val = Mock.value_of('8')", MOCK_TYPES);
env.update("Mock", Mock.class);
exec(input, env);
assertEquals(8, env.lookup("val"));
}
public void testStaticDirectJavaCallMethodIsNonStatic() throws Exception {
List<Statement> input = parseFileForSkylark(
"val = Mock.is_empty('a')", MOCK_TYPES);
env.update("Mock", Mock.class);
checkEvalError(input, env, "Method 'is_empty' is not static");
}
public void testDictComprehensions_IterationOrder() throws Exception {
List<Statement> input = parseFileForSkylark(
"def foo():\n"
+ " d = {x : x for x in ['c', 'a', 'b']}\n"
+ " s = ''\n"
+ " for a in d:\n"
+ " s += a\n"
+ " return s\n"
+ "s = foo()");
exec(input, env);
assertEquals("cab", env.lookup("s"));
}
public void testStructCreation() throws Exception {
exec(parseFileForSkylark("x = struct(a = 1, b = 2)"), env);
assertThat(env.lookup("x")).isInstanceOf(ClassObject.class);
}
public void testStructFields() throws Exception {
exec(parseFileForSkylark("x = struct(a = 1, b = 2)"), env);
ClassObject x = (ClassObject) env.lookup("x");
assertEquals(1, x.getValue("a"));
assertEquals(2, x.getValue("b"));
}
public void testStructAccessingFieldsFromSkylark() throws Exception {
exec(parseFileForSkylark(
"x = struct(a = 1, b = 2)\n"
+ "x1 = x.a\n"
+ "x2 = x.b\n"), env);
assertEquals(1, env.lookup("x1"));
assertEquals(2, env.lookup("x2"));
}
public void testStructAccessingUnknownField() throws Exception {
checkEvalError(parseFileForSkylark(
"x = struct(a = 1, b = 2)\n"
+ "y = x.c\n"), env, "Object of type 'struct' has no field 'c'");
}
public void testStructAccessingFieldsWithArgs() throws Exception {
checkEvalError(parseFileForSkylark(
"x = struct(a = 1, b = 2)\n"
+ "x1 = x.a(1)\n"),
env, "No matching method found for a(int) in struct");
}
public void testStructPosArgs() throws Exception {
checkEvalError(parseFileForSkylark(
"x = struct(1, b = 2)\n"),
env, "struct only supports keyword arguments");
}
public void testStructConcatenationFieldNames() throws Exception {
exec(parseFileForSkylark(
"x = struct(a = 1, b = 2)\n"
+ "y = struct(c = 1, d = 2)\n"
+ "z = x + y\n"), env);
SkylarkClassObject z = (SkylarkClassObject) env.lookup("z");
assertEquals(ImmutableSet.of("a", "b", "c", "d"), z.getKeys());
}
public void testStructConcatenationFieldValues() throws Exception {
exec(parseFileForSkylark(
"x = struct(a = 1, b = 2)\n"
+ "y = struct(c = 1, d = 2)\n"
+ "z = x + y\n"), env);
SkylarkClassObject z = (SkylarkClassObject) env.lookup("z");
assertEquals(1, z.getValue("a"));
assertEquals(2, z.getValue("b"));
assertEquals(1, z.getValue("c"));
assertEquals(2, z.getValue("d"));
}
public void testStructConcatenationCommonFields() throws Exception {
checkEvalError(parseFileForSkylark(
"x = struct(a = 1, b = 2)\n"
+ "y = struct(c = 1, a = 2)\n"
+ "z = x + y\n"), env, "Cannot concat structs with common field(s): a");
}
public void testDotExpressionOnNonStructObject() throws Exception {
checkEvalError(parseFileForSkylark(
"x = 'a'.field"), env, "Object of type 'string' has no field 'field'");
}
public void testPlusEqualsOnDict() throws Exception {
MethodLibrary.setupMethodEnvironment(env);
exec(parseFileForSkylark(
"def func():\n"
+ " d = {'a' : 1}\n"
+ " d += {'b' : 2}\n"
+ " return d\n"
+ "d = func()"), env);
assertEquals(ImmutableMap.of("a", 1, "b", 2), env.lookup("d"));
}
public void testDictAssignmentAsLValue() throws Exception {
exec(parseFileForSkylark(
"def func():\n"
+ " d = {'a' : 1}\n"
+ " d['b'] = 2\n"
+ " return d\n"
+ "d = func()"), env);
assertEquals(ImmutableMap.of("a", 1, "b", 2), env.lookup("d"));
}
public void testDictAssignmentAsLValueNoSideEffects() throws Exception {
MethodLibrary.setupMethodEnvironment(env);
exec(parseFileForSkylark(
"def func(d):\n"
+ " d['b'] = 2\n"
+ "d = {'a' : 1}\n"
+ "func(d)"), env);
assertEquals(ImmutableMap.of("a", 1), env.lookup("d"));
}
public void testListIndexAsLValueAsLValue() throws Exception {
checkEvalError(parseFileForSkylark(
"def id(l):\n"
+ " return l\n"
+ "def func():\n"
+ " l = id([1])\n"
+ " l[0] = 2\n"
+ " return l\n"
+ "l = func()"), env, "unsupported operand type(s) for +: 'list' and 'dict'");
}
public void testTopLevelDict() throws Exception {
exec(parseFileForSkylark(
"if 1:\n"
+ " v = 'a'\n"
+ "else:\n"
+ " v = 'b'"), env);
assertEquals("a", env.lookup("v"));
}
public void testUserFunctionKeywordArgs() throws Exception {
exec(parseFileForSkylark(
"def foo(a, b, c):\n"
+ " return a + b + c\n"
+ "s = foo(1, c=2, b=3)"), env);
assertEquals(6, env.lookup("s"));
}
public void testNoneTrueFalseInSkylark() throws Exception {
exec(parseFileForSkylark(
"a = None\n"
+ "b = True\n"
+ "c = False"), env);
assertSame(Environment.NONE, env.lookup("a"));
assertTrue((Boolean) env.lookup("b"));
assertFalse((Boolean) env.lookup("c"));
}
public void testHasattr() throws Exception {
exec(parseFileForSkylark(
"s = struct(a=1)\n"
+ "x = hasattr(s, 'a')\n"
+ "y = hasattr(s, 'b')\n"), env);
assertTrue((Boolean) env.lookup("x"));
assertFalse((Boolean) env.lookup("y"));
}
public void testHasattrMethods() throws Exception {
env.update("mock", new Mock());
ValidationEnvironment validEnv = SkylarkModules.getValidationEnvironment();
validEnv.update("mock", SkylarkType.of(Mock.class), null);
exec(Parser.parseFileForSkylark(createLexer(
"a = hasattr(mock, 'struct_field')\n"
+ "b = hasattr(mock, 'function')\n"
+ "c = hasattr(mock, 'is_empty')\n"
+ "d = hasattr('str', 'replace')\n"
+ "e = hasattr(mock, 'other')\n"),
syntaxEvents.reporter(), null, validEnv).statements, env);
assertTrue((Boolean) env.lookup("a"));
assertTrue((Boolean) env.lookup("b"));
assertTrue((Boolean) env.lookup("c"));
assertTrue((Boolean) env.lookup("d"));
assertFalse((Boolean) env.lookup("e"));
}
public void testGetattr() throws Exception {
exec(parseFileForSkylark(
"s = struct(a='val')\n"
+ "x = getattr(s, 'a')\n"
+ "y = getattr(s, 'b', 'def')\n"
+ "z = getattr(s, 'b', default = 'def')\n"
+ "w = getattr(s, 'a', default='ignored')"), env);
assertEquals("val", env.lookup("x"));
assertEquals("def", env.lookup("y"));
assertEquals("def", env.lookup("z"));
assertEquals("val", env.lookup("w"));
}
public void testGetattrNoAttr() throws Exception {
checkEvalError(parseFileForSkylark(
"s = struct(a='val')\n"
+ "getattr(s, 'b')"),
env, "Object of type 'struct' has no field 'b'");
}
@SuppressWarnings("unchecked")
public void testListAnTupleConcatenationDoesNotWorkInSkylark() throws Exception {
checkEvalError(parseFileForSkylark("[1, 2] + (3, 4)"), env,
"cannot concatenate lists and tuples");
}
public void testCannotCreateMixedListInSkylark() throws Exception {
env.update("mock", new Mock());
checkEvalError(parseFileForSkylark("[mock.string(), 1, 2]", MOCK_TYPES), env,
"Incompatible types in list: found a int but the previous elements were strings");
}
public void testCannotConcatListInSkylarkWithDifferentGenericTypes() throws Exception {
env.update("mock", new Mock());
checkEvalError(parseFileForSkylark("mock.string_list() + [1, 2]", MOCK_TYPES), env,
"cannot concatenate list of string with list of int");
}
@SuppressWarnings("unchecked")
public void testConcatEmptyListWithNonEmptyWorks() throws Exception {
exec(parseFileForSkylark("l = [] + ['a', 'b']", MOCK_TYPES), env);
assertThat((Iterable<Object>) env.lookup("l")).containsExactly("a", "b").inOrder();
}
public void testFormatStringWithTuple() throws Exception {
exec(parseFileForSkylark("v = '%s%s' % ('a', 1)"), env);
assertEquals("a1", env.lookup("v"));
}
@SuppressWarnings("unchecked")
public void testDirFindsClassObjectFields() throws Exception {
env.update("mock", new MockClassObject());
exec(parseFileForSkylark("v = dir(mock)", MOCK_TYPES), env);
assertThat((Iterable<String>) env.lookup("v")).containsExactly("field", "nset").inOrder();
}
@SuppressWarnings("unchecked")
public void testDirFindsJavaObjectStructFieldsAndMethods() throws Exception {
env.update("mock", new Mock());
exec(parseFileForSkylark("v = dir(mock)", MOCK_TYPES), env);
assertThat((Iterable<String>) env.lookup("v")).containsExactly("function", "is_empty",
"nullfunc_failing", "nullfunc_working", "return_mutable", "string", "string_list",
"struct_field", "value_of", "voidfunc").inOrder();
}
public void testPrint() throws Exception {
exec(parseFileForSkylark("print('hello')"), env);
syntaxEvents.assertContainsEvent("hello");
exec(parseFileForSkylark("print('a', 'b')"), env);
syntaxEvents.assertContainsEvent("a b");
exec(parseFileForSkylark("print('a', 'b', sep='x')"), env);
syntaxEvents.assertContainsEvent("axb");
}
public void testPrintBadKwargs() throws Exception {
checkEvalError("print(end='x', other='y')", "unexpected keywords: '[end, other]'");
}
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
public void testConcatLists() throws Exception {
// list
Object x = eval("[1,2] + [3,4]");
assertThat((Iterable<Object>) x).containsExactly(1, 2, 3, 4).inOrder();
assertFalse(((SkylarkList) x).isTuple());
// 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());
}
@SuppressWarnings("unchecked")
@Override
public void testListExprs() throws Exception {
assertThat((Iterable<Object>) eval("[1, 2, 3]")).containsExactly(1, 2, 3).inOrder();
assertThat((Iterable<Object>) eval("(1, 2, 3)")).containsExactly(1, 2, 3).inOrder();
}
@Override
public void testListConcatenation() throws Exception {}
@Override
public void testKeywordArgs() {}
}