blob: 4396608f66e69bf1e74cf40bb3a6b13f4b7a72df [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Ulf Adams89f012d2015-02-26 13:39:28 +00002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.syntax;
15
16import static com.google.common.truth.Truth.assertThat;
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000017import static org.junit.Assert.assertEquals;
18import static org.junit.Assert.assertFalse;
19import static org.junit.Assert.assertTrue;
Ulf Adams89f012d2015-02-26 13:39:28 +000020
Ulf Adams89f012d2015-02-26 13:39:28 +000021import com.google.common.collect.ImmutableMap;
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +000022import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
23import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000024import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
Florian Weikert28da3652015-07-01 14:52:30 +000025import com.google.devtools.build.lib.testutil.TestMode;
Ulf Adams89f012d2015-02-26 13:39:28 +000026
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000027import org.junit.Before;
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000028import org.junit.Test;
29import org.junit.runner.RunWith;
30import org.junit.runners.JUnit4;
31
Ulf Adams89f012d2015-02-26 13:39:28 +000032import java.util.Collections;
33import java.util.List;
34import java.util.Map;
35
36/**
37 * Test of evaluation behavior. (Implicitly uses lexer + parser.)
38 */
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000039@RunWith(JUnit4.class)
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000040public class EvaluationTest extends EvaluationTestCase {
Florian Weikertb4c59042015-12-01 10:47:18 +000041
Han-Wen Nienhuys33ce2112015-09-25 14:25:38 +000042 @Before
Florian Weikertb4c59042015-12-01 10:47:18 +000043 public final void setBuildMode() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +000044 super.setMode(TestMode.BUILD);
45 }
Ulf Adams89f012d2015-02-26 13:39:28 +000046
Florian Weikert28da3652015-07-01 14:52:30 +000047 /**
48 * Creates a new instance of {@code ModalTestCase}.
49 *
50 * <p>If a test uses this method, it allows potential subclasses to run the very same test in a
51 * different mode in subclasses
52 */
53 protected ModalTestCase newTest() {
54 return new BuildTest();
Ulf Adams89f012d2015-02-26 13:39:28 +000055 }
56
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000057 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000058 public void testExprs() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +000059 newTest()
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000060 .testStatement("'%sx' % 'foo' + 'bar1'", "fooxbar1")
61 .testStatement("('%sx' % 'foo') + 'bar2'", "fooxbar2")
62 .testStatement("'%sx' % ('foo' + 'bar3')", "foobar3x")
Florian Weikert28da3652015-07-01 14:52:30 +000063 .testStatement("123 + 456", 579)
64 .testStatement("456 - 123", 333)
65 .testStatement("8 % 3", 2)
66 .testIfErrorContains("unsupported operand type(s) for %: 'int' and 'string'", "3 % 'foo'");
Ulf Adams89f012d2015-02-26 13:39:28 +000067 }
68
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000069 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000070 public void testListExprs() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +000071 newTest().testExactOrder("[1, 2, 3]", 1, 2, 3).testExactOrder("(1, 2, 3)", 1, 2, 3);
Ulf Adams89f012d2015-02-26 13:39:28 +000072 }
73
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000074 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000075 public void testStringFormatMultipleArgs() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +000076 newTest().testStatement("'%sY%s' % ('X', 'Z')", "XYZ");
Ulf Adams89f012d2015-02-26 13:39:28 +000077 }
78
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000079 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000080 public void testAndOr() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +000081 new BuildTest()
82 .testStatement("8 or 9", 8)
83 .testStatement("0 or 9", 9)
84 .testStatement("8 and 9", 9)
85 .testStatement("0 and 9", 0)
Ulf Adams89f012d2015-02-26 13:39:28 +000086
Florian Weikert28da3652015-07-01 14:52:30 +000087 .testStatement("1 and 2 or 3", 2)
88 .testStatement("0 and 2 or 3", 3)
89 .testStatement("1 and 0 or 3", 3)
Ulf Adams89f012d2015-02-26 13:39:28 +000090
Florian Weikert28da3652015-07-01 14:52:30 +000091 .testStatement("1 or 2 and 3", 1)
92 .testStatement("0 or 2 and 3", 3)
93 .testStatement("0 or 0 and 3", 0)
94 .testStatement("1 or 0 and 3", 1)
Ulf Adams89f012d2015-02-26 13:39:28 +000095
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000096 .testStatement("None and 1", Runtime.NONE)
Florian Weikert28da3652015-07-01 14:52:30 +000097 .testStatement("\"\" or 9", 9)
98 .testStatement("\"abc\" or 9", "abc")
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000099
Florian Weikert28da3652015-07-01 14:52:30 +0000100 // check that 'foo' is not evaluated
101 .testStatement("8 or foo", 8)
102 .testStatement("0 and foo", 0);
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000103
Florian Weikert28da3652015-07-01 14:52:30 +0000104 new SkylarkTest()
105 .testIfErrorContains("name 'google' is not defined", "0 and google")
106 .testIfErrorContains("name 'google' is not defined", "8 or google");
Ulf Adams89f012d2015-02-26 13:39:28 +0000107 }
108
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000109 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000110 public void testNot() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000111 newTest().testStatement("not 1", false).testStatement("not ''", true);
Ulf Adams89f012d2015-02-26 13:39:28 +0000112 }
113
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000114 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000115 public void testNotWithLogicOperators() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000116 newTest()
117 .testStatement("not (0 and 0)", true)
118 .testStatement("not (1 or 0)", false)
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000119
Florian Weikert28da3652015-07-01 14:52:30 +0000120 .testStatement("0 and not 0", 0)
121 .testStatement("not 0 and 0", 0)
Ulf Adams89f012d2015-02-26 13:39:28 +0000122
Florian Weikert28da3652015-07-01 14:52:30 +0000123 .testStatement("1 and not 0", true)
124 .testStatement("not 0 or 0", true)
Ulf Adams89f012d2015-02-26 13:39:28 +0000125
Florian Weikert28da3652015-07-01 14:52:30 +0000126 .testStatement("not 1 or 0", 0)
127 .testStatement("not 1 or 1", 1);
Ulf Adams89f012d2015-02-26 13:39:28 +0000128 }
129
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000130 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000131 public void testNotWithArithmeticOperators() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000132 newTest().testStatement("not 0 + 0", true).testStatement("not 2 - 1", false);
Ulf Adams89f012d2015-02-26 13:39:28 +0000133 }
134
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000135 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000136 public void testNotWithCollections() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000137 newTest().testStatement("not []", true).testStatement("not {'a' : 1}", false);
Ulf Adams89f012d2015-02-26 13:39:28 +0000138 }
139
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000140 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000141 public void testEquality() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000142 newTest()
143 .testStatement("1 == 1", true)
144 .testStatement("1 == 2", false)
145 .testStatement("'hello' == 'hel' + 'lo'", true)
146 .testStatement("'hello' == 'bye'", false)
147 .testStatement("None == None", true)
148 .testStatement("[1, 2] == [1, 2]", true)
149 .testStatement("[1, 2] == [2, 1]", false);
Ulf Adams89f012d2015-02-26 13:39:28 +0000150 }
151
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000152 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000153 public void testInequality() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000154 newTest()
155 .testStatement("1 != 1", false)
156 .testStatement("1 != 2", true)
157 .testStatement("'hello' != 'hel' + 'lo'", false)
158 .testStatement("'hello' != 'bye'", true)
159 .testStatement("[1, 2] != [1, 2]", false)
160 .testStatement("[1, 2] != [2, 1]", true);
Ulf Adams89f012d2015-02-26 13:39:28 +0000161 }
162
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000163 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000164 public void testEqualityPrecedence() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000165 newTest()
166 .testStatement("1 + 3 == 2 + 2", true)
167 .testStatement("not 1 == 2", true)
168 .testStatement("not 1 != 2", false)
169 .testStatement("2 and 3 == 3 or 1", true)
170 .testStatement("2 or 3 == 3 and 1", 2);
Ulf Adams89f012d2015-02-26 13:39:28 +0000171 }
172
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000173 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000174 public void testLessThan() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000175 newTest()
176 .testStatement("1 <= 1", true)
177 .testStatement("1 < 1", false)
178 .testStatement("'a' <= 'b'", true)
179 .testStatement("'c' < 'a'", false);
Ulf Adams89f012d2015-02-26 13:39:28 +0000180 }
181
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000182 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000183 public void testGreaterThan() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000184 newTest()
185 .testStatement("1 >= 1", true)
186 .testStatement("1 > 1", false)
187 .testStatement("'a' >= 'b'", false)
188 .testStatement("'c' > 'a'", true);
Ulf Adams89f012d2015-02-26 13:39:28 +0000189 }
190
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000191 @Test
Francois-Rene Rideau6fc5ee72015-03-12 20:55:17 +0000192 public void testConditionalExpressions() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000193 newTest()
194 .testStatement("1 if True else 2", 1)
195 .testStatement("1 if False else 2", 2)
196 .testStatement("1 + 2 if 3 + 4 else 5 + 6", 3);
Francois-Rene Rideau6fc5ee72015-03-12 20:55:17 +0000197
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000198 setFailFast(false);
199 parseExpression("1 if 2");
Ulf Adamsc708f962015-10-22 12:02:28 +0000200 assertContainsError(
Francois-Rene Rideau6fc5ee72015-03-12 20:55:17 +0000201 "missing else clause in conditional expression or semicolon before if");
Francois-Rene Rideau6fc5ee72015-03-12 20:55:17 +0000202 }
203
204 @Test
Laurent Le Brunac8aae82015-04-16 11:42:55 +0000205 public void testListComparison() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000206 newTest()
207 .testStatement("[] < [1]", true)
208 .testStatement("[1] < [1, 1]", true)
209 .testStatement("[1, 1] < [1, 2]", true)
210 .testStatement("[1, 2] < [1, 2, 3]", true)
211 .testStatement("[1, 2, 3] <= [1, 2, 3]", true)
Laurent Le Brunac8aae82015-04-16 11:42:55 +0000212
Florian Weikert28da3652015-07-01 14:52:30 +0000213 .testStatement("['a', 'b'] > ['a']", true)
214 .testStatement("['a', 'b'] >= ['a']", true)
215 .testStatement("['a', 'b'] < ['a']", false)
216 .testStatement("['a', 'b'] <= ['a']", false)
Laurent Le Brunac8aae82015-04-16 11:42:55 +0000217
Florian Weikert28da3652015-07-01 14:52:30 +0000218 .testStatement("('a', 'b') > ('a', 'b')", false)
219 .testStatement("('a', 'b') >= ('a', 'b')", true)
220 .testStatement("('a', 'b') < ('a', 'b')", false)
221 .testStatement("('a', 'b') <= ('a', 'b')", true)
Laurent Le Brunac8aae82015-04-16 11:42:55 +0000222
Florian Weikert28da3652015-07-01 14:52:30 +0000223 .testStatement("[[1, 1]] > [[1, 1], []]", false)
Florian Weikertf31b9472015-08-04 16:36:58 +0000224 .testStatement("[[1, 1]] < [[1, 1], []]", true);
Ulf Adams89f012d2015-02-26 13:39:28 +0000225 }
226
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000227 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000228 public void testSumFunction() throws Exception {
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000229 BaseFunction sum = new BaseFunction("sum") {
230 @Override
231 public Object call(List<Object> args, Map<String, Object> kwargs,
232 FuncallExpression ast, Environment env) {
233 int sum = 0;
234 for (Object arg : args) {
235 sum += (Integer) arg;
Ulf Adams89f012d2015-02-26 13:39:28 +0000236 }
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000237 return sum;
238 }
239 };
Ulf Adams89f012d2015-02-26 13:39:28 +0000240
Florian Weikert28da3652015-07-01 14:52:30 +0000241 newTest().update(sum.getName(), sum).testStatement("sum(1, 2, 3, 4, 5, 6)", 21)
242 .testStatement("sum", sum).testStatement("sum(a=1, b=2)", 0);
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000243 }
Ulf Adams89f012d2015-02-26 13:39:28 +0000244
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000245 @Test
246 public void testNotCallInt() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000247 newTest().setUp("sum = 123456").testLookup("sum", 123456)
248 .testIfExactError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)")
249 .testStatement("sum", 123456);
Ulf Adams89f012d2015-02-26 13:39:28 +0000250 }
251
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000252 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000253 public void testKeywordArgs() throws Exception {
254
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000255 // This function returns the map of keyword arguments passed to it.
256 BaseFunction kwargs = new BaseFunction("kwargs") {
257 @Override
258 public Object call(List<Object> args,
259 final Map<String, Object> kwargs,
260 FuncallExpression ast,
261 Environment env) {
Francois-Rene Rideauc0a8c582016-01-28 18:36:22 +0000262 return SkylarkDict.copyOf(env, kwargs);
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000263 }
264 };
Ulf Adams89f012d2015-02-26 13:39:28 +0000265
Florian Weikert28da3652015-07-01 14:52:30 +0000266 newTest()
267 .update(kwargs.getName(), kwargs)
268 .testEval(
269 "kwargs(foo=1, bar='bar', wiz=[1,2,3]).items()",
270 "[('bar', 'bar'), ('foo', 1), ('wiz', [1, 2, 3])]");
Ulf Adams89f012d2015-02-26 13:39:28 +0000271 }
272
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000273 @Test
Laurent Le Brunbd716742015-04-15 11:05:03 +0000274 public void testModulo() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000275 newTest()
276 .testStatement("6 % 2", 0)
277 .testStatement("6 % 4", 2)
278 .testStatement("3 % 6", 3)
279 .testStatement("7 % -4", -1)
280 .testStatement("-7 % 4", 1)
281 .testStatement("-7 % -4", -3)
282 .testIfExactError("integer modulo by zero", "5 % 0");
Laurent Le Brunbd716742015-04-15 11:05:03 +0000283 }
284
285 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000286 public void testMult() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000287 newTest()
288 .testStatement("6 * 7", 42)
289 .testStatement("3 * 'ab'", "ababab")
290 .testStatement("0 * 'ab'", "")
291 .testStatement("'1' + '0' * 5", "100000");
Ulf Adams89f012d2015-02-26 13:39:28 +0000292 }
293
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000294 @Test
Laurent Le Brun8a528262015-04-15 14:23:35 +0000295 public void testDivision() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000296 newTest()
297 .testStatement("6 / 2", 3)
298 .testStatement("6 / 4", 1)
299 .testStatement("3 / 6", 0)
300 .testStatement("7 / -2", -4)
301 .testStatement("-7 / 2", -4)
302 .testStatement("-7 / -2", 3)
303 .testStatement("2147483647 / 2", 1073741823)
Laurent Le Brun7bda87e2015-08-24 15:13:53 +0000304 .testIfErrorContains("unsupported operand type(s) for /: 'string' and 'int'", "'str' / 2")
Florian Weikert28da3652015-07-01 14:52:30 +0000305 .testIfExactError("integer division by zero", "5 / 0");
Laurent Le Brun8a528262015-04-15 14:23:35 +0000306 }
307
308 @Test
309 public void testOperatorPrecedence() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000310 newTest()
311 .testStatement("2 + 3 * 4", 14)
312 .testStatement("2 + 3 / 4", 2)
313 .testStatement("2 * 3 + 4 / -2", 4);
Laurent Le Brun8a528262015-04-15 14:23:35 +0000314 }
315
316 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000317 public void testConcatStrings() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000318 newTest().testStatement("'foo' + 'bar'", "foobar");
Ulf Adams89f012d2015-02-26 13:39:28 +0000319 }
320
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000321 @SuppressWarnings("unchecked")
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000322 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000323 public void testConcatLists() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000324 // TODO(fwe): cannot be handled by current testing suite
Ulf Adams89f012d2015-02-26 13:39:28 +0000325 // list
326 Object x = eval("[1,2] + [3,4]");
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000327 assertThat((Iterable<Object>) x).containsExactly(1, 2, 3, 4).inOrder();
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000328 assertEquals(MutableList.of(env, 1, 2, 3, 4), x);
Ulf Adams89f012d2015-02-26 13:39:28 +0000329 assertFalse(EvalUtils.isImmutable(x));
330
331 // tuple
332 x = eval("(1,2) + (3,4)");
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000333 assertEquals(Tuple.of(1, 2, 3, 4), x);
Ulf Adams89f012d2015-02-26 13:39:28 +0000334 assertTrue(EvalUtils.isImmutable(x));
335
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000336 checkEvalError("unsupported operand type(s) for +: 'tuple' and 'list'",
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000337 "(1,2) + [3,4]"); // list + tuple
Ulf Adams89f012d2015-02-26 13:39:28 +0000338 }
339
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000340 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000341 public void testListComprehensions() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000342 newTest()
343 .testExactOrder("['foo/%s.java' % x for x in []]")
344 .testExactOrder("['foo/%s.java' % y for y in ['bar', 'wiz', 'quux']]", "foo/bar.java",
345 "foo/wiz.java", "foo/quux.java")
346 .testExactOrder("['%s/%s.java' % (z, t) " + "for z in ['foo', 'bar'] "
347 + "for t in ['baz', 'wiz', 'quux']]",
348 "foo/baz.java",
349 "foo/wiz.java",
350 "foo/quux.java",
351 "bar/baz.java",
352 "bar/wiz.java",
353 "bar/quux.java")
354 .testExactOrder("['%s/%s.java' % (b, b) " + "for a in ['foo', 'bar'] "
355 + "for b in ['baz', 'wiz', 'quux']]",
356 "baz/baz.java",
357 "wiz/wiz.java",
358 "quux/quux.java",
359 "baz/baz.java",
360 "wiz/wiz.java",
361 "quux/quux.java")
362 .testExactOrder("['%s/%s.%s' % (c, d, e) " + "for c in ['foo', 'bar'] "
363 + "for d in ['baz', 'wiz', 'quux'] " + "for e in ['java', 'cc']]",
364 "foo/baz.java",
365 "foo/baz.cc",
366 "foo/wiz.java",
367 "foo/wiz.cc",
368 "foo/quux.java",
369 "foo/quux.cc",
370 "bar/baz.java",
371 "bar/baz.cc",
372 "bar/wiz.java",
373 "bar/wiz.cc",
374 "bar/quux.java",
375 "bar/quux.cc");
Ulf Adams89f012d2015-02-26 13:39:28 +0000376 }
377
Laurent Le Brun741824b2015-03-20 15:10:19 +0000378 @Test
Laurent Le Brun52021662015-05-18 09:28:26 +0000379 public void testNestedListComprehensions() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000380 newTest().testExactOrder("li = [[1, 2], [3, 4]]\n" + "[j for i in li for j in i]", 1, 2,
381 3, 4).testExactOrder("input = [['abc'], ['def', 'ghi']]\n"
382 + "['%s %s' % (b, c) for a in input for b in a for c in b]",
383 "abc a",
384 "abc b",
385 "abc c",
386 "def d",
387 "def e",
388 "def f",
389 "ghi g",
390 "ghi h",
391 "ghi i");
Laurent Le Brun52021662015-05-18 09:28:26 +0000392 }
393
394 @Test
Laurent Le Brun741824b2015-03-20 15:10:19 +0000395 public void testListComprehensionsMultipleVariables() throws Exception {
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000396 newTest().testEval("[x + y for x, y in [(1, 2), (3, 4)]]", "[3, 7]")
397 .testEval("[z + t for (z, t) in [[1, 2], [3, 4]]]", "[3, 7]");
Laurent Le Brun741824b2015-03-20 15:10:19 +0000398 }
399
400 @Test
401 public void testListComprehensionsMultipleVariablesFail() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000402 newTest().testIfExactError("lvalue has length 3, but rvalue has has length 2",
403 "[x + y for x, y, z in [(1, 2), (3, 4)]]").testIfExactError(
404 "type 'int' is not a collection", "[x + y for x, y in (1, 2)]");
Laurent Le Brun741824b2015-03-20 15:10:19 +0000405 }
406
407 @Test
Laurent Le Brunb4c54742015-05-18 13:11:05 +0000408 public void testListComprehensionsWithFiltering() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000409 newTest()
410 .setUp("range3 = [0, 1, 2]")
411 .testEval("[a for a in (4, None, 2, None, 1) if a != None]", "[4, 2, 1]")
412 .testEval("[b+c for b in [0, 1, 2] for c in [0, 1, 2] if b + c > 2]", "[3, 3, 4]")
413 .testEval("[d+e for d in range3 if d % 2 == 1 for e in range3]", "[1, 2, 3]")
414 .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 ]",
415 "[[2, 6], [3, 8], [4, 6]]")
416 .testEval("[h for h in [4, 2, 0, 1] if h]", "[4, 2, 1]");
Laurent Le Brunb4c54742015-05-18 13:11:05 +0000417 }
418
419 @Test
420 public void testListComprehensionDefinitionOrder() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000421 newTest().testIfErrorContains("name 'y' is not defined",
Laurent Le Brunb4c54742015-05-18 13:11:05 +0000422 "[x for x in (1, 2) if y for y in (3, 4)]");
423 }
424
425 @Test
Laurent Le Brun741824b2015-03-20 15:10:19 +0000426 public void testTupleDestructuring() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000427 newTest()
428 .setUp("a, b = 1, 2")
429 .testLookup("a", 1)
430 .testLookup("b", 2)
431 .setUp("c, d = {'key1':2, 'key2':3}")
432 .testLookup("c", "key1")
433 .testLookup("d", "key2");
Laurent Le Brun741824b2015-03-20 15:10:19 +0000434 }
435
436 @Test
Laurent Le Brun29ad8622015-09-18 10:45:07 +0000437 public void testSingleTuple() throws Exception {
438 newTest().setUp("a, = [1]").testLookup("a", 1);
439 }
440
441 @Test
Laurent Le Brun2e78d612015-04-15 09:06:46 +0000442 public void testHeterogeneousDict() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000443 newTest().setUp("d = {'str': 1, 2: 3}", "a = d['str']", "b = d[2]").testLookup("a", 1)
444 .testLookup("b", 3);
Laurent Le Brun2e78d612015-04-15 09:06:46 +0000445 }
446
447 @Test
Laurent Le Brun6824d862015-09-11 13:51:41 +0000448 public void testAccessDictWithATupleKey() throws Exception {
449 newTest().setUp("x = {(1, 2): 3}[1, 2]").testLookup("x", 3);
450 }
451
452 @Test
Laurent Le Brun741824b2015-03-20 15:10:19 +0000453 public void testRecursiveTupleDestructuring() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000454 newTest()
455 .setUp("((a, b), (c, d)) = [(1, 2), (3, 4)]")
456 .testLookup("a", 1)
457 .testLookup("b", 2)
458 .testLookup("c", 3)
459 .testLookup("d", 4);
Laurent Le Brun741824b2015-03-20 15:10:19 +0000460 }
461
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000462 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000463 public void testListComprehensionModifiesGlobalEnv() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000464 new SkylarkTest().update("x", 42).testIfExactError("ERROR 1:1: Variable x is read only",
465 "[x + 1 for x in [1,2,3]]");
466 new BuildTest().update("x", 42).setUp("y =[x + 1 for x in [1,2,3]]")
467 .testExactOrder("y", 2, 3, 4).testLookup("x", 3); // (x is global)
Ulf Adams89f012d2015-02-26 13:39:28 +0000468 }
469
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000470 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000471 public void testDictComprehensions() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000472 newTest()
473 .testStatement("{a : a for a in []}", Collections.emptyMap())
474 .testStatement("{b : b for b in [1, 2]}", ImmutableMap.of(1, 1, 2, 2))
475 .testStatement("{c : 'v_' + c for c in ['a', 'b']}",
476 ImmutableMap.of("a", "v_a", "b", "v_b"))
477 .testStatement("{'k_' + d : d for d in ['a', 'b']}",
478 ImmutableMap.of("k_a", "a", "k_b", "b"))
479 .testStatement("{'k_' + e : 'v_' + e for e in ['a', 'b']}",
480 ImmutableMap.of("k_a", "v_a", "k_b", "v_b"))
481 .testStatement("{x+y : x*y for x, y in [[2, 3]]}", ImmutableMap.of(5, 6));
Ulf Adams89f012d2015-02-26 13:39:28 +0000482 }
483
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000484 @Test
Laurent Le Brun2e78d612015-04-15 09:06:46 +0000485 public void testDictComprehensionOnNonIterable() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000486 newTest().testIfExactError("type 'int' is not iterable", "{k : k for k in 3}");
Laurent Le Brun2e78d612015-04-15 09:06:46 +0000487 }
488
489 @Test
Florian Weikertffd8a5a2015-09-18 11:51:01 +0000490 public void testDictComprehension_ManyClauses() throws Exception {
491 new SkylarkTest().testStatement(
492 "{x : x * y for x in range(1, 10) if x % 2 == 0 for y in range(1, 10) if y == x}",
493 ImmutableMap.of(2, 4, 4, 16, 6, 36, 8, 64));
494 }
495
496 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000497 public void testDictComprehensions_MultipleKey() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000498 newTest().testStatement("{x : x for x in [1, 2, 1]}", ImmutableMap.of(1, 1, 2, 2))
499 .testStatement("{y : y for y in ['ab', 'c', 'a' + 'b']}",
500 ImmutableMap.of("ab", "ab", "c", "c"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000501 }
502
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000503 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000504 public void testDictComprehensions_ToString() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000505 assertEquals("{x: x for x in [1, 2]}",
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000506 parseExpression("{x : x for x in [1, 2]}").toString());
Ulf Adams89f012d2015-02-26 13:39:28 +0000507 assertEquals("{x + 'a': x for x in [1, 2]}",
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000508 parseExpression("{x + 'a' : x for x in [1, 2]}").toString());
Ulf Adams89f012d2015-02-26 13:39:28 +0000509 }
510
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000511 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000512 public void testListConcatenation() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000513 newTest()
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000514 .testStatement("[1, 2] + [3, 4]", MutableList.of(env, 1, 2, 3, 4))
515 .testStatement("(1, 2) + (3, 4)", Tuple.of(1, 2, 3, 4))
516 .testIfExactError("unsupported operand type(s) for +: 'list' and 'tuple'",
517 "[1, 2] + (3, 4)")
518 .testIfExactError("unsupported operand type(s) for +: 'tuple' and 'list'",
519 "(1, 2) + [3, 4]");
Ulf Adams89f012d2015-02-26 13:39:28 +0000520 }
521
Greg Estrenb3dece02015-05-14 17:18:41 +0000522 @SuppressWarnings("unchecked")
523 @Test
524 public void testSelectorListConcatenation() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000525 // TODO(fwe): cannot be handled by current testing suite
Greg Estrenb3dece02015-05-14 17:18:41 +0000526 SelectorList x = (SelectorList) eval("select({'foo': ['FOO'], 'bar': ['BAR']}) + []");
527 List<Object> elements = x.getElements();
528 assertThat(elements.size()).isEqualTo(2);
529 assertThat(elements.get(0)).isInstanceOf(SelectorValue.class);
Florian Weikert28da3652015-07-01 14:52:30 +0000530 assertThat((Iterable<Object>) elements.get(1)).isEmpty();
Greg Estrenb3dece02015-05-14 17:18:41 +0000531 }
532
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000533 @Test
Laurent Le Bruna58dcd12015-11-05 13:51:53 +0000534 public void testAddSelectIncompatibleType() throws Exception {
535 newTest()
536 .testIfErrorContains(
537 "'+' operator applied to incompatible types (select of list, int)",
538 "select({'foo': ['FOO'], 'bar': ['BAR']}) + 1");
539 }
540
541 @Test
542 public void testAddSelectIncompatibleType2() throws Exception {
543 newTest()
544 .testIfErrorContains(
545 "'+' operator applied to incompatible types (select of list, select of int)",
546 "select({'foo': ['FOO']}) + select({'bar': 2})");
547 }
548
549 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000550 public void testListComprehensionFailsOnNonSequence() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000551 newTest().testIfErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]");
Ulf Adams89f012d2015-02-26 13:39:28 +0000552 }
553
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000554 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000555 public void testListComprehensionOnString() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000556 newTest().testExactOrder("[x for x in 'abc']", "a", "b", "c");
Ulf Adams89f012d2015-02-26 13:39:28 +0000557 }
558
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000559 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000560 public void testInvalidAssignment() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000561 newTest().testIfErrorContains(
562 "can only assign to variables and tuples, not to 'x + 1'", "x + 1 = 2");
Ulf Adams89f012d2015-02-26 13:39:28 +0000563 }
564
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000565 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000566 public void testListComprehensionOnDictionary() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000567 newTest().testExactOrder("val = ['var_' + n for n in {'a':1,'b':2}] ; val", "var_a", "var_b");
Ulf Adams89f012d2015-02-26 13:39:28 +0000568 }
569
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000570 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000571 public void testListComprehensionOnDictionaryCompositeExpression() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000572 new BuildTest()
573 .setUp("d = {1:'a',2:'b'}", "l = [d[x] for x in d]")
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000574 .testLookup("l", MutableList.of(env, "a", "b"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000575 }
576
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000577 @Test
Laurent Le Brunab0ca1a2015-03-31 17:13:25 +0000578 public void testInOperator() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000579 newTest()
580 .testStatement("'b' in ['a', 'b']", Boolean.TRUE)
581 .testStatement("'c' in ['a', 'b']", Boolean.FALSE)
582 .testStatement("'b' in ('a', 'b')", Boolean.TRUE)
583 .testStatement("'c' in ('a', 'b')", Boolean.FALSE)
584 .testStatement("'b' in {'a' : 1, 'b' : 2}", Boolean.TRUE)
585 .testStatement("'c' in {'a' : 1, 'b' : 2}", Boolean.FALSE)
586 .testStatement("1 in {'a' : 1, 'b' : 2}", Boolean.FALSE)
587 .testStatement("'b' in 'abc'", Boolean.TRUE)
588 .testStatement("'d' in 'abc'", Boolean.FALSE);
Ulf Adams89f012d2015-02-26 13:39:28 +0000589 }
590
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000591 @Test
Laurent Le Brune3f4ed72015-05-08 14:47:26 +0000592 public void testNotInOperator() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000593 newTest()
594 .testStatement("'b' not in ['a', 'b']", Boolean.FALSE)
595 .testStatement("'c' not in ['a', 'b']", Boolean.TRUE)
596 .testStatement("'b' not in ('a', 'b')", Boolean.FALSE)
597 .testStatement("'c' not in ('a', 'b')", Boolean.TRUE)
598 .testStatement("'b' not in {'a' : 1, 'b' : 2}", Boolean.FALSE)
599 .testStatement("'c' not in {'a' : 1, 'b' : 2}", Boolean.TRUE)
600 .testStatement("1 not in {'a' : 1, 'b' : 2}", Boolean.TRUE)
601 .testStatement("'b' not in 'abc'", Boolean.FALSE)
602 .testStatement("'d' not in 'abc'", Boolean.TRUE);
Laurent Le Brune3f4ed72015-05-08 14:47:26 +0000603 }
604
605 @Test
Laurent Le Brunab0ca1a2015-03-31 17:13:25 +0000606 public void testInFail() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000607 newTest()
608 .testIfExactError(
609 "in operator only works on strings if the left operand is also a string", "1 in '123'")
610 .testIfExactError(
611 "in operator only works on lists, tuples, sets, dicts and strings", "'a' in 1");
Ulf Adams89f012d2015-02-26 13:39:28 +0000612 }
613
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000614 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000615 public void testInCompositeForPrecedence() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000616 newTest().testStatement("not 'a' in ['a'] or 0", 0);
Ulf Adams89f012d2015-02-26 13:39:28 +0000617 }
618
619 private Object createObjWithStr() {
620 return new Object() {
621 @Override
622 public String toString() {
623 return "str marker";
624 }
625 };
626 }
627
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000628 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000629 public void testPercOnObject() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000630 newTest().update("obj", createObjWithStr()).testStatement("'%s' % obj", "str marker");
Ulf Adams89f012d2015-02-26 13:39:28 +0000631 }
632
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000633 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000634 public void testPercOnObjectList() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000635 newTest().update("obj", createObjWithStr()).testStatement("'%s %s' % (obj, obj)",
636 "str marker str marker");
Ulf Adams89f012d2015-02-26 13:39:28 +0000637 }
638
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000639 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000640 public void testPercOnObjectInvalidFormat() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000641 newTest().update("obj", createObjWithStr()).testIfExactError(
642 "invalid argument str marker for format pattern %d", "'%d' % obj");
Ulf Adams89f012d2015-02-26 13:39:28 +0000643 }
644
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000645 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000646 public void testDictKeys() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000647 newTest().testExactOrder("v = {'a': 1}.keys() + ['b', 'c'] ; v", "a", "b", "c");
Ulf Adams89f012d2015-02-26 13:39:28 +0000648 }
649
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000650 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000651 public void testDictKeysTooManyArgs() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000652 newTest().testIfExactError(
653 "too many (2) positional arguments in call to keys(self: dict)", "{'a': 1}.keys('abc')");
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000654 }
655
656 @Test
657 public void testDictKeysTooManyKeyArgs() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000658 newTest().testIfExactError("unexpected keyword 'arg' in call to keys(self: dict)",
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000659 "{'a': 1}.keys(arg='abc')");
660 }
661
662 @Test
663 public void testDictKeysDuplicateKeyArgs() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000664 newTest().testIfExactError("duplicate keywords 'arg', 'k' in call to keys",
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000665 "{'a': 1}.keys(arg='abc', arg='def', k=1, k=2)");
666 }
667
668 @Test
669 public void testArgBothPosKey() throws Exception {
Florian Weikert28da3652015-07-01 14:52:30 +0000670 newTest().testIfErrorContains(
671 "arguments 'old', 'new' passed both by position and by name "
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000672 + "in call to replace(self: string, ",
673 "'banana'.replace('a', 'o', 3, old='a', new=4)");
Ulf Adams89f012d2015-02-26 13:39:28 +0000674 }
675}