aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google
diff options
context:
space:
mode:
authorGravatar Francois-Rene Rideau <tunes@google.com>2015-08-26 13:37:30 +0000
committerGravatar Philipp Wollermann <philwo@google.com>2015-08-27 14:44:17 +0000
commitb5c3b3a1479f552c2ba2a844a84107efe16e8c50 (patch)
tree18223b599aeafe3deaa07abfa460ce22bc7b49f6 /src/test/java/com/google
parent3c620c0ba325edaeefe81ef92519cd85a732e3a9 (diff)
Import MethodLibraryTest.java into bazel
-- MOS_MIGRATED_REVID=101570634
Diffstat (limited to 'src/test/java/com/google')
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java1012
1 files changed, 1012 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
new file mode 100644
index 0000000000..1a489d29e1
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
@@ -0,0 +1,1012 @@
+// Copyright 2006 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.syntax;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+/**
+ * Tests for MethodLibrary.
+ */
+@RunWith(JUnit4.class)
+public class MethodLibraryTest extends EvaluationTestCase {
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setFailFast(true);
+ }
+
+ @Test
+ public void testStackTrace() throws Exception {
+ new SkylarkTest().testIfExactError(
+ "Method string.index(sub: string, start: int, end: int or NoneType) is not "
+ + "applicable for arguments (int, int, NoneType): 'sub' is int, but should be string\n"
+ + "\tin string.index [Built-In]\n"
+ + "\tin bar [4:4]\n"
+ + "\tin foo [2:4]",
+ "def foo():",
+ " bar(1)",
+ "def bar(x):",
+ " 'test'.index(x)",
+ "foo()");
+ }
+
+ @Test
+ public void testBuiltinFunctionErrorMessage() throws Exception {
+ new SkylarkTest()
+ .testIfErrorContains(
+ "Method set.union(newElements: Iterable) is not applicable for arguments (string): "
+ + "'newElements' is string, but should be Iterable",
+ "set([]).union('a')")
+ .testIfErrorContains(
+ "Method string.startswith(sub: string, start: int, end: int or NoneType) is not "
+ + "applicable for arguments (int, int, NoneType): 'sub' is int, "
+ + "but should be string",
+ "'test'.startswith(1)")
+ .testIfErrorContains(
+ "Method dict(args: Iterable, **kwargs) is not applicable for arguments "
+ + "(string, dict): 'args' is string, but should be Iterable",
+ "dict('a')");
+ }
+
+ @Test
+ public void testHasAttr() throws Exception {
+ new SkylarkTest()
+ .testStatement("hasattr(set(), 'union')", Boolean.TRUE)
+ .testStatement("hasattr('test', 'count')", Boolean.TRUE)
+ .testStatement("hasattr(dict(a = 1, b = 2), 'items')", Boolean.TRUE)
+ .testStatement("hasattr({}, 'items')", Boolean.TRUE);
+ }
+
+ @Test
+ public void testDir() throws Exception {
+ new SkylarkTest().testStatement(
+ "str(dir({}))", "[\"$index\", \"get\", \"items\", \"keys\", \"values\"]");
+ }
+
+ @Test
+ public void testBoolean() throws Exception {
+ new BothModesTest().testStatement("False", Boolean.FALSE).testStatement("True", Boolean.TRUE);
+ }
+
+ @Test
+ public void testBooleanUnsupportedOperationFails() throws Exception {
+ new BothModesTest().testIfErrorContains(
+ "unsupported operand type(s) for +: 'bool' and 'bool'", "True + True");
+ }
+
+ @Test
+ public void testPyStringJoin() throws Exception {
+ new BothModesTest().testStatement("'-'.join([ 'a', 'b', 'c' ])", "a-b-c");
+ }
+
+ @Test
+ public void testPyStringGlobalJoin() throws Exception {
+ // TODO(bazel-team): BUILD and Skylark should use the same code path (and same error message).
+ new BuildTest().testIfErrorContains(
+ "name 'join' is not defined", "join(' ', [ 'a', 'b', 'c' ])");
+
+ new SkylarkTest().testIfErrorContains("ERROR 1:1: function 'join' does not exist",
+ "join(' ', [ 'a', 'b', 'c' ])");
+
+ new BothModesTest().testStatement("' '.join([ 'a', 'b', 'c' ])", "a b c");
+ }
+
+ @Test
+ public void testPyStringJoinCompr() throws Exception {
+ new BothModesTest().testStatement("''.join([(x + '*') for x in ['a', 'b', 'c']])", "a*b*c*")
+ .testStatement(
+ "''.join([(y + '*' + z + '|') " + "for y in ['a', 'b', 'c'] for z in ['d', 'e']])",
+ "a*d|a*e|b*d|b*e|c*d|c*e|");
+ }
+
+ @Test
+ public void testPyStringLower() throws Exception {
+ new BothModesTest().testStatement("'Blah Blah'.lower()", "blah blah");
+ }
+
+ @Test
+ public void testPyStringUpper() throws Exception {
+ new BothModesTest()
+ .testStatement("'ein bier'.upper()", "EIN BIER")
+ .testStatement("''.upper()", "");
+ }
+
+ @Test
+ public void testPyStringReplace() throws Exception {
+ new BothModesTest()
+ .testStatement("'banana'.replace('a', 'e')", "benene")
+ .testStatement("'banana'.replace('a', '$()')", "b$()n$()n$()")
+ .testStatement("'banana'.replace('a', '$')", "b$n$n$")
+ .testStatement("'banana'.replace('a', '\\\\')", "b\\n\\n\\")
+ .testStatement("'b$()n$()n$()'.replace('$()', '$($())')", "b$($())n$($())n$($())")
+ .testStatement("'b\\\\n\\\\n\\\\'.replace('\\\\', '$()')", "b$()n$()n$()");
+ }
+
+ @Test
+ public void testPyStringReplace2() throws Exception {
+ new BothModesTest()
+ .testStatement("'banana'.replace('a', 'e', 2)", "benena")
+;
+ }
+
+ @Test
+ public void testPyStringSplit() throws Exception {
+ new BothModesTest().testEval("'h i'.split(' ')", "['h', 'i']");
+ }
+
+ @Test
+ public void testPyStringSplit2() throws Exception {
+ new BothModesTest().testEval("'h i p'.split(' ')", "['h', 'i', 'p']");
+ }
+
+ @Test
+ public void testPyStringSplit3() throws Exception {
+ new BothModesTest().testEval("'a,e,i,o,u'.split(',', 2)", "['a', 'e', 'i,o,u']");
+ }
+
+ @Test
+ public void testPyStringSplitNoSep() throws Exception {
+ new BothModesTest().testEval(
+ "' 1 2 3 '.split(' ')", "['', '', '1', '', '2', '', '3', '', '']");
+ }
+
+ @Test
+ public void testPyStringRSplitRegex() throws Exception {
+ new BothModesTest()
+ .testEval("'foo/bar.lisp'.rsplit('.')", "['foo/bar', 'lisp']")
+ .testEval("'foo/bar.?lisp'.rsplit('.?')", "['foo/bar', 'lisp']")
+ .testEval("'fwe$foo'.rsplit('$')", "['fwe', 'foo']")
+ .testEval("'windows'.rsplit('\\w')", "['windows']");
+ }
+
+ @Test
+ public void testPyStringRSplitNoMatch() throws Exception {
+ new BothModesTest()
+ .testEval("''.rsplit('o')", "['']")
+ .testEval("'google'.rsplit('x')", "['google']");
+ }
+
+ @Test
+ public void testPyStringRSplitSeparator() throws Exception {
+ new BothModesTest()
+ .testEval("'xxxxxx'.rsplit('x')", "['', '', '', '', '', '', '']")
+ .testEval("'xxxxxx'.rsplit('x', 1)", "['xxxxx', '']")
+ .testEval("'xxxxxx'.rsplit('x', 2)", "['xxxx', '', '']")
+ .testEval("'xxxxxx'.rsplit('x', 3)", "['xxx', '', '', '']")
+ .testEval("'xxxxxx'.rsplit('x', 4)", "['xx', '', '', '', '']")
+ .testEval("'xxxxxx'.rsplit('x', 5)", "['x', '', '', '', '', '']")
+ .testEval("'xxxxxx'.rsplit('x', 6)", "['', '', '', '', '', '', '']")
+ .testEval("'xxxxxx'.rsplit('x', 7)", "['', '', '', '', '', '', '']");
+
+ }
+
+ @Test
+ public void testPyStringRSplitLongerSep() throws Exception {
+ new BothModesTest().testEval("'abcdabef'.rsplit('ab')", "['', 'cd', 'ef']").testEval(
+ "'google_or_gogol'.rsplit('go')", "['', 'ogle_or_', '', 'l']");
+ }
+
+ @Test
+ public void testPyStringRSplitMaxSplit() throws Exception {
+ new BothModesTest()
+ .testEval("'google'.rsplit('o')", "['g', '', 'gle']")
+ .testEval("'google'.rsplit('o')", "['g', '', 'gle']")
+ .testEval("'google'.rsplit('o', 1)", "['go', 'gle']")
+ .testEval("'google'.rsplit('o', 2)", "['g', '', 'gle']")
+ .testEval("'google'.rsplit('o', 3)", "['g', '', 'gle']")
+ .testEval("'ogooglo'.rsplit('o')", "['', 'g', '', 'gl', '']")
+ .testEval("'ogooglo'.rsplit('o', 1)", "['ogoogl', '']")
+ .testEval("'ogooglo'.rsplit('o', 2)", "['ogo', 'gl', '']")
+ .testEval("'ogooglo'.rsplit('o', 3)", "['og', '', 'gl', '']")
+ .testEval("'ogooglo'.rsplit('o', 4)", "['', 'g', '', 'gl', '']")
+ .testEval("'ogooglo'.rsplit('o', 5)", "['', 'g', '', 'gl', '']")
+ .testEval("'google'.rsplit('google')", "['', '']")
+ .testEval("'google'.rsplit('google', 1)", "['', '']")
+ .testEval("'google'.rsplit('google', 2)", "['', '']");
+ }
+
+ @Test
+ public void testPyStringPartitionEasy() throws Exception {
+ new BothModesTest().testEval("'lawl'.partition('a')", "['l', 'a', 'wl']").testEval(
+ "'lawl'.rpartition('a')", "['l', 'a', 'wl']");
+ }
+
+ @Test
+ public void testPyStringPartitionMultipleSep() throws Exception {
+ new BothModesTest()
+ .testEval("'google'.partition('o')", "['g', 'o', 'ogle']")
+ .testEval("'google'.rpartition('o')", "['go', 'o', 'gle']")
+ .testEval("'xxx'.partition('x')", "['', 'x', 'xx']")
+ .testEval("'xxx'.rpartition('x')", "['xx', 'x', '']");
+ }
+
+ @Test
+ public void testPyStringPartitionEmptyInput() throws Exception {
+ new BothModesTest()
+ .testEval("''.partition('a')", "['', '', '']")
+ .testEval("''.rpartition('a')", "['', '', '']");
+ }
+
+ @Test
+ public void testPyStringPartitionEmptySeparator() throws Exception {
+ new BothModesTest()
+ .testIfErrorContains("Empty separator", "'google'.partition('')")
+ .testIfErrorContains("Empty separator", "'google'.rpartition('')");
+ }
+
+ @Test
+ public void testPyStringPartitionDefaultSep() throws Exception {
+ new BothModesTest()
+ .testEval("'hi this is a test'.partition()", "['hi', ' ', 'this is a test']")
+ .testEval("'hi this is a test'.rpartition()", "['hi this is a', ' ', 'test']")
+ .testEval("'google'.partition()", "['google', '', '']")
+ .testEval("'google'.rpartition()", "['', '', 'google']");
+ }
+
+ @Test
+ public void testPyStringPartitionNoMatch() throws Exception {
+ new BothModesTest()
+ .testEval("'google'.partition('x')", "['google', '', '']")
+ .testEval("'google'.rpartition('x')", "['', '', 'google']");
+ }
+
+ @Test
+ public void testPyStringPartitionWordBoundaries() throws Exception {
+ new BothModesTest()
+ .testEval("'goog'.partition('g')", "['', 'g', 'oog']")
+ .testEval("'goog'.rpartition('g')", "['goo', 'g', '']")
+ .testEval("'plex'.partition('p')", "['', 'p', 'lex']")
+ .testEval("'plex'.rpartition('p')", "['', 'p', 'lex']")
+ .testEval("'plex'.partition('x')", "['ple', 'x', '']")
+ .testEval("'plex'.rpartition('x')", "['ple', 'x', '']");
+ }
+
+ @Test
+ public void testPyStringPartitionLongSep() throws Exception {
+ new BothModesTest()
+ .testEval("'google'.partition('oog')", "['g', 'oog', 'le']")
+ .testEval("'google'.rpartition('oog')", "['g', 'oog', 'le']")
+ .testEval(
+ "'lolgooglolgooglolgooglol'.partition('goog')", "['lol', 'goog', 'lolgooglolgooglol']")
+ .testEval(
+ "'lolgooglolgooglolgooglol'.rpartition('goog')",
+ "['lolgooglolgooglol', 'goog', 'lol']");
+ }
+
+ @Test
+ public void testPyStringPartitionCompleteString() throws Exception {
+ new BothModesTest()
+ .testEval("'google'.partition('google')", "['', 'google', '']")
+ .testEval("'google'.rpartition('google')", "['', 'google', '']");
+ }
+
+ @Test
+ public void testPyStringTitle() throws Exception {
+ new BothModesTest().testStatement(
+ "'this is a very simple test'.title()", "This Is A Very Simple Test");
+ new BothModesTest().testStatement(
+ "'Do We Keep Capital Letters?'.title()", "Do We Keep Capital Letters?");
+ new BothModesTest().testStatement(
+ "'this isn\\'t just an ol\\' apostrophe test'.title()",
+ "This Isn'T Just An Ol' Apostrophe Test");
+ new BothModesTest().testStatement(
+ "'Let us test crazy characters: _bla.exe//foo:bla(test$class)'.title()",
+ "Let Us Test Crazy Characters: _Bla.Exe//Foo:Bla(Test$Class)");
+ new BothModesTest().testStatement(
+ "'any germans here? äöü'.title()",
+ "Any Germans Here? Äöü");
+ new BothModesTest().testStatement(
+ "'WE HAve tO lOWERCASE soMEthING heRE, AI?'.title()",
+ "We Have To Lowercase Something Here, Ai?");
+ new BothModesTest().testStatement(
+ "'wh4t ab0ut s0me numb3rs'.title()", "Wh4T Ab0Ut S0Me Numb3Rs");
+ }
+
+ @Test
+ public void testPyStringRfind() throws Exception {
+ new BothModesTest()
+ .testStatement("'banana'.rfind('na')", 4)
+ .testStatement("'banana'.rfind('na', 3, 1)", -1)
+ .testStatement("'aaaa'.rfind('a', 1, 1)", -1)
+ .testStatement("'aaaa'.rfind('a', 1, 50)", 3)
+ .testStatement("'aaaa'.rfind('aaaaa')", -1)
+ .testStatement("'abababa'.rfind('ab', 1)", 4)
+ .testStatement("'abababa'.rfind('ab', 0)", 4)
+ .testStatement("'abababa'.rfind('ab', -1)", -1)
+ .testStatement("'abababa'.rfind('ab', -2)", -1)
+ .testStatement("'abababa'.rfind('ab', -3)", 4)
+ .testStatement("'abababa'.rfind('ab', 0, 1)", -1)
+ .testStatement("'abababa'.rfind('ab', 0, 2)", 0)
+ .testStatement("'abababa'.rfind('ab', -1000)", 4)
+ .testStatement("'abababa'.rfind('ab', 1000)", -1)
+ .testStatement("''.rfind('a', 1)", -1);
+ }
+
+ @Test
+ public void testPyStringFind() throws Exception {
+ new BothModesTest()
+ .testStatement("'banana'.find('na')", 2)
+ .testStatement("'banana'.find('na', 3, 1)", -1)
+ .testStatement("'aaaa'.find('a', 1, 1)", -1)
+ .testStatement("'aaaa'.find('a', 1, 50)", 1)
+ .testStatement("'aaaa'.find('aaaaa')", -1)
+ .testStatement("'abababa'.find('ab', 1)", 2)
+ .testStatement("'abababa'.find('ab', 0)", 0)
+ .testStatement("'abababa'.find('ab', -1)", -1)
+ .testStatement("'abababa'.find('ab', -2)", -1)
+ .testStatement("'abababa'.find('ab', -3)", 4)
+ .testStatement("'abababa'.find('ab', 0, 1)", -1)
+ .testStatement("'abababa'.find('ab', 0, 2)", 0)
+ .testStatement("'abababa'.find('ab', -1000)", 0)
+ .testStatement("'abababa'.find('ab', 1000)", -1)
+ .testStatement("''.find('a', 1)", -1);
+ }
+
+ @Test
+ public void testPyStringIndex() throws Exception {
+ new BothModesTest()
+ .testStatement("'banana'.index('na')", 2)
+ .testStatement("'abababa'.index('ab', 1)", 2)
+ .testIfErrorContains("substring \"foo\" not found in \"banana\"", "'banana'.index('foo')");
+ }
+
+ @Test
+ public void testPyStringRIndex() throws Exception {
+ new BothModesTest()
+ .testStatement("'banana'.rindex('na')", 4)
+ .testStatement("'abababa'.rindex('ab', 1)", 4)
+ .testIfErrorContains("substring \"foo\" not found in \"banana\"", "'banana'.rindex('foo')");
+ }
+
+ @Test
+ public void testPyStringEndswith() throws Exception {
+ new BothModesTest()
+ .testStatement("'Apricot'.endswith('cot')", true)
+ .testStatement("'a'.endswith('')", true)
+ .testStatement("''.endswith('')", true)
+ .testStatement("'Apricot'.endswith('co')", false)
+ .testStatement("'Apricot'.endswith('co', -1)", false)
+ .testStatement("'abcd'.endswith('c', -2, -1)", true)
+ .testStatement("'abcd'.endswith('c', 1, 8)", false)
+ .testStatement("'abcd'.endswith('d', 1, 8)", true);
+ }
+
+ @Test
+ public void testPyStringStartswith() throws Exception {
+ new BothModesTest()
+ .testStatement("'Apricot'.startswith('Apr')", true)
+ .testStatement("'Apricot'.startswith('A')", true)
+ .testStatement("'Apricot'.startswith('')", true)
+ .testStatement("'Apricot'.startswith('z')", false)
+ .testStatement("''.startswith('')", true)
+ .testStatement("''.startswith('a')", false);
+ }
+
+ @Test
+ public void testPySubstring() throws Exception {
+ new BothModesTest()
+ .testStatement("'012345678'[0:-1]", "01234567")
+ .testStatement("'012345678'[2:4]", "23")
+ .testStatement("'012345678'[-5:-3]", "45")
+ .testStatement("'012345678'[2:2]", "")
+ .testStatement("'012345678'[2:]", "2345678")
+ .testStatement("'012345678'[:3]", "012")
+ .testStatement("'012345678'[-1:]", "8")
+ .testStatement("'012345678'[:]", "012345678")
+ .testStatement("'012345678'[-1:2]", "")
+ .testStatement("'012345678'[4:2]", "");
+ }
+
+ @Test
+ public void testPyStringFormatEscaping() throws Exception {
+ new BothModesTest()
+ .testStatement("'{{}}'.format()", "{}")
+ .testStatement("'{{}}'.format(42)", "{}")
+ .testStatement("'{{ }}'.format()", "{ }")
+ .testStatement("'{{ }}'.format(42)", "{ }")
+ .testStatement("'{{{{}}}}'.format()", "{{}}")
+ .testStatement("'{{{{}}}}'.format(42)", "{{}}")
+
+ .testStatement("'{{0}}'.format(42)", "{0}")
+
+ .testStatement("'{{}}'.format(42)", "{}")
+ .testStatement("'{{{}}}'.format(42)", "{42}")
+ .testStatement("'{{ '.format(42)", "{ ")
+ .testStatement("' }}'.format(42)", " }")
+ .testStatement("'{{ {}'.format(42)", "{ 42")
+ .testStatement("'{} }}'.format(42)", "42 }")
+
+ .testStatement("'{{0}}'.format(42)", "{0}")
+ .testStatement("'{{{0}}}'.format(42)", "{42}")
+ .testStatement("'{{ 0'.format(42)", "{ 0")
+ .testStatement("'0 }}'.format(42)", "0 }")
+ .testStatement("'{{ {0}'.format(42)", "{ 42")
+ .testStatement("'{0} }}'.format(42)", "42 }")
+
+ .testStatement("'{{test}}'.format(test = 42)", "{test}")
+ .testStatement("'{{{test}}}'.format(test = 42)", "{42}")
+ .testStatement("'{{ test'.format(test = 42)", "{ test")
+ .testStatement("'test }}'.format(test = 42)", "test }")
+ .testStatement("'{{ {test}'.format(test = 42)", "{ 42")
+ .testStatement("'{test} }}'.format(test = 42)", "42 }")
+
+ .testIfErrorContains("Found '}' without matching '{'", "'{{}'.format(1)")
+ .testIfErrorContains("Found '}' without matching '{'", "'{}}'.format(1)");
+ }
+
+ @Test
+ public void testPyStringFormatManualPositionals() throws Exception {
+ new BothModesTest()
+ .testStatement(
+ "'{0}, {1} {2} {3} test'.format('hi', 'this', 'is', 'a')", "hi, this is a test")
+ .testStatement(
+ "'{3}, {2} {1} {0} test'.format('a', 'is', 'this', 'hi')", "hi, this is a test")
+ .testStatement(
+ "'skip some {0}'.format('arguments', 'obsolete', 'deprecated')", "skip some arguments")
+ .testStatement(
+ "'{0} can be reused: {0}'.format('this', 'obsolete')", "this can be reused: this");
+ }
+
+ @Test
+ public void testPyStringFormatManualPositionalsErrors() throws Exception {
+ new BothModesTest()
+ .testIfErrorContains("No replacement found for index 0", "'{0}'.format()")
+ .testIfErrorContains("No replacement found for index 1", "'{0} and {1}'.format('this')")
+ .testIfErrorContains(
+ "No replacement found for index 2", "'{0} and {2}'.format('this', 'that')")
+ .testIfErrorContains(
+ "No replacement found for index -1", "'{-0} and {-1}'.format('this', 'that')")
+ .testIfErrorContains(
+ "Invalid character ',' inside replacement field",
+ "'{0,1} and {1}'.format('this', 'that')")
+ .testIfErrorContains(
+ "Invalid character '.' inside replacement field",
+ "'{0.1} and {1}'.format('this', 'that')");
+ }
+
+ @Test
+ public void testPyStringFormatAutomaticPositionals() throws Exception {
+ new BothModesTest()
+ .testStatement("'{}, {} {} {} test'.format('hi', 'this', 'is', 'a')", "hi, this is a test")
+ .testStatement(
+ "'skip some {}'.format('arguments', 'obsolete', 'deprecated')", "skip some arguments");
+ }
+
+ @Test
+ public void testPyStringFormatAutomaticPositionalsError() throws Exception {
+ new BothModesTest()
+ .testIfErrorContains("No replacement found for index 0", "'{}'.format()")
+ .testIfErrorContains("No replacement found for index 1", "'{} and {}'.format('this')");
+ }
+
+ @Test
+ public void testPyStringFormatMixedFields() throws Exception {
+ new BothModesTest()
+ .testStatement("'{test} and {}'.format(2, test = 1)", "1 and 2")
+ .testStatement("'{test} and {0}'.format(2, test = 1)", "1 and 2")
+
+ .testIfErrorContains(
+ "non-keyword arg after keyword arg", "'{test} and {}'.format(test = 1, 2)")
+ .testIfErrorContains(
+ "non-keyword arg after keyword arg", "'{test} and {0}'.format(test = 1, 2)")
+
+ .testIfErrorContains(
+ "Cannot mix manual and automatic numbering of positional fields",
+ "'{} and {1}'.format(1, 2)")
+ .testIfErrorContains(
+ "Cannot mix manual and automatic numbering of positional fields",
+ "'{1} and {}'.format(1, 2)");
+ }
+
+ @Test
+ public void testPyStringFormatInvalidFields() throws Exception {
+ for (char unsupported : new char[] {'.', '[', ']', ','}) {
+ new BothModesTest().testIfErrorContains(
+ String.format("Invalid character '%c' inside replacement field", unsupported),
+ String.format("'{test%ctest}'.format(test = 1)", unsupported));
+ }
+
+ new BothModesTest().testIfErrorContains(
+ "Nested replacement fields are not supported", "'{ {} }'.format(42)");
+ }
+
+ @Test
+ public void testPyStringFormat() throws Exception {
+ new BothModesTest()
+ .testStatement("'abc'.format()", "abc")
+ .testStatement("'x{key}x'.format(key = 2)", "x2x")
+ .testStatement("'x{key}x'.format(key = 'abc')", "xabcx")
+ .testStatement("'{a}{b}{a}{b}'.format(a = 3, b = True)", "3True3True")
+ .testStatement("'{a}{b}{a}{b}'.format(a = 3, b = True)", "3True3True")
+ .testStatement("'{s1}{s2}'.format(s1 = ['a'], s2 = 'a')", "[\"a\"]a")
+
+ .testIfErrorContains("Missing argument 'b'", "'{a}{b}'.format(a = 5)")
+
+ .testStatement("'{a}'.format(a = '$')", "$")
+ .testStatement("'{a}'.format(a = '$a')", "$a")
+ .testStatement("'{a}$'.format(a = '$a')", "$a$");
+
+ // The test below is using **kwargs, which is not available in BUILD mode.
+ new SkylarkTest().testStatement("'{(}'.format(**{'(': 2})", "2");
+ }
+
+ @Test
+ public void testListSlice() throws Exception {
+ new BothModesTest()
+ .testEval("[0,1,2,3][0:-1]", "[0, 1, 2]")
+ .testEval("[0,1,2,3,4,5][2:4]", "[2, 3]")
+ .testEval("[0,1,2,3,4,5][-2:-1]", "[4]")
+ .testEval("[][1:2]", "[]")
+ .testEval("[1,2,3][1:0]", "[]")
+ .testEval("[0,1,2,3][-10:10]", "[0, 1, 2, 3]");
+ }
+
+ @Test
+ public void testListSort() throws Exception {
+ new BothModesTest()
+ .testEval("sorted([0,1,2,3])", "[0, 1, 2, 3]")
+ .testEval("sorted([])", "[]")
+ .testEval("sorted([3, 2, 1, 0])", "[0, 1, 2, 3]")
+ .testEval("sorted([[1], [], [2], [1, 2]])", "[[], [1], [1, 2], [2]]")
+ .testEval("sorted([True, False, True])", "[False, True, True]")
+ .testEval("sorted(['a','x','b','z'])", "[\"a\", \"b\", \"x\", \"z\"]")
+ .testEval("sorted([sorted, sorted])", "[sorted, sorted]")
+ .testEval("sorted({1: True, 5: True, 4: False})", "[1, 4, 5]");
+
+ new SkylarkTest() // set is available only in Skylark mode.
+ .testEval("sorted(set([1, 5, 4]))", "[1, 4, 5]");
+ }
+
+ @Test
+ public void testDictionaryWithMultipleKeys() throws Exception {
+ new BothModesTest().testStatement("{0: 'a', 1: 'b', 0: 'c'}[0]", "c");
+ }
+
+ @Test
+ public void testDictionaryKeyNotFound() throws Exception {
+ new BothModesTest()
+ .testIfErrorContains("Key \"0\" not found in dictionary", "{}['0']")
+ .testIfErrorContains("Key 0 not found in dictionary", "{'0': 1, 2: 3, 4: 5}[0]");
+ }
+
+ @Test
+ public void testListAccessBadIndex() throws Exception {
+ new BothModesTest().testIfErrorContains(
+ "expected value of type 'int' for index operand, but got \"a\" (string)",
+ "[[1], [2]]['a']");
+ }
+
+ @Test
+ public void testDictionaryAccess() throws Exception {
+ new BothModesTest().testEval("{1: ['foo']}[1]", "['foo']")
+ .testStatement("{'4': 8}['4']", 8)
+ .testStatement("{'a': 'aa', 'b': 'bb', 'c': 'cc'}['b']", "bb");
+ }
+
+ @Test
+ public void testDictionaryVariableAccess() throws Exception {
+ new BothModesTest().setUp("d = {'a' : 1}", "a = d['a']\n").testLookup("a", 1);
+ }
+
+ @Test
+ public void testStringIndexing() throws Exception {
+ new BothModesTest()
+ .testStatement("'somestring'[0]", "s")
+ .testStatement("'somestring'[1]", "o")
+ .testStatement("'somestring'[4]", "s")
+ .testStatement("'somestring'[9]", "g")
+ .testStatement("'somestring'[-1]", "g")
+ .testStatement("'somestring'[-2]", "n")
+ .testStatement("'somestring'[-10]", "s");
+ }
+
+ @Test
+ public void testStringIndexingOutOfRange() throws Exception {
+ new BothModesTest()
+ .testIfErrorContains("List index out of range", "'abcdef'[10]")
+ .testIfErrorContains("List index out of range", "'abcdef'[-11]")
+ .testIfErrorContains("List index out of range", "'abcdef'[42]");
+ }
+
+ @Test
+ public void testDictionaryCreation() throws Exception {
+ String expected = "{'a': 1, 'b': 2, 'c': 3}";
+
+ new BothModesTest()
+ .testEval("dict([('a', 1), ('b', 2), ('c', 3)])", expected)
+ .testEval("dict(a = 1, b = 2, c = 3)", expected)
+ .testEval("dict([('a', 1)], b = 2, c = 3)", expected);
+ }
+
+ @Test
+ public void testDictionaryCreationInnerLists() throws Exception {
+ new BothModesTest().testEval("dict([[1, 2], [3, 4]], a = 5)", "{1: 2, 3: 4, 'a': 5}");
+ }
+
+ @Test
+ public void testDictionaryCreationEmpty() throws Exception {
+ new BothModesTest()
+ .testEval("dict()", "{}")
+ .testEval("dict([])", "{}");
+ }
+
+ @Test
+ public void testDictionaryCreationDifferentKeyTypes() throws Exception {
+ String expected = "{'a': 1, 2: 3}";
+
+ new BothModesTest()
+ .testEval("dict([('a', 1), (2, 3)])", expected)
+ .testEval("dict([(2, 3)], a = 1)", expected);
+ }
+
+ @Test
+ public void testDictionaryCreationKeyCollision() throws Exception {
+ String expected = "{'a': 1, 'b': 2, 'c': 3}";
+
+ new BothModesTest()
+ .testEval("dict([('a', 42), ('b', 2), ('a', 1), ('c', 3)])", expected)
+ .testEval("dict([('a', 42)], a = 1, b = 2, c = 3)", expected);
+ new SkylarkTest()
+ .testEval("dict([('a', 42)], **{'a': 1, 'b': 2, 'c': 3})", expected);
+ }
+
+ @Test
+ public void testDictionaryCreationInvalidPositional() throws Exception {
+ String unexpectedString =
+ "expected value of type 'list(object)' for dict(args), but got \"a\" (string)";
+
+ new BothModesTest()
+ .testIfErrorContains(
+ "Method dict(args: Iterable, **kwargs) is not applicable for arguments "
+ + "(string, dict): 'args' is string, but should be Iterable",
+ "dict('a')")
+ .testIfErrorContains(unexpectedString, "dict(['a'])")
+ .testIfErrorContains(unexpectedString, "dict([('a')])")
+ .testIfErrorContains("too many (3) positional arguments", "dict((3,4), (3,2), (1,2))")
+ .testIfErrorContains(
+ "Tuple has length 3, but exactly two elements are required",
+ "dict([('a', 'b', 'c')])");
+ }
+
+ @Test
+ public void testDictionaryValues() throws Exception {
+ new BothModesTest()
+ .testEval("{1: 'foo'}.values()", "['foo']")
+ .testEval("{}.values()", "[]")
+ .testEval("{True: 3, False: 5}.values()", "[5, 3]")
+ .testEval("{'a': 5, 'c': 2, 'b': 4, 'd': 3}.values()", "[5, 4, 2, 3]");
+ // sorted by keys
+ }
+
+ @Test
+ public void testDictionaryKeys() throws Exception {
+ new BothModesTest()
+ .testEval("{1: 'foo'}.keys()", "[1]")
+ .testEval("{}.keys()", "[]")
+ .testEval("{True: 3, False: 5}.keys()", "[False, True]")
+ .testEval(
+ "{1:'a', 2:'b', 6:'c', 0:'d', 5:'e', 4:'f', 3:'g'}.keys()", "[0, 1, 2, 3, 4, 5, 6]");
+ }
+
+ @Test
+ public void testDictionaryGet() throws Exception {
+ new BuildTest()
+ .testStatement("{1: 'foo'}.get(1)", "foo")
+ .testStatement("{1: 'foo'}.get(2)", Environment.NONE)
+ .testStatement("{1: 'foo'}.get(2, 'a')", "a")
+ .testStatement("{1: 'foo'}.get(2, default='a')", "a")
+ .testStatement("{1: 'foo'}.get(2, default=None)", Environment.NONE);
+ }
+
+ @Test
+ public void testDictionaryItems() throws Exception {
+ new BothModesTest()
+ .testEval("{'a': 'foo'}.items()", "[('a', 'foo')]")
+ .testEval("{}.items()", "[]")
+ .testEval("{1: 3, 2: 5}.items()", "[(1, 3), (2, 5)]")
+ .testEval("{'a': 5, 'c': 2, 'b': 4}.items()", "[('a', 5), ('b', 4), ('c', 2)]");
+ }
+
+ @Test
+ public void testSetUnionWithList() throws Exception {
+ evaluateSet("set([]).union(['a', 'b', 'c'])", "a", "b", "c");
+ evaluateSet("set(['a']).union(['b', 'c'])", "a", "b", "c");
+ evaluateSet("set(['a', 'b']).union(['c'])", "a", "b", "c");
+ evaluateSet("set(['a', 'b', 'c']).union([])", "a", "b", "c");
+ }
+
+ @Test
+ public void testSetUnionWithSet() throws Exception {
+ evaluateSet("set([]).union(set(['a', 'b', 'c']))", "a", "b", "c");
+ evaluateSet("set(['a']).union(set(['b', 'c']))", "a", "b", "c");
+ evaluateSet("set(['a', 'b']).union(set(['c']))", "a", "b", "c");
+ evaluateSet("set(['a', 'b', 'c']).union(set([]))", "a", "b", "c");
+ }
+
+ @Test
+ public void testSetUnionDuplicates() throws Exception {
+ evaluateSet("set(['a', 'b', 'c']).union(['a', 'b', 'c'])", "a", "b", "c");
+ evaluateSet("set(['a', 'a', 'a']).union(['a', 'a'])", "a");
+
+ evaluateSet("set(['a', 'b', 'c']).union(set(['a', 'b', 'c']))", "a", "b", "c");
+ evaluateSet("set(['a', 'a', 'a']).union(set(['a', 'a']))", "a");
+ }
+
+ @Test
+ public void testSetUnionError() throws Exception {
+ new SkylarkTest()
+ .testIfErrorContains("insufficient arguments received by union", "set(['a']).union()")
+ .testIfErrorContains(
+ "Method set.union(newElements: Iterable) is not applicable for arguments (string): "
+ + "'newElements' is string, but should be Iterable",
+ "set(['a']).union('b')");
+ }
+
+ @Test
+ public void testSetUnionSideEffects() throws Exception {
+ eval("def func():",
+ " n1 = set(['a'])",
+ " n2 = n1.union(['b'])",
+ " return n1",
+ "n = func()");
+ assertEquals(ImmutableList.of("a"), ((SkylarkNestedSet) lookup("n")).toCollection());
+ }
+
+ private void evaluateSet(String statement, Object... expectedElements) throws Exception {
+ new SkylarkTest().testCollection(statement, expectedElements);
+ }
+
+ @Test
+ public void testListIndex() throws Exception {
+ new BothModesTest()
+ .testStatement("['a', 'b', 'c', 'd'][0]", "a")
+ .testStatement("['a', 'b', 'c', 'd'][1]", "b")
+ .testStatement("['a', 'b', 'c', 'd'][-1]", "d")
+ .testStatement("['a', 'b', 'c', 'd'][-2]", "c")
+ .testStatement("[0, 1, 2][-3]", 0)
+ .testStatement("[0, 1, 2][-2]", 1)
+ .testStatement("[0, 1, 2][-1]", 2)
+ .testStatement("[0, 1, 2][0]", 0);
+ }
+
+ @Test
+ public void testListIndexOutOfRange() throws Exception {
+ new BothModesTest()
+ .testIfErrorContains("List index out of range", "[0, 1, 2][3]")
+ .testIfErrorContains("List index out of range", "[0, 1, 2][-4]")
+ .testIfErrorContains("List is empty", "[][0]")
+ .testIfErrorContains("List index out of range", "[0][-2]")
+ .testIfErrorContains("List index out of range", "[0][1]");
+ }
+
+ @Test
+ public void testRange() throws Exception {
+ new BothModesTest()
+ .testStatement("str(range(5))", "[0, 1, 2, 3, 4]")
+ .testStatement("str(range(0))", "[]")
+ .testStatement("str(range(1))", "[0]")
+ .testStatement("str(range(-2))", "[]")
+
+ .testStatement("str(range(-3, 2))", "[-3, -2, -1, 0, 1]")
+ .testStatement("str(range(3, 2))", "[]")
+ .testStatement("str(range(3, 3))", "[]")
+ .testStatement("str(range(3, 4))", "[3]")
+ .testStatement("str(range(3, 5))", "[3, 4]")
+
+ .testStatement("str(range(-3, 5, 2))", "[-3, -1, 1, 3]")
+ .testStatement("str(range(-3, 6, 2))", "[-3, -1, 1, 3, 5]")
+ .testStatement("str(range(5, 0, -1))", "[5, 4, 3, 2, 1]")
+ .testStatement("str(range(5, 0, -10))", "[5]")
+ .testStatement("str(range(0, -3, -2))", "[0, -2]")
+ .testIfErrorContains("step cannot be 0", "range(2, 3, 0)");
+ }
+
+ @Test
+ public void testEnumerate() throws Exception {
+ new BothModesTest()
+ .testStatement("str(enumerate([]))", "[]")
+ .testStatement("str(enumerate([5]))", "[(0, 5)]")
+ .testStatement("str(enumerate([5, 3]))", "[(0, 5), (1, 3)]")
+ .testStatement("str(enumerate(['a', 'b', 'c']))", "[(0, \"a\"), (1, \"b\"), (2, \"c\")]")
+ .testStatement("str(enumerate(['a']) + [(1, 'b')])", "[(0, \"a\"), (1, \"b\")]");
+ }
+
+ @Test
+ public void testEnumerateBadArg() throws Exception {
+ new BothModesTest().testIfErrorContains(
+ "expected List or list for 'list' while calling enumerate but got string instead: a",
+ "enumerate('a')");
+ }
+
+ @Test
+ public void testPyListAppend() throws Exception {
+ new BuildTest()
+ .setUp("FOO = ['a', 'b']", "FOO.append('c')")
+ .testLookup("FOO", Arrays.asList("a", "b", "c"))
+ .testIfErrorContains(
+ "function 'append' is not defined on object of type 'Tuple'", "(1, 2).append(3)");
+ }
+
+ @Test
+ public void testPyListExtend() throws Exception {
+ new BuildTest()
+ .setUp("FOO = ['a', 'b']", "FOO.extend(['c', 'd'])")
+ .testLookup("FOO", Arrays.asList("a", "b", "c", "d"))
+ .testIfErrorContains(
+ "function 'extend' is not defined on object of type 'Tuple'", "(1, 2).extend([3, 4])");
+ }
+
+ @Test
+ public void testReassignmentOfPrimitivesNotForbiddenByCoreLanguage() throws Exception {
+ new BuildTest()
+ .setUp("cc_binary = (['hello.cc'])")
+ .testIfErrorContains(
+ "'List' object is not callable",
+ "cc_binary(name = 'hello', srcs=['hello.cc'], malloc = '//base:system_malloc')");
+ }
+
+ @Test
+ public void testLenOnString() throws Exception {
+ new BothModesTest().testStatement("len('abc')", 3);
+ }
+
+ @Test
+ public void testLenOnList() throws Exception {
+ new BothModesTest().testStatement("len([1,2,3])", 3);
+ }
+
+ @Test
+ public void testLenOnDict() throws Exception {
+ new BothModesTest().testStatement("len({'a' : 1, 'b' : 2})", 2);
+ }
+
+ @Test
+ public void testLenOnBadType() throws Exception {
+ new BothModesTest().testIfErrorContains("int is not iterable", "len(1)");
+ }
+
+ @Test
+ public void testIndexOnFunction() throws Exception {
+ new BuildTest()
+ .testIfErrorContains("operator [] is not defined on object of type 'function'", "len[1]")
+ .testIfErrorContains(
+ "operator [:] is not defined on object of type 'function'", "len[1:4]");
+ }
+
+ @Test
+ public void testStrip() throws Exception {
+ new BothModesTest()
+ .testStatement("' abc\t'.strip()", "abc")
+ .testStatement("'abc '.strip()", "abc");
+ }
+
+ @Test
+ public void testBool() throws Exception {
+ new BothModesTest()
+ .testStatement("bool(1)", Boolean.TRUE)
+ .testStatement("bool(0)", Boolean.FALSE)
+ .testStatement("bool([1, 2])", Boolean.TRUE)
+ .testStatement("bool([])", Boolean.FALSE)
+ .testStatement("bool(None)", Boolean.FALSE);
+ }
+
+ @Test
+ public void testStr() throws Exception {
+ new BothModesTest()
+ .testStatement("str(1)", "1")
+ .testStatement("str(-2)", "-2")
+ .testStatement("str([1, 2])", "[1, 2]")
+ .testStatement("str(True)", "True")
+ .testStatement("str(False)", "False")
+ .testStatement("str(None)", "None")
+ .testStatement("str(str)", "<function str>");
+
+ new SkylarkTest()
+ .testStatement("str(struct(x = 2, y = 3, z = 4))", "struct(x = 2, y = 3, z = 4)");
+ }
+
+ @Test
+ public void testInt() throws Exception {
+ new BothModesTest()
+ .testStatement("int('1')", 1)
+ .testStatement("int('-1234')", -1234)
+ .testIfErrorContains("invalid literal for int(): \"1.5\"", "int('1.5')")
+ .testIfErrorContains("invalid literal for int(): \"ab\"", "int('ab')")
+ .testStatement("int(42)", 42)
+ .testStatement("int(-1)", -1)
+ .testStatement("int(True)", 1)
+ .testStatement("int(False)", 0)
+ .testIfErrorContains("None is not of type string or int or bool", "int(None)");
+ }
+
+ @Test
+ public void testStrFunction() throws Exception {
+ new SkylarkTest().testStatement("def foo(x): return x\nstr(foo)", "<function foo>");
+ }
+
+ @Test
+ public void testType() throws Exception {
+ new SkylarkTest()
+ .testStatement("type(1)", "int")
+ .testStatement("type('a')", "string")
+ .testStatement("type([1, 2])", "list")
+ .testStatement("type((1, 2))", "tuple")
+ .testStatement("type(True)", "bool")
+ .testStatement("type(None)", "NoneType")
+ .testStatement("type(str)", "function");
+ }
+
+ @Test
+ public void testSelectFunction() throws Exception {
+ enableSkylarkMode();
+ eval("a = select({'a': 1})");
+ SelectorList result = (SelectorList) lookup("a");
+ assertThat(((SelectorValue) Iterables.getOnlyElement(result.getElements())).getDictionary())
+ .isEqualTo(ImmutableMap.of("a", 1));
+ }
+
+ @Test
+ public void testCountFunction() throws Exception {
+ new BothModesTest()
+ .testStatement("'abc'.count('')", 4)
+ .testStatement("'abc'.count('a')", 1)
+ .testStatement("'abc'.count('b')", 1)
+ .testStatement("'abc'.count('c')", 1)
+ .testStatement("'abbc'.count('b')", 2)
+ .testStatement("'aba'.count('a')", 2)
+ .testStatement("'aaa'.count('aa')", 1)
+ .testStatement("'aaaa'.count('aa')", 2)
+ .testStatement("'abc'.count('a', 0)", 1)
+ .testStatement("'abc'.count('a', 1)", 0)
+ .testStatement("'abc'.count('c', 0, 3)", 1)
+ .testStatement("'abc'.count('c', 0, 2)", 0)
+ .testStatement("'abc'.count('a', -1)", 0)
+ .testStatement("'abc'.count('c', -1)", 1)
+ .testStatement("'abc'.count('c', 0, 5)", 1)
+ .testStatement("'abc'.count('c', 0, -1)", 0)
+ .testStatement("'abc'.count('a', 0, -1)", 1);
+ }
+
+ @Test
+ public void testZipFunction() throws Exception {
+ new BothModesTest()
+ .testStatement("str(zip())", "[]")
+ .testStatement("str(zip([1, 2]))", "[(1,), (2,)]")
+ .testStatement("str(zip([1, 2], ['a', 'b']))", "[(1, \"a\"), (2, \"b\")]")
+ .testStatement("str(zip([1, 2, 3], ['a', 'b']))", "[(1, \"a\"), (2, \"b\")]")
+ .testStatement("str(zip([1], [2], [3]))", "[(1, 2, 3)]")
+ .testStatement("str(zip([1], {2: 'a'}))", "[(1, 2)]")
+ .testStatement("str(zip([1], []))", "[]")
+ .testIfErrorContains("type 'int' is not iterable", "zip(123)")
+ .testIfErrorContains("type 'int' is not iterable", "zip([1], 1)");
+
+ new SkylarkTest() // because of set
+ .testStatement("str(zip([1], set([2])))", "[(1, 2)]");
+ }
+}