blob: 37f3b95351814b26f0d29b9bf7a8b885faa05299 [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.assertNotNull;
Ulf Adams89f012d2015-02-26 13:39:28 +000019
Ulf Adams89f012d2015-02-26 13:39:28 +000020import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +000021import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
Ulf Adams89f012d2015-02-26 13:39:28 +000022
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000023import org.junit.Test;
24import org.junit.runner.RunWith;
25import org.junit.runners.JUnit4;
26
Ulf Adams89f012d2015-02-26 13:39:28 +000027import java.util.ArrayList;
28import java.util.List;
29import java.util.Map;
30
31/**
32 * A test class for functions and scoping.
33 */
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000034@RunWith(JUnit4.class)
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000035public class FunctionTest extends EvaluationTestCase {
Ulf Adams89f012d2015-02-26 13:39:28 +000036
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000037 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000038 public void testFunctionDef() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000039 eval("def func(a,b,c):",
40 " a = 1",
41 " b = a\n");
42 UserDefinedFunction stmt = (UserDefinedFunction) lookup("func");
Ulf Adams89f012d2015-02-26 13:39:28 +000043 assertNotNull(stmt);
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000044 assertThat(stmt.getName()).isEqualTo("func");
45 assertThat(stmt.getFunctionSignature().getSignature().getShape().getMandatoryPositionals())
46 .isEqualTo(3);
Ulf Adams89f012d2015-02-26 13:39:28 +000047 assertThat(stmt.getStatements()).hasSize(2);
48 }
49
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000050 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000051 public void testFunctionDefDuplicateArguments() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000052 setFailFast(false);
53 parseFile("def func(a,b,a):",
54 " a = 1\n");
55 assertContainsEvent("duplicate parameter name in function definition");
Ulf Adams89f012d2015-02-26 13:39:28 +000056 }
57
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000058 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000059 public void testFunctionDefCallOuterFunc() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000060 List<Object> params = new ArrayList<>();
61 createOuterFunction(params);
62 eval("def func(a):",
63 " outer_func(a)",
64 "func(1)",
65 "func(2)");
Ulf Adams89f012d2015-02-26 13:39:28 +000066 assertThat(params).containsExactly(1, 2).inOrder();
67 }
68
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000069 private void createOuterFunction(final List<Object> params) throws Exception {
Francois-Rene Rideau4feb1602015-03-18 19:49:13 +000070 BaseFunction outerFunc = new BaseFunction("outer_func") {
Ulf Adams89f012d2015-02-26 13:39:28 +000071 @Override
72 public Object call(List<Object> args, Map<String, Object> kwargs, FuncallExpression ast,
73 Environment env) throws EvalException, InterruptedException {
74 params.addAll(args);
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000075 return Runtime.NONE;
Ulf Adams89f012d2015-02-26 13:39:28 +000076 }
77 };
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000078 update("outer_func", outerFunc);
Ulf Adams89f012d2015-02-26 13:39:28 +000079 }
80
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000081 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000082 public void testFunctionDefNoEffectOutsideScope() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000083 update("a", 1);
84 eval("def func():",
85 " a = 2",
86 "func()\n");
87 assertEquals(1, lookup("a"));
Ulf Adams89f012d2015-02-26 13:39:28 +000088 }
89
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +000090 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +000091 public void testFunctionDefGlobalVaribleReadInFunction() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +000092 eval("a = 1",
93 "def func():",
94 " b = a",
95 " return b",
96 "c = func()\n");
97 assertEquals(1, lookup("c"));
Ulf Adams89f012d2015-02-26 13:39:28 +000098 }
99
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000100 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000101 public void testFunctionDefLocalGlobalScope() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000102 eval("a = 1",
103 "def func():",
104 " a = 2",
105 " b = a",
106 " return b",
107 "c = func()\n");
108 assertEquals(2, lookup("c"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000109 }
110
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000111 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000112 public void testFunctionDefLocalVariableReferencedBeforeAssignment() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000113 checkEvalErrorContains("Variable 'a' is referenced before assignment.",
114 "a = 1",
115 "def func():",
116 " b = a",
117 " a = 2",
118 " return b",
119 "c = func()\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000120 }
121
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000122 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000123 public void testFunctionDefLocalVariableReferencedAfterAssignment() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000124 eval("a = 1",
125 "def func():",
126 " a = 2",
127 " b = a",
128 " a = 3",
129 " return b",
130 "c = func()\n");
131 assertEquals(2, lookup("c"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000132 }
133
134 @SuppressWarnings("unchecked")
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000135 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000136 public void testSkylarkGlobalComprehensionIsAllowed() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000137 eval("a = [i for i in [1, 2, 3]]\n");
138 assertThat((Iterable<Object>) lookup("a")).containsExactly(1, 2, 3).inOrder();
Ulf Adams89f012d2015-02-26 13:39:28 +0000139 }
140
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000141 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000142 public void testFunctionReturn() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000143 eval("def func():",
144 " return 2",
145 "b = func()\n");
146 assertEquals(2, lookup("b"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000147 }
148
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000149 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000150 public void testFunctionReturnFromALoop() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000151 eval("def func():",
152 " for i in [1, 2, 3, 4, 5]:",
153 " return i",
154 "b = func()\n");
155 assertEquals(1, lookup("b"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000156 }
157
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000158 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000159 public void testFunctionExecutesProperly() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000160 eval("def func(a):",
161 " b = 1",
162 " if a:",
163 " b = 2",
164 " return b",
165 "c = func(0)",
166 "d = func(1)\n");
167 assertEquals(1, lookup("c"));
168 assertEquals(2, lookup("d"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000169 }
170
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000171 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000172 public void testFunctionCallFromFunction() throws Exception {
173 final List<Object> params = new ArrayList<>();
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000174 createOuterFunction(params);
175 eval("def func2(a):",
176 " outer_func(a)",
177 "def func1(b):",
178 " func2(b)",
179 "func1(1)",
180 "func1(2)\n");
Ulf Adams89f012d2015-02-26 13:39:28 +0000181 assertThat(params).containsExactly(1, 2).inOrder();
182 }
183
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000184 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000185 public void testFunctionCallFromFunctionReadGlobalVar() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000186 eval("a = 1",
187 "def func2():",
188 " return a",
189 "def func1():",
190 " return func2()",
191 "b = func1()\n");
192 assertEquals(1, lookup("b"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000193 }
194
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000195 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000196 public void testSingleLineFunction() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000197 eval("def func(): return 'a'",
198 "s = func()\n");
199 assertEquals("a", lookup("s"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000200 }
201
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000202 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000203 public void testFunctionReturnsDictionary() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000204 eval("def func(): return {'a' : 1}",
205 "d = func()",
206 "a = d['a']\n");
207 assertEquals(1, lookup("a"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000208 }
209
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000210 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000211 public void testFunctionReturnsList() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000212 eval("def func(): return [1, 2, 3]",
213 "d = func()",
214 "a = d[1]\n");
215 assertEquals(2, lookup("a"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000216 }
217
218 @SuppressWarnings("unchecked")
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000219 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000220 public void testFunctionListArgumentsAreImmutable() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000221 eval("l = [1]",
222 "def func(l):",
223 " l += [2]",
224 "func(l)");
225 assertThat((Iterable<Object>) lookup("l")).containsExactly(1);
Ulf Adams89f012d2015-02-26 13:39:28 +0000226 }
227
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000228 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000229 public void testFunctionDictArgumentsAreImmutable() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000230 eval("d = {'a' : 1}",
231 "def func(d):",
232 " d += {'a' : 2}",
233 "func(d)");
234 assertEquals(ImmutableMap.of("a", 1), lookup("d"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000235 }
236
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000237 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000238 public void testFunctionNameAliasing() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000239 eval("def func(a):",
240 " return a + 1",
241 "alias = func",
242 "r = alias(1)");
243 assertEquals(2, lookup("r"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000244 }
245
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000246 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000247 public void testCallingFunctionsWithMixedModeArgs() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000248 eval("def func(a, b, c):",
249 " return a + b + c",
250 "v = func(1, c = 2, b = 3)");
251 assertEquals(6, lookup("v"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000252 }
253
254 private String functionWithOptionalArgs() {
255 return "def func(a, b = None, c = None):\n"
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000256 + " r = a + 'a'\n"
257 + " if b:\n"
258 + " r += 'b'\n"
259 + " if c:\n"
260 + " r += 'c'\n"
261 + " return r\n";
Ulf Adams89f012d2015-02-26 13:39:28 +0000262 }
263
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000264 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000265 public void testWhichOptionalArgsAreDefinedForFunctions() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000266 eval(functionWithOptionalArgs(),
267 "v1 = func('1', 1, 1)",
268 "v2 = func(b = 2, a = '2', c = 2)",
269 "v3 = func('3')",
270 "v4 = func('4', c = 1)\n");
271 assertEquals("1abc", lookup("v1"));
272 assertEquals("2abc", lookup("v2"));
273 assertEquals("3a", lookup("v3"));
274 assertEquals("4ac", lookup("v4"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000275 }
276
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000277 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000278 public void testDefaultArguments() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000279 eval("def func(a, b = 'b', c = 'c'):",
280 " return a + b + c",
281 "v1 = func('a', 'x', 'y')",
282 "v2 = func(b = 'x', a = 'a', c = 'y')",
283 "v3 = func('a')",
284 "v4 = func('a', c = 'y')\n");
285 assertEquals("axy", lookup("v1"));
286 assertEquals("axy", lookup("v2"));
287 assertEquals("abc", lookup("v3"));
288 assertEquals("aby", lookup("v4"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000289 }
290
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000291 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000292 public void testDefaultArgumentsInsufficientArgNum() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000293 checkEvalError("insufficient arguments received by func(a, b = \"b\", c = \"c\") "
Francois-Rene Rideau4feb1602015-03-18 19:49:13 +0000294 + "(got 0, expected at least 1)",
Ulf Adams89f012d2015-02-26 13:39:28 +0000295 "def func(a, b = 'b', c = 'c'):",
296 " return a + b + c",
297 "func()");
298 }
299
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000300 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000301 public void testKwargs() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000302 eval("def foo(a, b = 'b', *, c, d = 'd'):",
303 " return a + b + c + d",
304 "args = {'a': 'x', 'c': 'z'}",
305 "v1 = foo(**args)",
306 "v2 = foo('x', c = 'c', d = 'e', **{'b': 'y'})",
307 "v3 = foo(c = 'z', a = 'x', **{'b': 'y', 'd': 'f'})");
308 assertEquals("xbzd", lookup("v1"));
309 assertEquals("xyce", lookup("v2"));
310 assertEquals("xyzf", lookup("v3"));
311 UserDefinedFunction foo = (UserDefinedFunction) lookup("foo");
Francois-Rene Rideau012f7892015-03-31 17:27:01 +0000312 assertEquals("foo(a, b = \"b\", *, c, d = \"d\")", foo.toString());
Ulf Adams89f012d2015-02-26 13:39:28 +0000313 }
314
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000315 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000316 public void testKwargsBadKey() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000317 checkEvalError("Keywords must be strings, not int",
318 "def func(a, b): return a + b",
Ulf Adams89f012d2015-02-26 13:39:28 +0000319 "func('a', **{3: 1})");
320 }
321
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000322 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000323 public void testKwargsIsNotDict() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000324 checkEvalError("Argument after ** must be a dictionary, not int",
325 "def func(a, b): return a + b",
Ulf Adams89f012d2015-02-26 13:39:28 +0000326 "func('a', **42)");
327 }
328
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000329 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000330 public void testKwargsCollision() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000331 checkEvalError("argument 'b' passed both by position and by name in call to func(a, b)",
332 "def func(a, b): return a + b",
Ulf Adams89f012d2015-02-26 13:39:28 +0000333 "func('a', 'b', **{'b': 'foo'})");
334 }
335
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000336 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000337 public void testKwargsCollisionWithNamed() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000338 checkEvalError("duplicate keyword 'b' in call to func",
339 "def func(a, b): return a + b",
Ulf Adams89f012d2015-02-26 13:39:28 +0000340 "func('a', b = 'b', **{'b': 'foo'})");
341 }
342
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000343 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000344 public void testDefaultArguments2() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000345 eval("a = 2",
346 "def foo(x=a): return x",
347 "def bar():",
348 " a = 3",
349 " return foo()",
350 "v = bar()\n");
351 assertEquals(2, lookup("v"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000352 }
353
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000354 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000355 public void testMixingPositionalOptional() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000356 eval("def f(name, value = '', optional = ''): return value",
357 "v = f('name', 'value')\n");
358 assertEquals("value", lookup("v"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000359 }
360
Han-Wen Nienhuysccf19ea2015-02-27 15:53:24 +0000361 @Test
Ulf Adams89f012d2015-02-26 13:39:28 +0000362 public void testStarArg() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000363 eval("def f(name, value = '1', optional = '2'): return name + value + optional",
364 "v1 = f(*['name', 'value'])",
365 "v2 = f('0', *['name', 'value'])",
366 "v3 = f('0', *['b'], optional = '3')",
367 "v4 = f(*[],name='a')\n");
368 assertEquals("namevalue2", lookup("v1"));
369 assertEquals("0namevalue", lookup("v2"));
370 assertEquals("0b3", lookup("v3"));
371 assertEquals("a12", lookup("v4"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000372 }
373
Francois-Rene Rideau4feb1602015-03-18 19:49:13 +0000374 @Test
375 public void testStarParam() throws Exception {
Francois-Rene Rideau5f3e30c2015-04-10 19:08:39 +0000376 eval("def f(name, value = '1', *rest, mandatory, optional = '2'):",
377 " r = name + value + mandatory + optional + '|'",
378 " for x in rest: r += x",
379 " return r",
380 "v1 = f('a', 'b', mandatory = 'z')",
381 "v2 = f('a', 'b', 'c', 'd', mandatory = 'z')",
382 "v3 = f('a', *['b', 'c', 'd'], mandatory = 'y', optional = 'z')",
383 "v4 = f(*['a'], **{'value': 'b', 'mandatory': 'c'})",
384 "v5 = f('a', 'b', 'c', *['d', 'e'], mandatory = 'f', **{'optional': 'g'})\n");
385 assertEquals("abz2|", lookup("v1"));
386 assertEquals("abz2|cd", lookup("v2"));
387 assertEquals("abyz|cd", lookup("v3"));
388 assertEquals("abc2|", lookup("v4"));
389 assertEquals("abfg|cde", lookup("v5"));
Ulf Adams89f012d2015-02-26 13:39:28 +0000390 }
391}