MethodLibraryTest: Implemented an alternative approach that reduces duplicated code and may lead to cleaner tests. -- MOS_MIGRATED_REVID=97326780
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java index b764413..9b0ce0b 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
@@ -20,8 +20,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.devtools.build.lib.packages.PackageFactory; -import com.google.devtools.build.lib.testutil.TestRuleClassProvider; +import com.google.devtools.build.lib.testutil.TestMode; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,151 +36,161 @@ */ @RunWith(JUnit4.class) public class EvaluationTest extends EvaluationTestCase { + public EvaluationTest() throws Exception { + super.setMode(TestMode.BUILD); + } - @Override - public EvaluationContext newEvaluationContext() { - return EvaluationContext.newBuildContext(getEventHandler(), - new PackageFactory(TestRuleClassProvider.getRuleClassProvider()).getEnvironment()); + /** + * Creates a new instance of {@code ModalTestCase}. + * + * <p>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() { + return new BuildTest(); + } @Test public void testExprs() throws Exception { - assertEquals("fooxbar", eval("'%sx' % 'foo' + 'bar'")); - assertEquals("fooxbar", eval("('%sx' % 'foo') + 'bar'")); - assertEquals("foobarx", eval("'%sx' % ('foo' + 'bar')")); - assertEquals(579, eval("123 + 456")); - assertEquals(333, eval("456 - 123")); - assertEquals(2, eval("8 % 3")); - - checkEvalErrorContains("unsupported operand type(s) for %: 'int' and 'string'", "3 % 'foo'"); + newTest() + .testStatement("'%sx' % 'foo' + 'bar'", "fooxbar") + .testStatement("('%sx' % 'foo') + 'bar'", "fooxbar") + .testStatement("'%sx' % ('foo' + 'bar')", "foobarx") + .testStatement("123 + 456", 579) + .testStatement("456 - 123", 333) + .testStatement("8 % 3", 2) + .testIfErrorContains("unsupported operand type(s) for %: 'int' and 'string'", "3 % 'foo'"); } - @SuppressWarnings("unchecked") @Test 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(); + newTest().testExactOrder("[1, 2, 3]", 1, 2, 3).testExactOrder("(1, 2, 3)", 1, 2, 3); } @Test public void testStringFormatMultipleArgs() throws Exception { - assertEquals("XYZ", eval("'%sY%s' % ('X', 'Z')")); + newTest().testStatement("'%sY%s' % ('X', 'Z')", "XYZ"); } @Test public void testAndOr() throws Exception { - assertEquals(8, eval("8 or 9")); - assertEquals(9, eval("0 or 9")); - assertEquals(9, eval("8 and 9")); - assertEquals(0, eval("0 and 9")); + new BuildTest() + .testStatement("8 or 9", 8) + .testStatement("0 or 9", 9) + .testStatement("8 and 9", 9) + .testStatement("0 and 9", 0) - assertEquals(2, eval("1 and 2 or 3")); - assertEquals(3, eval("0 and 2 or 3")); - assertEquals(3, eval("1 and 0 or 3")); + .testStatement("1 and 2 or 3", 2) + .testStatement("0 and 2 or 3", 3) + .testStatement("1 and 0 or 3", 3) - assertEquals(1, eval("1 or 2 and 3")); - assertEquals(3, eval("0 or 2 and 3")); - assertEquals(0, eval("0 or 0 and 3")); - assertEquals(1, eval("1 or 0 and 3")); - assertEquals(1, eval("1 or 0 and 3")); + .testStatement("1 or 2 and 3", 1) + .testStatement("0 or 2 and 3", 3) + .testStatement("0 or 0 and 3", 0) + .testStatement("1 or 0 and 3", 1) - assertEquals(Environment.NONE, eval("None and 1")); - assertEquals(9, eval("\"\" or 9")); - assertEquals("abc", eval("\"abc\" or 9")); + .testStatement("None and 1", Environment.NONE) + .testStatement("\"\" or 9", 9) + .testStatement("\"abc\" or 9", "abc") + + // check that 'foo' is not evaluated + .testStatement("8 or foo", 8) + .testStatement("0 and foo", 0); - if (isSkylark()) { - checkEvalError("ERROR 1:6: name 'foo' is not defined", "8 or foo"); - checkEvalError("ERROR 1:7: name 'foo' is not defined", "0 and foo"); - } else { - assertEquals(8, eval("8 or foo")); // check that 'foo' is not evaluated - assertEquals(0, eval("0 and foo")); // check that 'foo' is not evaluated - } + new SkylarkTest() + .testIfErrorContains("name 'google' is not defined", "0 and google") + .testIfErrorContains("name 'google' is not defined", "8 or google"); } @Test public void testNot() throws Exception { - assertEquals(false, eval("not 1")); - assertEquals(true, eval("not ''")); + newTest().testStatement("not 1", false).testStatement("not ''", true); } @Test public void testNotWithLogicOperators() throws Exception { - assertEquals(true, eval("not (0 and 0)")); - assertEquals(false, eval("not (1 or 0)")); + newTest() + .testStatement("not (0 and 0)", true) + .testStatement("not (1 or 0)", false) - assertEquals(0, eval("0 and not 0")); - assertEquals(0, eval("not 0 and 0")); + .testStatement("0 and not 0", 0) + .testStatement("not 0 and 0", 0) - assertEquals(true, eval("1 and not 0")); - assertEquals(true, eval("not 0 or 0")); + .testStatement("1 and not 0", true) + .testStatement("not 0 or 0", true) - assertEquals(0, eval("not 1 or 0")); - assertEquals(1, eval("not 1 or 1")); + .testStatement("not 1 or 0", 0) + .testStatement("not 1 or 1", 1); } @Test public void testNotWithArithmeticOperators() throws Exception { - assertEquals(true, eval("not 0 + 0")); - assertEquals(false, eval("not 2 - 1")); + newTest().testStatement("not 0 + 0", true).testStatement("not 2 - 1", false); } @Test public void testNotWithCollections() throws Exception { - assertEquals(true, eval("not []")); - assertEquals(false, eval("not {'a' : 1}")); + newTest().testStatement("not []", true).testStatement("not {'a' : 1}", false); } @Test public void testEquality() throws Exception { - assertEquals(true, eval("1 == 1")); - assertEquals(false, eval("1 == 2")); - assertEquals(true, eval("'hello' == 'hel' + 'lo'")); - assertEquals(false, eval("'hello' == 'bye'")); - assertEquals(true, eval("None == None")); - assertEquals(true, eval("[1, 2] == [1, 2]")); - assertEquals(false, eval("[1, 2] == [2, 1]")); + newTest() + .testStatement("1 == 1", true) + .testStatement("1 == 2", false) + .testStatement("'hello' == 'hel' + 'lo'", true) + .testStatement("'hello' == 'bye'", false) + .testStatement("None == None", true) + .testStatement("[1, 2] == [1, 2]", true) + .testStatement("[1, 2] == [2, 1]", false); } @Test public void testInequality() throws Exception { - assertEquals(false, eval("1 != 1")); - assertEquals(true, eval("1 != 2")); - assertEquals(false, eval("'hello' != 'hel' + 'lo'")); - assertEquals(true, eval("'hello' != 'bye'")); - assertEquals(false, eval("[1, 2] != [1, 2]")); - assertEquals(true, eval("[1, 2] != [2, 1]")); + newTest() + .testStatement("1 != 1", false) + .testStatement("1 != 2", true) + .testStatement("'hello' != 'hel' + 'lo'", false) + .testStatement("'hello' != 'bye'", true) + .testStatement("[1, 2] != [1, 2]", false) + .testStatement("[1, 2] != [2, 1]", true); } @Test public void testEqualityPrecedence() throws Exception { - assertEquals(true, eval("1 + 3 == 2 + 2")); - assertEquals(true, eval("not 1 == 2")); - assertEquals(false, eval("not 1 != 2")); - assertEquals(true, eval("2 and 3 == 3 or 1")); - assertEquals(2, eval("2 or 3 == 3 and 1")); + newTest() + .testStatement("1 + 3 == 2 + 2", true) + .testStatement("not 1 == 2", true) + .testStatement("not 1 != 2", false) + .testStatement("2 and 3 == 3 or 1", true) + .testStatement("2 or 3 == 3 and 1", 2); } @Test public void testLessThan() throws Exception { - assertEquals(true, eval("1 <= 1")); - assertEquals(false, eval("1 < 1")); - assertEquals(true, eval("'a' <= 'b'")); - assertEquals(false, eval("'c' < 'a'")); + newTest() + .testStatement("1 <= 1", true) + .testStatement("1 < 1", false) + .testStatement("'a' <= 'b'", true) + .testStatement("'c' < 'a'", false); } @Test public void testGreaterThan() throws Exception { - assertEquals(true, eval("1 >= 1")); - assertEquals(false, eval("1 > 1")); - assertEquals(false, eval("'a' >= 'b'")); - assertEquals(true, eval("'c' > 'a'")); + newTest() + .testStatement("1 >= 1", true) + .testStatement("1 > 1", false) + .testStatement("'a' >= 'b'", false) + .testStatement("'c' > 'a'", true); } @Test public void testConditionalExpressions() throws Exception { - assertEquals(1, eval("1 if True else 2")); - assertEquals(2, eval("1 if False else 2")); - assertEquals(3, eval("1 + 2 if 3 + 4 else 5 + 6")); + 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"); @@ -191,32 +200,33 @@ @Test public void testListComparison() throws Exception { - assertThat(eval("[] < [1]")).isEqualTo(true); - assertThat(eval("[1] < [1, 1]")).isEqualTo(true); - assertThat(eval("[1, 1] < [1, 2]")).isEqualTo(true); - assertThat(eval("[1, 2] < [1, 2, 3]")).isEqualTo(true); - assertThat(eval("[1, 2, 3] <= [1, 2, 3]")).isEqualTo(true); + 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) - assertThat(eval("['a', 'b'] > ['a']")).isEqualTo(true); - assertThat(eval("['a', 'b'] >= ['a']")).isEqualTo(true); - assertThat(eval("['a', 'b'] < ['a']")).isEqualTo(false); - assertThat(eval("['a', 'b'] <= ['a']")).isEqualTo(false); + .testStatement("['a', 'b'] > ['a']", true) + .testStatement("['a', 'b'] >= ['a']", true) + .testStatement("['a', 'b'] < ['a']", false) + .testStatement("['a', 'b'] <= ['a']", false) - assertThat(eval("('a', 'b') > ('a', 'b')")).isEqualTo(false); - assertThat(eval("('a', 'b') >= ('a', 'b')")).isEqualTo(true); - assertThat(eval("('a', 'b') < ('a', 'b')")).isEqualTo(false); - assertThat(eval("('a', 'b') <= ('a', 'b')")).isEqualTo(true); + .testStatement("('a', 'b') > ('a', 'b')", false) + .testStatement("('a', 'b') >= ('a', 'b')", true) + .testStatement("('a', 'b') < ('a', 'b')", false) + .testStatement("('a', 'b') <= ('a', 'b')", true) - assertThat(eval("[[1, 1]] > [[1, 1], []]")).isEqualTo(false); - assertThat(eval("[[1, 1]] < [[1, 1], []]")).isEqualTo(true); + .testStatement("[[1, 1]] > [[1, 1], []]", false) + .testStatement("[[1, 1]] < [[1, 1], []]", true) - checkEvalError("Cannot compare int with string", "[1] < ['a']"); - checkEvalError("Cannot compare list with int", "[1] < 1"); + .testIfExactError("Cannot compare int with string", "[1] < ['a']") + .testIfExactError("Cannot compare list with int", "[1] < 1"); } @Test public void testCompareStringInt() throws Exception { - checkEvalError("Cannot compare string with int", "'a' >= 1"); + newTest().testIfExactError("Cannot compare string with int", "'a' >= 1"); } @Test @@ -233,18 +243,15 @@ } }; - update(sum.getName(), sum); - assertEquals(21, eval("sum(1, 2, 3, 4, 5, 6)")); - assertEquals(sum, eval("sum")); - assertEquals(0, eval("sum(a=1, b=2)")); + 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 { - eval("sum = 123456"); - assertEquals(123456, lookup("sum")); - checkEvalError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)"); - assertEquals(123456, eval("sum")); + newTest().setUp("sum = 123456").testLookup("sum", 123456) + .testIfExactError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)") + .testStatement("sum", 123456); } @Test @@ -261,59 +268,64 @@ } }; - update(kwargs.getName(), kwargs); - - assertEquals(eval("[('bar', 'bar'), ('foo', 1), ('wiz', [1, 2, 3])]"), - eval("kwargs(foo=1, bar='bar', wiz=[1,2,3]).items()")); + newTest() + .update(kwargs.getName(), kwargs) + .testEval( + "kwargs(foo=1, bar='bar', wiz=[1,2,3]).items()", + "[('bar', 'bar'), ('foo', 1), ('wiz', [1, 2, 3])]"); } @Test public void testModulo() throws Exception { - assertThat(eval("6 % 2")).isEqualTo(0); - assertThat(eval("6 % 4")).isEqualTo(2); - assertThat(eval("3 % 6")).isEqualTo(3); - assertThat(eval("7 % -4")).isEqualTo(-1); - assertThat(eval("-7 % 4")).isEqualTo(1); - assertThat(eval("-7 % -4")).isEqualTo(-3); - checkEvalError("integer modulo by zero", "5 % 0"); + 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 { - assertEquals(42, eval("6 * 7")); - - assertEquals("ababab", eval("3 * 'ab'")); - assertEquals("", eval("0 * 'ab'")); - assertEquals("100000", eval("'1' + '0' * 5")); + newTest() + .testStatement("6 * 7", 42) + .testStatement("3 * 'ab'", "ababab") + .testStatement("0 * 'ab'", "") + .testStatement("'1' + '0' * 5", "100000"); } @Test public void testDivision() throws Exception { - assertThat(eval("6 / 2")).isEqualTo(3); - assertThat(eval("6 / 4")).isEqualTo(1); - assertThat(eval("3 / 6")).isEqualTo(0); - assertThat(eval("7 / -2")).isEqualTo(-4); - assertThat(eval("-7 / 2")).isEqualTo(-4); - assertThat(eval("-7 / -2")).isEqualTo(3); - assertThat(eval("2147483647 / 2")).isEqualTo(1073741823); - checkEvalError("integer division by zero", "5 / 0"); + 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) + .testIfExactError("integer division by zero", "5 / 0"); } @Test public void testOperatorPrecedence() throws Exception { - assertThat(eval("2 + 3 * 4")).isEqualTo(14); - assertThat(eval("2 + 3 / 4")).isEqualTo(2); - assertThat(eval("2 * 3 + 4 / -2")).isEqualTo(4); + newTest() + .testStatement("2 + 3 * 4", 14) + .testStatement("2 + 3 / 4", 2) + .testStatement("2 * 3 + 4 / -2", 4); } @Test public void testConcatStrings() throws Exception { - assertEquals("foobar", eval("'foo' + 'bar'")); + 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<Object>) x).containsExactly(1, 2, 3, 4).inOrder(); @@ -329,160 +341,150 @@ "(1,2) + [3,4]"); // list + tuple } - @SuppressWarnings("unchecked") @Test public void testListComprehensions() throws Exception { - assertThat((Iterable<Object>) eval("['foo/%s.java' % x for x in []]")).isEmpty(); - - assertThat((Iterable<Object>) eval("['foo/%s.java' % y for y in ['bar', 'wiz', 'quux']]")) - .containsExactly("foo/bar.java", "foo/wiz.java", "foo/quux.java").inOrder(); - - assertThat((Iterable<Object>) eval( - "['%s/%s.java' % (z, t) " - + "for z in ['foo', 'bar'] " - + "for t in ['baz', 'wiz', 'quux']]")) - .containsExactly("foo/baz.java", "foo/wiz.java", "foo/quux.java", - "bar/baz.java", "bar/wiz.java", "bar/quux.java").inOrder(); - - assertThat((Iterable<Object>) eval( - "['%s/%s.java' % (b, b) " - + "for a in ['foo', 'bar'] " - + "for b in ['baz', 'wiz', 'quux']]")) - .containsExactly("baz/baz.java", "wiz/wiz.java", "quux/quux.java", - "baz/baz.java", "wiz/wiz.java", "quux/quux.java").inOrder(); - - assertThat((Iterable<Object>) eval( - "['%s/%s.%s' % (c, d, e) " - + "for c in ['foo', 'bar'] " - + "for d in ['baz', 'wiz', 'quux'] " - + "for e in ['java', 'cc']]")) - .containsExactly("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").inOrder(); + 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"); } @Test public void testNestedListComprehensions() throws Exception { - assertThat((Iterable<?>) eval( - "li = [[1, 2], [3, 4]]\n" - + "[j for i in li for j in i]")) - .containsExactly(1, 2, 3, 4).inOrder(); - - assertThat((Iterable<?>) eval( - "input = [['abc'], ['def', 'ghi']]\n" - + "['%s %s' % (b, c) for a in input for b in a for c in b]")) - .containsExactly( - "abc a", "abc b", "abc c", "def d", "def e", "def f", "ghi g", "ghi h", "ghi i") - .inOrder(); + 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 { - assertThat(eval("[x + y for x, y in [(1, 2), (3, 4)]]").toString()) - .isEqualTo("[3, 7]"); - assertThat(eval("[z + t for (z, t) in [[1, 2], [3, 4]]]").toString()) - .isEqualTo("[3, 7]"); + 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 { - checkEvalError("lvalue has length 3, but rvalue has has length 2", - "[x + y for x, y, z in [(1, 2), (3, 4)]]"); - - checkEvalError("type 'int' is not a collection", - "[x + y for x, y in (1, 2)]"); + newTest().testIfExactError("lvalue has length 3, but rvalue has has 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 { - eval("range3 = [0, 1, 2]"); // used below - - assertThat(eval("[a for a in (4, None, 2, None, 1) if a != None]").toString()) - .isEqualTo("[4, 2, 1]"); - assertThat(eval("[b+c for b in [0, 1, 2] for c in [0, 1, 2] if b + c > 2]").toString()) - .isEqualTo("[3, 3, 4]"); - assertThat(eval("[d+e for d in range3 if d % 2 == 1 for e in range3]").toString()) - .isEqualTo("[1, 2, 3]"); - assertThat(eval( - "[[f,g] for f in [0, 1, 2, 3, 4] if f for g in [5, 6, 7, 8] if f * g % 12 == 0 ]") - .toString()).isEqualTo("[[2, 6], [3, 8], [4, 6]]"); - assertThat(eval("[h for h in [4, 2, 0, 1] if h]").toString()) - .isEqualTo("[4, 2, 1]"); + 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 { - checkEvalErrorContains("name 'y' is not defined", + 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 { - eval("a, b = 1, 2"); - assertThat(lookup("a")).isEqualTo(1); - assertThat(lookup("b")).isEqualTo(2); - - eval("c, d = {'key1':2, 'key2':3}"); - assertThat(lookup("c")).isEqualTo("key1"); - assertThat(lookup("d")).isEqualTo("key2"); + 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 testHeterogeneousDict() throws Exception { - eval("d = {'str': 1, 2: 3}\n" - + "a = d['str']\n" - + "b = d[2]"); - assertThat(lookup("a")).isEqualTo(1); - assertThat(lookup("b")).isEqualTo(3); + newTest().setUp("d = {'str': 1, 2: 3}", "a = d['str']", "b = d[2]").testLookup("a", 1) + .testLookup("b", 3); } @Test public void testRecursiveTupleDestructuring() throws Exception { - eval("((a, b), (c, d)) = [(1, 2), (3, 4)]"); - assertThat(lookup("a")).isEqualTo(1); - assertThat(lookup("b")).isEqualTo(2); - assertThat(lookup("c")).isEqualTo(3); - assertThat(lookup("d")).isEqualTo(4); + newTest() + .setUp("((a, b), (c, d)) = [(1, 2), (3, 4)]") + .testLookup("a", 1) + .testLookup("b", 2) + .testLookup("c", 3) + .testLookup("d", 4); } - @SuppressWarnings("unchecked") @Test public void testListComprehensionModifiesGlobalEnv() throws Exception { - update("x", 42); - if (isSkylark()) { - checkEvalError("ERROR 1:1: Variable x is read only", "[x + 1 for x in [1,2,3]]"); - } else { - assertThat((Iterable<Object>) eval("[x + 1 for x in [1,2,3]]")) - .containsExactly(2, 3, 4).inOrder(); - assertEquals(3, lookup("x")); // (x is global) - } + new SkylarkTest().update("x", 42).testIfExactError("ERROR 1:1: 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 { - assertEquals(Collections.emptyMap(), eval("{a : a for a in []}")); - assertEquals(ImmutableMap.of(1, 1, 2, 2), eval("{b : b for b in [1, 2]}")); - assertEquals(ImmutableMap.of("a", "v_a", "b", "v_b"), - eval("{c : 'v_' + c for c in ['a', 'b']}")); - assertEquals(ImmutableMap.of("k_a", "a", "k_b", "b"), - eval("{'k_' + d : d for d in ['a', 'b']}")); - assertEquals(ImmutableMap.of("k_a", "v_a", "k_b", "v_b"), - eval("{'k_' + e : 'v_' + e for e in ['a', 'b']}")); - assertEquals(ImmutableMap.of(5, 6), eval("{x+y : x*y for x, y in [[2, 3]]}")); + 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 { - checkEvalError( - "type 'int' is not iterable", - "{k : k for k in 3}"); + newTest().testIfExactError("type 'int' is not iterable", "{k : k for k in 3}"); } @Test public void testDictComprehensions_MultipleKey() throws Exception { - assertEquals(ImmutableMap.of(1, 1, 2, 2), eval("{x : x for x in [1, 2, 1]}")); - assertEquals(ImmutableMap.of("ab", "ab", "c", "c"), - eval("{y : y for y in ['ab', 'c', 'a' + 'b']}")); + 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 @@ -495,92 +497,92 @@ @Test public void testListConcatenation() throws Exception { - assertEquals(Arrays.asList(1, 2, 3, 4), eval("[1, 2] + [3, 4]")); - assertEquals(ImmutableList.of(1, 2, 3, 4), eval("(1, 2) + (3, 4)")); - checkEvalError("can only concatenate Tuple (not \"List\") to Tuple", - "[1, 2] + (3, 4)"); - checkEvalError("can only concatenate List (not \"Tuple\") to List", - "(1, 2) + [3, 4]"); + newTest() + .testStatement("[1, 2] + [3, 4]", Arrays.asList(1, 2, 3, 4)) + .testStatement("(1, 2) + (3, 4)", ImmutableList.of(1, 2, 3, 4)) + .testIfExactError("can only concatenate Tuple (not \"List\") to Tuple", "[1, 2] + (3, 4)") + .testIfExactError("can only concatenate List (not \"Tuple\") to List", "(1, 2) + [3, 4]"); } @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<Object> elements = x.getElements(); assertThat(elements.size()).isEqualTo(2); assertThat(elements.get(0)).isInstanceOf(SelectorValue.class); - assertThat((Iterable) elements.get(1)).isEmpty(); + assertThat((Iterable<Object>) elements.get(1)).isEmpty(); } @Test public void testListComprehensionFailsOnNonSequence() throws Exception { - checkEvalErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]"); + newTest().testIfErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]"); } - @SuppressWarnings("unchecked") @Test public void testListComprehensionOnString() throws Exception { - assertThat((Iterable<Object>) eval("[x for x in 'abc']")) - .containsExactly("a", "b", "c").inOrder(); + newTest().testExactOrder("[x for x in 'abc']", "a", "b", "c"); } @Test public void testInvalidAssignment() throws Exception { - update("x", 1); - checkEvalErrorContains("can only assign to variables and tuples, not to 'x + 1'", - "x + 1 = 2"); + newTest().testIfErrorContains( + "can only assign to variables and tuples, not to 'x + 1'", "x + 1 = 2"); } - @SuppressWarnings("unchecked") @Test public void testListComprehensionOnDictionary() throws Exception { - assertThat((Iterable<Object>) eval("val = ['var_' + n for n in {'a':1,'b':2}] ; val")) - .containsExactly("var_a", "var_b").inOrder(); + newTest().testExactOrder("val = ['var_' + n for n in {'a':1,'b':2}] ; val", "var_a", "var_b"); } @Test public void testListComprehensionOnDictionaryCompositeExpression() throws Exception { - eval("d = {1:'a',2:'b'}\n" + "l = [d[x] for x in d]"); - assertEquals("[\"a\", \"b\"]", Printer.repr(lookup("l"))); + new BuildTest() + .setUp("d = {1:'a',2:'b'}", "l = [d[x] for x in d]") + .testLookup("l", ImmutableList.of("a", "b")); } @Test public void testInOperator() throws Exception { - assertEquals(Boolean.TRUE, eval("'b' in ['a', 'b']")); - assertEquals(Boolean.FALSE, eval("'c' in ['a', 'b']")); - assertEquals(Boolean.TRUE, eval("'b' in ('a', 'b')")); - assertEquals(Boolean.FALSE, eval("'c' in ('a', 'b')")); - assertEquals(Boolean.TRUE, eval("'b' in {'a' : 1, 'b' : 2}")); - assertEquals(Boolean.FALSE, eval("'c' in {'a' : 1, 'b' : 2}")); - assertEquals(Boolean.FALSE, eval("1 in {'a' : 1, 'b' : 2}")); - assertEquals(Boolean.TRUE, eval("'b' in 'abc'")); - assertEquals(Boolean.FALSE, eval("'d' in 'abc'")); + 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 { - assertEquals(Boolean.FALSE, eval("'b' not in ['a', 'b']")); - assertEquals(Boolean.TRUE, eval("'c' not in ['a', 'b']")); - assertEquals(Boolean.FALSE, eval("'b' not in ('a', 'b')")); - assertEquals(Boolean.TRUE, eval("'c' not in ('a', 'b')")); - assertEquals(Boolean.FALSE, eval("'b' not in {'a' : 1, 'b' : 2}")); - assertEquals(Boolean.TRUE, eval("'c' not in {'a' : 1, 'b' : 2}")); - assertEquals(Boolean.TRUE, eval("1 not in {'a' : 1, 'b' : 2}")); - assertEquals(Boolean.FALSE, eval("'b' not in 'abc'")); - assertEquals(Boolean.TRUE, eval("'d' not in 'abc'")); + 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 { - 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"); + newTest() + .testIfExactError( + "in operator only works on strings if the left operand is also a string", "1 in '123'") + .testIfExactError( + "in operator only works on lists, tuples, sets, dicts and strings", "'a' in 1"); } @Test public void testInCompositeForPrecedence() throws Exception { - assertEquals(0, eval("not 'a' in ['a'] or 0")); + newTest().testStatement("not 'a' in ['a'] or 0", 0); } private Object createObjWithStr() { @@ -594,50 +596,48 @@ @Test public void testPercOnObject() throws Exception { - update("obj", createObjWithStr()); - assertEquals("str marker", eval("'%s' % obj")); + newTest().update("obj", createObjWithStr()).testStatement("'%s' % obj", "str marker"); } @Test public void testPercOnObjectList() throws Exception { - update("obj", createObjWithStr()); - assertEquals("str marker str marker", eval("'%s %s' % (obj, obj)")); + newTest().update("obj", createObjWithStr()).testStatement("'%s %s' % (obj, obj)", + "str marker str marker"); } @Test public void testPercOnObjectInvalidFormat() throws Exception { - update("obj", createObjWithStr()); - checkEvalError("invalid argument str marker for format pattern %d", "'%d' % obj"); + newTest().update("obj", createObjWithStr()).testIfExactError( + "invalid argument str marker for format pattern %d", "'%d' % obj"); } - @SuppressWarnings("unchecked") @Test public void testDictKeys() throws Exception { - assertThat((Iterable<Object>) eval("v = {'a': 1}.keys() + ['b', 'c'] ; v")) - .containsExactly("a", "b", "c").inOrder(); + newTest().testExactOrder("v = {'a': 1}.keys() + ['b', 'c'] ; v", "a", "b", "c"); } @Test public void testDictKeysTooManyArgs() throws Exception { - checkEvalError("too many (2) positional arguments in call to keys(self: dict)", - "{'a': 1}.keys('abc')"); + newTest().testIfExactError( + "too many (2) positional arguments in call to keys(self: dict)", "{'a': 1}.keys('abc')"); } @Test public void testDictKeysTooManyKeyArgs() throws Exception { - checkEvalError("unexpected keyword 'arg' in call to keys(self: dict)", + newTest().testIfExactError("unexpected keyword 'arg' in call to keys(self: dict)", "{'a': 1}.keys(arg='abc')"); } @Test public void testDictKeysDuplicateKeyArgs() throws Exception { - checkEvalError("duplicate keywords 'arg', 'k' in call to keys", + 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 { - checkEvalErrorStartsWith("arguments 'old', 'new' passed both by position and by name " + 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)"); }
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java index 5749c40..692a338 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
@@ -16,43 +16,89 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import com.google.common.truth.Ordered; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventCollector; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.EventKind; import com.google.devtools.build.lib.events.util.EventCollectionApparatus; -import com.google.devtools.build.lib.rules.SkylarkModules; +import com.google.devtools.build.lib.packages.PackageFactory; +import com.google.devtools.build.lib.testutil.TestMode; +import com.google.devtools.build.lib.testutil.TestRuleClassProvider; import org.junit.Before; +import java.util.LinkedList; import java.util.List; /** * Base class for test cases that use parsing and evaluation services. */ public class EvaluationTestCase { - + private EventCollectionApparatus eventCollectionApparatus; + private PackageFactory factory; + private TestMode testMode = TestMode.SKYLARK; + protected EvaluationContext evaluationContext; + public EvaluationTestCase() { + createNewInfrastructure(); + } + @Before public void setUp() throws Exception { - eventCollectionApparatus = new EventCollectionApparatus(EventKind.ALL_EVENTS); + createNewInfrastructure(); evaluationContext = newEvaluationContext(); } + public EvaluationContext newEvaluationContext() throws Exception { + if (testMode == null) { + throw new IllegalArgumentException( + "TestMode is null. Please set a Testmode via setMode() or set the " + + "evaluatenContext manually by overriding newEvaluationContext()"); + } + + return testMode.createContext(getEventHandler(), factory.getEnvironment()); + } + + protected void createNewInfrastructure() { + eventCollectionApparatus = new EventCollectionApparatus(EventKind.ALL_EVENTS); + factory = new PackageFactory(TestRuleClassProvider.getRuleClassProvider()); + } + + /** + * Sets the specified {@code TestMode} and tries to create the appropriate {@code + * EvaluationContext} + * + * @param testMode + * @throws Exception + */ + protected void setMode(TestMode testMode) throws Exception { + this.testMode = testMode; + evaluationContext = newEvaluationContext(); + } + + protected void enableSkylarkMode() throws Exception { + setMode(TestMode.SKYLARK); + } + + protected void enableBuildMode() throws Exception { + setMode(TestMode.BUILD); + } + protected EventHandler getEventHandler() { return eventCollectionApparatus.reporter(); } + protected PackageFactory getFactory() { + return factory; + } + public Environment getEnvironment() { return evaluationContext.getEnvironment(); } - - public EvaluationContext newEvaluationContext() throws Exception { - return SkylarkModules.newEvaluationContext(getEventHandler()); - } - + public boolean isSkylark() { return evaluationContext.isSkylark(); } @@ -133,4 +179,356 @@ eventCollectionApparatus.collector().clear(); return this; } + + /** + * Encapsulates a separate test which can be executed by a {@code TestMode} + */ + protected interface Testable { + public void run() throws Exception; + } + + /** + * Base class for test cases that run in specific modes (e.g. Build and/or Skylark) + * + */ + protected abstract class ModalTestCase { + private final SetupActions setup; + + protected ModalTestCase() { + setup = new SetupActions(); + } + + /** + * Allows the execution of several statements before each following test + * @param statements The statement(s) to be executed + * @return This {@code ModalTestCase} + */ + public ModalTestCase setUp(String... statements) { + setup.registerEval(statements); + return this; + } + + /** + * Allows the update of the specified variable before each following test + * @param name The name of the variable that should be updated + * @param value The new value of the variable + * @return This {@code ModalTestCase} + */ + public ModalTestCase update(String name, Object value) { + setup.registerUpdate(name, value); + return this; + } + + /** + * Evaluates two parameters and compares their results. + * @param statement The statement to be evaluated + * @param expectedEvalString The expression of the expected result + * @return This {@code ModalTestCase} + * @throws Exception + */ + public ModalTestCase testEval(String statement, String expectedEvalString) throws Exception { + runTest(createComparisonTestable(statement, expectedEvalString, true)); + return this; + } + + /** + * Evaluates the given statement and compares its result to the expected object + * @param statement + * @param expected + * @return This {@code ModalTestCase} + * @throws Exception + */ + public ModalTestCase testStatement(String statement, Object expected) throws Exception { + runTest(createComparisonTestable(statement, expected, false)); + return this; + } + + /** + * Evaluates the given statement and compares its result to the collection of expected objects + * without considering their order + * @param statement The statement to be evaluated + * @param items The expected items + * @return This {@code ModalTestCase} + * @throws Exception + */ + public ModalTestCase testCollection(String statement, Object... items) throws Exception { + runTest(collectionTestable(statement, false, items)); + return this; + } + + /** + * Evaluates the given statement and compares its result to the collection of expected objects + * while considering their order + * @param statement The statement to be evaluated + * @param items The expected items, in order + * @return This {@code ModalTestCase} + * @throws Exception + */ + public ModalTestCase testExactOrder(String statement, Object... items) throws Exception { + runTest(collectionTestable(statement, true, items)); + return this; + } + + /** + * Evaluates the given statement and checks whether the given error message appears + * @param expectedError The expected error message + * @param statements The statement(s) to be evaluated + * @return This ModalTestCase + * @throws Exception + */ + public ModalTestCase testIfExactError(String expectedError, String... statements) + throws Exception { + runTest(errorTestable(true, expectedError, statements)); + return this; + } + + /** + * Evaluates the given statement and checks whether an error that contains the expected message + * occurs + * @param expectedError + * @param statements + * @return This ModalTestCase + * @throws Exception + */ + public ModalTestCase testIfErrorContains(String expectedError, String... statements) + throws Exception { + runTest(errorTestable(false, expectedError, statements)); + return this; + } + + /** + * Looks up the value of the specified variable and compares it to the expected value + * @param name + * @param expected + * @return This ModalTestCase + * @throws Exception + */ + public ModalTestCase testLookup(String name, Object expected) throws Exception { + runTest(createLookUpTestable(name, expected)); + return this; + } + + /** + * Creates a Testable that checks whether the evaluation of the given statement leads to the + * expected error + * @param statements + * @param error + * @param exactMatch If true, the error message has to be identical to the expected error + * @return An instance of Testable that runs the error check + */ + protected Testable errorTestable(final boolean exactMatch, final String error, + final String... statements) { + return new Testable() { + @Override + public void run() throws Exception { + if (exactMatch) { + checkEvalError(error, statements); + } else { + checkEvalErrorContains(error, statements); + } + } + }; + } + + /** + * Creates a testable that checks whether the evaluation of the given statement leads to a list + * that contains exactly the expected objects + * @param statement The statement to be evaluated + * @param ordered Determines whether the order of the elements is checked as well + * @param expected Expected objects + * @return An instance of Testable that runs the check + */ + protected Testable collectionTestable( + final String statement, final boolean ordered, final Object... expected) { + return new Testable() { + @Override + public void run() throws Exception { + Ordered tmp = assertThat((Iterable<?>) eval(statement)).containsExactly(expected); + + if (ordered) { + tmp.inOrder(); + } + } + }; + } + + /** + * Creates a testable that compares the evaluation of the given statement to a specified result + * + * @param statement The statement to be evaluated + * @param expected Either the expected object or an expression whose evaluation leads to the + * expected object + * @param expectedIsExpression Signals whether {@code expected} is an object or an expression + * @return An instance of Testable that runs the comparison + */ + protected Testable createComparisonTestable( + final String statement, final Object expected, final boolean expectedIsExpression) { + return new Testable() { + @Override + public void run() throws Exception { + Object actual = eval(statement); + + // Prints the actual object instead of evaluating the expected expression + if (expectedIsExpression) { + actual = Printer.repr(actual, '\''); + } + + assertThat(actual).isEqualTo(expected); + } + }; + } + + /** + * Creates a Testable that looks up the given variable and compares its value to the expected + * value + * @param name + * @param expected + * @return An instance of Testable that does both lookup and comparison + */ + protected Testable createLookUpTestable(final String name, final Object expected) { + return new Testable() { + @Override + public void run() throws Exception { + assertThat(lookup(name)).isEqualTo(expected); + } + }; + } + + /** + * Executes the given Testable + * @param testable + * @throws Exception + */ + protected void runTest(Testable testable) throws Exception { + run(new TestableDecorator(setup, testable)); + } + + protected abstract void run(Testable testable) throws Exception; + } + + /** + * A simple decorator that allows the execution of setup actions before running + * a {@code Testable} + */ + class TestableDecorator implements Testable { + private final SetupActions setup; + private final Testable decorated; + + public TestableDecorator(SetupActions setup, Testable decorated) { + this.setup = setup; + this.decorated = decorated; + } + + /** + * Executes all stored actions and updates plus the actual {@code Testable} + */ + @Override + public void run() throws Exception { + setup.executeAll(); + decorated.run(); + } + } + + /** + * A container for collection actions that should be executed before a test + */ + class SetupActions { + private List<Testable> setup; + + public SetupActions() { + setup = new LinkedList<>(); + } + + /** + * Registers a variable that has to be updated before a test + * + * @param name + * @param value + */ + public void registerUpdate(final String name, final Object value) { + setup.add(new Testable() { + @Override + public void run() throws Exception { + EvaluationTestCase.this.update(name, value); + } + }); + } + + /** + * Registers a statement for evaluation prior to a test + * + * @param statements + */ + public void registerEval(final String... statements) { + setup.add(new Testable() { + @Override + public void run() throws Exception { + EvaluationTestCase.this.eval(statements); + } + }); + } + + /** + * Executes all stored actions and updates + * @throws Exception + */ + public void executeAll() throws Exception { + for (Testable testable : setup) { + testable.run(); + } + } + } + + /** + * A class that executes each separate test in both modes (Build and Skylark) + */ + protected class BothModesTest extends ModalTestCase { + public BothModesTest() {} + + /** + * Executes the given Testable in both Build and Skylark mode + */ + @Override + protected void run(Testable testable) throws Exception { + enableSkylarkMode(); + try { + testable.run(); + } catch (Exception e) { + throw new Exception("While in Skylark mode", e); + } + + enableBuildMode(); + try { + testable.run(); + } catch (Exception e) { + throw new Exception("While in Build mode", e); + } + } + } + + /** + * A class that runs all tests in Build mode + */ + protected class BuildTest extends ModalTestCase { + public BuildTest() {} + + @Override + protected void run(Testable testable) throws Exception { + enableBuildMode(); + testable.run(); + } + } + + /** + * A class that runs all tests in Skylark mode + */ + protected class SkylarkTest extends ModalTestCase { + public SkylarkTest() {} + + @Override + protected void run(Testable testable) throws Exception { + enableSkylarkMode(); + testable.run(); + } + } }
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java index 0264681..88f4503 100644 --- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java +++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -15,8 +15,6 @@ 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; @@ -29,8 +27,8 @@ 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 com.google.devtools.build.lib.testutil.TestMode; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,13 +39,19 @@ */ @RunWith(JUnit4.class) public class SkylarkEvaluationTest extends EvaluationTest { - - // Restoring defaults overridden by EvaluationTest - @Override - public EvaluationContext newEvaluationContext() { - return SkylarkModules.newEvaluationContext(getEventHandler()); + public SkylarkEvaluationTest() throws Exception { + setMode(TestMode.SKYLARK); } + /** + * Creates an instance of {@code SkylarkTest} in order to run the tests from the base class in a + * Skylark context + */ + @Override + protected ModalTestCase newTest() { + return new SkylarkTest(); + } + @SkylarkModule(name = "Mock", doc = "") static class Mock { @SkylarkCallable(doc = "") @@ -146,43 +150,34 @@ @Test public void testSimpleIf() throws Exception { - eval("def foo():", + new SkylarkTest().setUp("def foo():", " a = 0", " x = 0", " if x: a = 5", " return a", - "a = foo()"); - assertEquals(0, lookup("a")); + "a = foo()").testLookup("a", 0); } @Test public void testIfPass() throws Exception { - eval("def foo():", + new SkylarkTest().setUp("def foo():", " a = 1", " x = True", " if x: pass", " return a", - "a = foo()"); - assertEquals(1, lookup("a")); + "a = foo()").testLookup("a", 1); } @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")); + executeNestedIf(0, 0, 0); + executeNestedIf(1, 0, 3); + executeNestedIf(1, 1, 5); } - private void executeNestedIf(int x, int y) throws Exception { + private void executeNestedIf(int x, int y, int expected) throws Exception { String fun = String.format("foo%s%s", x, y); - eval("def " + fun + "():", + new SkylarkTest().setUp("def " + fun + "():", " x = " + x, " y = " + y, " a = 0", @@ -192,7 +187,7 @@ " a = 2", " b = 3", " return a + b", - "x = " + fun + "()"); + "x = " + fun + "()").testLookup("x", expected); } @Test @@ -202,14 +197,14 @@ } private void executeIfElse(String fun, String y, int expected) throws Exception { - eval("def " + fun + "():", + new SkylarkTest().setUp("def " + fun + "():", " y = '" + y + "'", " x = 5", " if x:", " if y: a = 2", " else: a = 3", - " return a\n"); - assertEquals(expected, eval(fun + "()")); + " return a", + "z = " + fun + "()").testLookup("z", expected); } @Test @@ -228,7 +223,7 @@ } private void execIfElifElse(int x, int y, int v) throws Exception { - eval("def foo():", + new SkylarkTest().setUp("def foo():", " x = " + x + "", " y = " + y + "", " if x:", @@ -237,100 +232,89 @@ " return 2", " else:", " return 3", - "v = foo()"); - assertEquals(v, lookup("v")); + "v = foo()").testLookup("v", v); } @Test public void testForOnList() throws Exception { - eval("def foo():", + new SkylarkTest().setUp("def foo():", " s = ''", " for i in ['hello', ' ', 'world']:", " s = s + i", " return s", - "s = foo()"); - assertEquals("hello world", lookup("s")); + "s = foo()").testLookup("s", "hello world"); } - @SuppressWarnings("unchecked") @Test public void testForOnString() throws Exception { - eval("def foo():", + new SkylarkTest().setUp("def foo():", " s = []", " for i in 'abc':", " s = s + [i]", " return s", - "s = foo()"); - assertThat((Iterable<Object>) lookup("s")).containsExactly("a", "b", "c").inOrder(); + "s = foo()").testExactOrder("s", "a", "b", "c"); } @Test public void testForAssignmentList() throws Exception { - eval("def foo():", + new SkylarkTest().setUp("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 + " d = ['d', 'e', 'f']", // check that we use the old list " return s", - "s = foo()"); - assertEquals("abc", lookup("s")); + "s = foo()").testLookup("s", "abc"); } @Test public void testForAssignmentDict() throws Exception { - eval("def func():", + new SkylarkTest().setUp("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")); + "s = func()").testLookup("s", "abc"); } @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()"); - + new SkylarkTest() + .update("mock", new Mock()) + .testIfExactError("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():", + new SkylarkTest().setUp("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")); + "s = foo()").testLookup("s", "abc"); } @Test public void testForLoopReuseVariable() throws Exception { - eval("def foo():", + new SkylarkTest().setUp("def foo():", " s = ''", " for i in ['a', 'b']:", " for i in ['c', 'd']: s = s + i", " return s", - "s = foo()"); - assertEquals("cdcd", lookup("s")); + "s = foo()").testLookup("s", "cdcd"); } @Test public void testForLoopMultipleVariables() throws Exception { - eval("def foo():", + new SkylarkTest().setUp("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")); + "s = foo()").testLookup("s", "12.34."); } @Test @@ -457,15 +441,12 @@ " " + statement + "", "y = foo2()"); } - + @Test public void testNoneAssignment() throws Exception { - eval("def foo(x=None):", - " x = 1", - " x = None", - " return 2", - "s = foo()"); - assertEquals(2, lookup("s")); + new SkylarkTest() + .setUp("def foo(x=None):", " x = 1", " x = None", " return 2", "s = foo()") + .testLookup("s", 2); } @Test @@ -481,98 +462,111 @@ @Test public void testJavaCalls() throws Exception { - update("mock", new Mock()); - eval("b = mock.is_empty('a')"); - assertEquals(Boolean.FALSE, lookup("b")); + new SkylarkTest() + .update("mock", new Mock()) + .setUp("b = mock.is_empty('a')") + .testLookup("b", Boolean.FALSE); } @Test public void testJavaCallsOnSubClass() throws Exception { - update("mock", new MockSubClass()); - eval("b = mock.is_empty('a')"); - assertEquals(Boolean.FALSE, lookup("b")); + new SkylarkTest() + .update("mock", new MockSubClass()) + .setUp("b = mock.is_empty('a')") + .testLookup("b", Boolean.FALSE); } @Test public void testJavaCallsOnInterface() throws Exception { - update("mock", new MockSubClass()); - eval("b = mock.is_empty_interface('a')"); - assertEquals(Boolean.FALSE, lookup("b")); + new SkylarkTest() + .update("mock", new MockSubClass()) + .setUp("b = mock.is_empty_interface('a')") + .testLookup("b", Boolean.FALSE); } @Test public void testJavaCallsNotSkylarkCallable() throws Exception { - update("mock", new Mock()); - checkEvalError("No matching method found for value() in Mock", "mock.value()"); + new SkylarkTest() + .update("mock", new Mock()) + .testIfExactError("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()"); + new SkylarkTest().testIfExactError("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", + new SkylarkTest().testIfExactError( + "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')"); + new SkylarkTest() + .update("mock", new MockMultipleMethodClass()) + .testIfExactError( + "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" + new SkylarkTest().testIfExactError( + "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"); + new SkylarkTest().testIfExactError( + "No matching method found for to_string() in int", "s = 3.to_string()"); } @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')"); + new SkylarkTest() + .update("mock", new MockSubClass()) + .testIfExactError( + "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")); + new SkylarkTest() + .update("mock", new Mock()) + .setUp("v = mock.struct_field") + .testLookup("v", "a"); } @Test public void testStructAccessAsFuncall() throws Exception { - update("mock", new Mock()); - checkEvalError("No matching method found for struct_field() in Mock", - "v = mock.struct_field()"); + new SkylarkTest() + .update("mock", new Mock()) + .testIfExactError( + "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"); + new SkylarkTest() + .update("mock", new Mock()) + .testIfExactError("Object of type 'Mock' has no field \"function\"", "v = mock.function"); } @Test public void testConditionalStructConcatenation() throws Exception { + // TODO(fwe): cannot be handled by current testing suite eval("def func():", " x = struct(a = 1, b = 2)", " if True:", " x += struct(c = 1, d = 2)", " return x", - "x = func()\n"); + "x = func()"); SkylarkClassObject x = (SkylarkClassObject) lookup("x"); assertEquals(1, x.getValue("a")); assertEquals(2, x.getValue("b")); @@ -582,96 +576,103 @@ @Test public void testJavaFunctionReturnsMutableObject() throws Exception { - update("mock", new Mock()); - checkEvalError("Method 'return_mutable' returns a mutable object (type of Mock)", - "mock.return_mutable()"); + new SkylarkTest() + .update("mock", new Mock()) + .testIfExactError( + "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)"); + new SkylarkTest() + .update("mock", new Mock()) + .testIfExactError( + "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")); + new SkylarkTest() + .update("mock", new MockClassObject()) + .setUp("v = mock.field") + .testLookup("v", "a"); } @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'])")); + new SkylarkTest().testStatement("'b' in set(['a', 'b'])", Boolean.TRUE) + .testStatement("'c' in set(['a', 'b'])", Boolean.FALSE) + .testStatement("1 in set(['a', 'b'])", Boolean.FALSE); } @Test public void testClassObjectCannotAccessNestedSet() throws Exception { - update("mock", new MockClassObject()); - checkEvalError("Type is not allowed in Skylark: EmptyNestedSet", "v = mock.nset"); + new SkylarkTest() + .update("mock", new MockClassObject()) + .testIfExactError("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")); + new SkylarkTest() + .update("mock", new Mock()) + .setUp("v = mock.nullfunc_working()") + .testLookup("v", Environment.NONE); } @Test public void testVoidJavaFunctionReturnsNone() throws Exception { - update("mock", new Mock()); - eval("v = mock.voidfunc()"); - assertSame(Environment.NONE, lookup("v")); + new SkylarkTest() + .update("mock", new Mock()) + .setUp("v = mock.voidfunc()") + .testLookup("v", Environment.NONE); } @Test public void testAugmentedAssignment() throws Exception { - eval("def f1(x):", + new SkylarkTest().setUp("def f1(x):", " x += 1", " return x", "", - "foo = f1(41)\n"); - assertEquals(42, lookup("foo")); + "foo = f1(41)").testLookup("foo", 42); } @Test public void testStaticDirectJavaCall() throws Exception { - update("Mock", Mock.class); - eval("val = Mock.value_of('8')"); - assertEquals(8, lookup("val")); + new SkylarkTest().update("Mock", Mock.class).setUp("val = Mock.value_of('8')") + .testLookup("val", 8); } @Test public void testStaticDirectJavaCallMethodIsNonStatic() throws Exception { - update("Mock", Mock.class); - checkEvalError("Method 'is_empty' is not static", "val = Mock.is_empty('a')"); + new SkylarkTest().update("Mock", Mock.class).testIfExactError("Method 'is_empty' is not static", + "val = Mock.is_empty('a')"); } @Test public void testDictComprehensions_IterationOrder() throws Exception { - eval("def foo():", + new SkylarkTest().setUp("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")); + "s = foo()").testLookup("s", "abc"); } @Test public void testStructCreation() throws Exception { + // TODO(fwe): cannot be handled by current testing suite eval("x = struct(a = 1, b = 2)"); assertThat(lookup("x")).isInstanceOf(ClassObject.class); } @Test public void testStructFields() throws Exception { + // TODO(fwe): cannot be handled by current testing suite eval("x = struct(a = 1, b = 2)"); ClassObject x = (ClassObject) lookup("x"); assertEquals(1, x.getValue("a")); @@ -680,35 +681,33 @@ @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")); + new SkylarkTest() + .setUp("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b") + .testLookup("x1", 1) + .testLookup("x2", 2); } @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"); + new SkylarkTest().testIfExactError( + "Object of type 'struct' has no field \"c\"", "x = struct(a = 1, b = 2)", "y = x.c"); } @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"); + new SkylarkTest().testIfExactError( + "No matching method found for a(int) in struct", "x = struct(a = 1, b = 2)", "x1 = x.a(1)"); } @Test public void testStructPosArgs() throws Exception { - checkEvalError("struct(**kwargs) does not accept positional arguments, but got 1", - "x = struct(1, b = 2)\n"); + new SkylarkTest().testIfExactError( + "struct(**kwargs) does not accept positional arguments, but got 1", "x = struct(1, b = 2)"); } @Test public void testStructConcatenationFieldNames() throws Exception { + // TODO(fwe): cannot be handled by current testing suite eval("x = struct(a = 1, b = 2)", "y = struct(c = 1, d = 2)", "z = x + y\n"); @@ -718,6 +717,7 @@ @Test public void testStructConcatenationFieldValues() throws Exception { + // TODO(fwe): cannot be handled by current testing suite eval("x = struct(a = 1, b = 2)", "y = struct(c = 1, d = 2)", "z = x + y\n"); @@ -730,49 +730,46 @@ @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"); + new SkylarkTest().testIfExactError("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"); + new SkylarkTest().testIfExactError("Object of type 'string' has no field \"field\"", + "x = 'a'.field"); } @Test public void testPlusEqualsOnDict() throws Exception { - eval("def func():", + new SkylarkTest().setUp("def func():", " d = {'a' : 1}", " d += {'b' : 2}", " return d", - "d = func()"); - assertEquals(ImmutableMap.of("a", 1, "b", 2), lookup("d")); + "d = func()") + .testLookup("d", ImmutableMap.of("a", 1, "b", 2)); } @Test public void testDictAssignmentAsLValue() throws Exception { - eval("def func():", + new SkylarkTest().setUp("def func():", " d = {'a' : 1}", " d['b'] = 2", " return d", - "d = func()"); - assertEquals(ImmutableMap.of("a", 1, "b", 2), lookup("d")); + "d = func()").testLookup("d", ImmutableMap.of("a", 1, "b", 2)); } @Test public void testDictAssignmentAsLValueNoSideEffects() throws Exception { - eval("def func(d):", + new SkylarkTest().setUp("def func(d):", " d['b'] = 2", "d = {'a' : 1}", - "func(d)"); - assertEquals(ImmutableMap.of("a", 1), lookup("d")); + "func(d)").testLookup("d", ImmutableMap.of("a", 1)); } @Test public void testListIndexAsLValueAsLValue() throws Exception { - checkEvalError("unsupported operand type(s) for +: 'list' and 'dict'", + new SkylarkTest().testIfExactError("unsupported operand type(s) for +: 'list' and 'dict'", "def id(l):", " return l", "def func():", @@ -784,32 +781,30 @@ @Test public void testTopLevelDict() throws Exception { - eval("if 1:", + new SkylarkTest().setUp("if 1:", " v = 'a'", "else:", - " v = 'b'"); - assertEquals("a", lookup("v")); + " v = 'b'").testLookup("v", "a"); } @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")); + new SkylarkTest().setUp("def foo(a, b, c):", + " return a + b + c", "s = foo(1, c=2, b=3)") + .testLookup("s", 6); } @Test public void testFunctionCallOrdering() throws Exception { - eval("def func(): return foo() * 2", + new SkylarkTest().setUp("def func(): return foo() * 2", "def foo(): return 2", - "x = func()"); - assertThat(lookup("x")).isEqualTo(4); + "x = func()") + .testLookup("x", 4); } @Test public void testFunctionCallBadOrdering() throws Exception { - checkEvalError("name 'foo' is not defined", + new SkylarkTest().testIfExactError("name 'foo' is not defined", "def func(): return foo() * 2", "x = func()", "def foo(): return 2"); @@ -817,118 +812,111 @@ @Test public void testNoneTrueFalseInSkylark() throws Exception { - eval("a = None", + new SkylarkTest().setUp("a = None", "b = True", - "c = False"); - assertSame(Environment.NONE, lookup("a")); - assertTrue((Boolean) lookup("b")); - assertFalse((Boolean) lookup("c")); + "c = False") + .testLookup("a", Environment.NONE) + .testLookup("b", Boolean.TRUE) + .testLookup("c", Boolean.FALSE); } @Test public void testHasattr() throws Exception { - eval("s = struct(a=1)", + new SkylarkTest().setUp("s = struct(a=1)", "x = hasattr(s, 'a')", - "y = hasattr(s, 'b')\n"); - assertTrue((Boolean) lookup("x")); - assertFalse((Boolean) lookup("y")); + "y = hasattr(s, 'b')\n") + .testLookup("x", Boolean.TRUE) + .testLookup("y", Boolean.FALSE); } @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")); + new SkylarkTest() + .update("mock", new Mock()) + .setUp("a = hasattr(mock, 'struct_field')", "b = hasattr(mock, 'function')", + "c = hasattr(mock, 'is_empty')", "d = hasattr('str', 'replace')", + "e = hasattr(mock, 'other')\n") + .testLookup("a", Boolean.TRUE) + .testLookup("b", Boolean.TRUE) + .testLookup("c", Boolean.TRUE) + .testLookup("d", Boolean.TRUE) + .testLookup("e", Boolean.FALSE); } @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")); + new SkylarkTest() + .setUp("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')") + .testLookup("x", "val") + .testLookup("y", "def") + .testLookup("z", "def") + .testLookup("w", "val"); } @Test public void testGetattrNoAttr() throws Exception { - checkEvalError("Object of type 'struct' has no field \"b\"", - "s = struct(a='val')", - "getattr(s, 'b')"); + new SkylarkTest().testIfExactError("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)"); + new SkylarkTest().testIfExactError("cannot concatenate lists and tuples", "[1, 2] + (3, 4)"); } @Test public void testCannotCreateMixedListInSkylark() throws Exception { - update("mock", new Mock()); - checkEvalError( + new SkylarkTest().update("mock", new Mock()).testIfExactError( "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]"); + new SkylarkTest().update("mock", new Mock()).testIfExactError( + "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(); + new SkylarkTest().testExactOrder("[] + ['a', 'b']", "a", "b"); } @Test public void testFormatStringWithTuple() throws Exception { - eval("v = '%s%s' % ('a', 1)"); - assertEquals("a1", lookup("v")); + new SkylarkTest().setUp("v = '%s%s' % ('a', 1)").testLookup("v", "a1"); } @Test public void testSingletonTuple() throws Exception { - eval("v = (1,)"); - assertEquals("(1,)", lookup("v").toString()); + new SkylarkTest().testExactOrder("(1,)", 1); } - @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(); + new SkylarkTest().update("mock", new MockClassObject()).setUp() + .testExactOrder("dir(mock)", "field", "nset"); } - @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(); + new SkylarkTest().update("mock", new Mock()).testExactOrder("dir(mock)", + "function", + "is_empty", + "nullfunc_failing", + "nullfunc_working", + "return_mutable", + "string", + "string_list", + "struct_field", + "value_of", + "voidfunc"); } @Test public void testPrint() throws Exception { + // TODO(fwe): cannot be handled by current testing suite setFailFast(false); eval("print('hello')"); assertContainsEvent("hello"); @@ -940,7 +928,7 @@ @Test public void testPrintBadKwargs() throws Exception { - checkEvalError( + new SkylarkTest().testIfExactError( "unexpected keywords 'end', 'other' in call to print(*args, sep: string = \" \")", "print(end='x', other='y')"); } @@ -960,6 +948,10 @@ @Override @Test public void testConcatLists() throws Exception { + new SkylarkTest().testExactOrder("[1,2] + [3,4]", 1, 2, 3, 4).testExactOrder("(1,2)", 1, 2) + .testExactOrder("(1,2) + (3,4)", 1, 2, 3, 4); + + // TODO(fwe): cannot be handled by current testing suite // list Object x = eval("[1,2] + [3,4]"); assertThat((Iterable<Object>) x).containsExactly(1, 2, 3, 4).inOrder(); @@ -981,56 +973,56 @@ @Override @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"); + new SkylarkTest().testIfExactError( + "in operator only works on strings if the left operand is also a string", "1 in '123'"); + new SkylarkTest().testIfExactError( + "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"); + new SkylarkTest().testIfExactError("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", + new SkylarkTest().testIfExactError("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", + new SkylarkTest().testIfExactError("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", + new SkylarkTest().testIfExactError("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)]"); + new SkylarkTest().testIfExactError("ERROR 2:1: Variable x is read only", + "[x + y for x, y in (1, 2)]", "[x + y for x, y in (1, 2)]"); - checkEvalError("type 'int' is not a collection", "[x2 + y2 for x2, y2 in (1, 2)]"); + new SkylarkTest().testIfExactError("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")); + new SkylarkTest().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 testConditionalExpressionAtToplevel() throws Exception { - eval("x = 1 if 2 else 3"); - assertEquals(1, lookup("x")); + new SkylarkTest().setUp("x = 1 if 2 else 3").testLookup("x", 1); } @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)")); + new SkylarkTest().setUp("def foo(a, b, c): return a+b if c else a-b\n").testStatement( + "foo(23, 5, 0)", 18); } }
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java b/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java new file mode 100644 index 0000000..5ec7ac2 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java
@@ -0,0 +1,44 @@ +// 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.testutil; + +import com.google.devtools.build.lib.events.EventHandler; +import com.google.devtools.build.lib.rules.SkylarkModules; +import com.google.devtools.build.lib.syntax.Environment; +import com.google.devtools.build.lib.syntax.EvaluationContext; + +/** + * Describes a particular testing mode by determining how the + * appropriate {@code EvaluationContext} has to be created + */ +public abstract class TestMode { + public static final TestMode BUILD = new TestMode() { + @Override + public EvaluationContext createContext(EventHandler eventHandler, Environment environment) + throws Exception { + return EvaluationContext.newBuildContext(eventHandler, environment); + } + }; + + public static final TestMode SKYLARK = new TestMode() { + @Override + public EvaluationContext createContext(EventHandler eventHandler, Environment environment) + throws Exception { + return SkylarkModules.newEvaluationContext(eventHandler); + } + }; + + public abstract EvaluationContext createContext( + EventHandler eventHandler, Environment environment) throws Exception; +} \ No newline at end of file