From b5c3b3a1479f552c2ba2a844a84107efe16e8c50 Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Wed, 26 Aug 2015 13:37:30 +0000 Subject: Import MethodLibraryTest.java into bazel -- MOS_MIGRATED_REVID=101570634 --- .../build/lib/syntax/MethodLibraryTest.java | 1012 ++++++++++++++++++++ 1 file changed, 1012 insertions(+) create mode 100644 src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java (limited to 'src/test/java/com/google') 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)", ""); + + 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)", ""); + } + + @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)]"); + } +} -- cgit v1.2.3