Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 2 | // |
| 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. |
| 14 | package com.google.devtools.build.lib.syntax; |
| 15 | |
| 16 | import static com.google.common.truth.Truth.assertThat; |
| 17 | |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 18 | import com.google.common.collect.ImmutableMap; |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 19 | import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter; |
| 20 | import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; |
Francois-Rene Rideau | 93ed7f1 | 2015-10-20 15:39:33 +0000 | [diff] [blame] | 21 | import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; |
| 22 | import com.google.devtools.build.lib.syntax.SkylarkList.Tuple; |
Han-Wen Nienhuys | ceae8c5 | 2015-09-22 16:24:45 +0000 | [diff] [blame] | 23 | import com.google.devtools.build.lib.syntax.util.EvaluationTestCase; |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 24 | import com.google.devtools.build.lib.testutil.TestMode; |
Andreas Bergmeier | a397340 | 2016-12-15 15:33:41 +0000 | [diff] [blame] | 25 | import java.util.Collections; |
| 26 | import java.util.List; |
| 27 | import java.util.Map; |
Han-Wen Nienhuys | 33ce211 | 2015-09-25 14:25:38 +0000 | [diff] [blame] | 28 | import org.junit.Before; |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 29 | import org.junit.Test; |
| 30 | import org.junit.runner.RunWith; |
| 31 | import org.junit.runners.JUnit4; |
| 32 | |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 33 | /** |
| 34 | * Test of evaluation behavior. (Implicitly uses lexer + parser.) |
| 35 | */ |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 36 | @RunWith(JUnit4.class) |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 37 | public class EvaluationTest extends EvaluationTestCase { |
Florian Weikert | b4c5904 | 2015-12-01 10:47:18 +0000 | [diff] [blame] | 38 | |
Han-Wen Nienhuys | 33ce211 | 2015-09-25 14:25:38 +0000 | [diff] [blame] | 39 | @Before |
Florian Weikert | b4c5904 | 2015-12-01 10:47:18 +0000 | [diff] [blame] | 40 | public final void setBuildMode() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 41 | super.setMode(TestMode.BUILD); |
| 42 | } |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 43 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 44 | /** |
| 45 | * Creates a new instance of {@code ModalTestCase}. |
| 46 | * |
| 47 | * <p>If a test uses this method, it allows potential subclasses to run the very same test in a |
| 48 | * different mode in subclasses |
| 49 | */ |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 50 | protected ModalTestCase newTest(String... skylarkOptions) { |
| 51 | return new BuildTest(skylarkOptions); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 52 | } |
| 53 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 54 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 55 | public void testExprs() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 56 | newTest() |
Francois-Rene Rideau | 0f7ba34 | 2015-08-31 16:16:21 +0000 | [diff] [blame] | 57 | .testStatement("'%sx' % 'foo' + 'bar1'", "fooxbar1") |
| 58 | .testStatement("('%sx' % 'foo') + 'bar2'", "fooxbar2") |
| 59 | .testStatement("'%sx' % ('foo' + 'bar3')", "foobar3x") |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 60 | .testStatement("123 + 456", 579) |
| 61 | .testStatement("456 - 123", 333) |
| 62 | .testStatement("8 % 3", 2) |
brandjon | f2ed858 | 2017-06-27 15:05:35 +0200 | [diff] [blame] | 63 | .testIfErrorContains("unsupported operand type(s) for %: 'int' and 'string'", "3 % 'foo'") |
| 64 | .testStatement("-5", -5) |
Marwan Tammam | 66aa424 | 2019-07-24 06:56:08 -0700 | [diff] [blame] | 65 | .testIfErrorContains("unsupported unary operation: -string", "-'foo'"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 66 | } |
| 67 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 68 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 69 | public void testListExprs() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 70 | newTest().testExactOrder("[1, 2, 3]", 1, 2, 3).testExactOrder("(1, 2, 3)", 1, 2, 3); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 71 | } |
| 72 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 73 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 74 | public void testStringFormatMultipleArgs() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 75 | newTest().testStatement("'%sY%s' % ('X', 'Z')", "XYZ"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 76 | } |
| 77 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 78 | @Test |
Francois-Rene Rideau | 6fc5ee7 | 2015-03-12 20:55:17 +0000 | [diff] [blame] | 79 | public void testConditionalExpressions() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 80 | newTest() |
| 81 | .testStatement("1 if True else 2", 1) |
| 82 | .testStatement("1 if False else 2", 2) |
| 83 | .testStatement("1 + 2 if 3 + 4 else 5 + 6", 3); |
Francois-Rene Rideau | 6fc5ee7 | 2015-03-12 20:55:17 +0000 | [diff] [blame] | 84 | |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 85 | setFailFast(false); |
| 86 | parseExpression("1 if 2"); |
Ulf Adams | c708f96 | 2015-10-22 12:02:28 +0000 | [diff] [blame] | 87 | assertContainsError( |
Francois-Rene Rideau | 6fc5ee7 | 2015-03-12 20:55:17 +0000 | [diff] [blame] | 88 | "missing else clause in conditional expression or semicolon before if"); |
Francois-Rene Rideau | 6fc5ee7 | 2015-03-12 20:55:17 +0000 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | @Test |
Laurent Le Brun | ac8aae8 | 2015-04-16 11:42:55 +0000 | [diff] [blame] | 92 | public void testListComparison() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 93 | newTest() |
| 94 | .testStatement("[] < [1]", true) |
| 95 | .testStatement("[1] < [1, 1]", true) |
| 96 | .testStatement("[1, 1] < [1, 2]", true) |
| 97 | .testStatement("[1, 2] < [1, 2, 3]", true) |
| 98 | .testStatement("[1, 2, 3] <= [1, 2, 3]", true) |
Laurent Le Brun | ac8aae8 | 2015-04-16 11:42:55 +0000 | [diff] [blame] | 99 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 100 | .testStatement("['a', 'b'] > ['a']", true) |
| 101 | .testStatement("['a', 'b'] >= ['a']", true) |
| 102 | .testStatement("['a', 'b'] < ['a']", false) |
| 103 | .testStatement("['a', 'b'] <= ['a']", false) |
Laurent Le Brun | ac8aae8 | 2015-04-16 11:42:55 +0000 | [diff] [blame] | 104 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 105 | .testStatement("('a', 'b') > ('a', 'b')", false) |
| 106 | .testStatement("('a', 'b') >= ('a', 'b')", true) |
| 107 | .testStatement("('a', 'b') < ('a', 'b')", false) |
| 108 | .testStatement("('a', 'b') <= ('a', 'b')", true) |
Laurent Le Brun | ac8aae8 | 2015-04-16 11:42:55 +0000 | [diff] [blame] | 109 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 110 | .testStatement("[[1, 1]] > [[1, 1], []]", false) |
Florian Weikert | f31b947 | 2015-08-04 16:36:58 +0000 | [diff] [blame] | 111 | .testStatement("[[1, 1]] < [[1, 1], []]", true); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 112 | } |
| 113 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 114 | @Test |
Vladimir Moskva | dbb3487 | 2016-11-07 18:32:59 +0000 | [diff] [blame] | 115 | public void testSetComparison() throws Exception { |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 116 | newTest().testIfExactError("Cannot compare depsets", "depset([1, 2]) < depset([3, 4])"); |
Vladimir Moskva | dbb3487 | 2016-11-07 18:32:59 +0000 | [diff] [blame] | 117 | } |
| 118 | |
| 119 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 120 | public void testSumFunction() throws Exception { |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 121 | BaseFunction sum = |
| 122 | new BaseFunction("sum") { |
| 123 | @Override |
| 124 | public Object call( |
| 125 | List<Object> args, |
| 126 | Map<String, Object> kwargs, |
| 127 | FuncallExpression ast, |
| 128 | StarlarkThread thread) { |
| 129 | int sum = 0; |
| 130 | for (Object arg : args) { |
| 131 | sum += (Integer) arg; |
| 132 | } |
| 133 | return sum; |
| 134 | } |
| 135 | }; |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 136 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 137 | newTest().update(sum.getName(), sum).testStatement("sum(1, 2, 3, 4, 5, 6)", 21) |
| 138 | .testStatement("sum", sum).testStatement("sum(a=1, b=2)", 0); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 139 | } |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 140 | |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 141 | @Test |
| 142 | public void testNotCallInt() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 143 | newTest().setUp("sum = 123456").testLookup("sum", 123456) |
| 144 | .testIfExactError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)") |
| 145 | .testStatement("sum", 123456); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 146 | } |
| 147 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 148 | @Test |
fzaiser | e0f1333 | 2017-08-14 12:00:51 +0200 | [diff] [blame] | 149 | public void testComplexFunctionCall() throws Exception { |
| 150 | newTest().setUp("functions = [min, max]", "l = [1,2]") |
| 151 | .testEval("(functions[0](l), functions[1](l))", "(1, 2)"); |
| 152 | } |
| 153 | |
| 154 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 155 | public void testKeywordArgs() throws Exception { |
| 156 | |
Francois-Rene Rideau | 76023b9 | 2015-04-17 15:31:59 +0000 | [diff] [blame] | 157 | // This function returns the map of keyword arguments passed to it. |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 158 | BaseFunction kwargs = |
| 159 | new BaseFunction("kwargs") { |
| 160 | @Override |
| 161 | public Object call( |
| 162 | List<Object> args, |
| 163 | final Map<String, Object> kwargs, |
| 164 | FuncallExpression ast, |
| 165 | StarlarkThread thread) { |
| 166 | return SkylarkDict.copyOf(thread, kwargs); |
| 167 | } |
| 168 | }; |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 169 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 170 | newTest() |
| 171 | .update(kwargs.getName(), kwargs) |
| 172 | .testEval( |
| 173 | "kwargs(foo=1, bar='bar', wiz=[1,2,3]).items()", |
Vladimir Moskva | 76e31d1 | 2016-12-05 16:28:37 +0000 | [diff] [blame] | 174 | "[('foo', 1), ('bar', 'bar'), ('wiz', [1, 2, 3])]") |
| 175 | .testEval( |
| 176 | "kwargs(wiz=[1,2,3], bar='bar', foo=1).items()", |
| 177 | "[('wiz', [1, 2, 3]), ('bar', 'bar'), ('foo', 1)]"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 178 | } |
| 179 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 180 | @Test |
Laurent Le Brun | bd71674 | 2015-04-15 11:05:03 +0000 | [diff] [blame] | 181 | public void testModulo() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 182 | newTest() |
| 183 | .testStatement("6 % 2", 0) |
| 184 | .testStatement("6 % 4", 2) |
| 185 | .testStatement("3 % 6", 3) |
| 186 | .testStatement("7 % -4", -1) |
| 187 | .testStatement("-7 % 4", 1) |
| 188 | .testStatement("-7 % -4", -3) |
| 189 | .testIfExactError("integer modulo by zero", "5 % 0"); |
Laurent Le Brun | bd71674 | 2015-04-15 11:05:03 +0000 | [diff] [blame] | 190 | } |
| 191 | |
| 192 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 193 | public void testMult() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 194 | newTest() |
| 195 | .testStatement("6 * 7", 42) |
| 196 | .testStatement("3 * 'ab'", "ababab") |
| 197 | .testStatement("0 * 'ab'", "") |
Florian Weikert | 03b5b57 | 2016-11-02 13:34:09 +0000 | [diff] [blame] | 198 | .testStatement("'1' + '0' * 5", "100000") |
| 199 | .testStatement("'ab' * -4", "") |
| 200 | .testStatement("-1 * ''", ""); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 201 | } |
| 202 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 203 | @Test |
laurentlb | c381cf1 | 2018-04-11 04:12:14 -0700 | [diff] [blame] | 204 | public void testSlashOperatorIsForbidden() throws Exception { |
laurentlb | 387cb02 | 2019-01-11 09:39:38 -0800 | [diff] [blame] | 205 | newTest().testIfErrorContains("The `/` operator is not allowed.", "5 / 2"); |
Laurent Le Brun | 8a52826 | 2015-04-15 14:23:35 +0000 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | @Test |
laurentlb | 094bb26 | 2017-05-19 21:18:25 +0200 | [diff] [blame] | 209 | public void testFloorDivision() throws Exception { |
| 210 | newTest() |
| 211 | .testStatement("6 // 2", 3) |
| 212 | .testStatement("6 // 4", 1) |
| 213 | .testStatement("3 // 6", 0) |
| 214 | .testStatement("7 // -2", -4) |
| 215 | .testStatement("-7 // 2", -4) |
| 216 | .testStatement("-7 // -2", 3) |
| 217 | .testStatement("2147483647 // 2", 1073741823) |
laurentlb | 58dab6c | 2018-11-12 11:34:30 -0800 | [diff] [blame] | 218 | .testIfErrorContains("unsupported operand type(s) for //: 'string' and 'int'", "'str' // 2") |
laurentlb | 094bb26 | 2017-05-19 21:18:25 +0200 | [diff] [blame] | 219 | .testIfExactError("integer division by zero", "5 // 0"); |
| 220 | } |
| 221 | |
| 222 | @Test |
laurentlb | 34bbdcf | 2017-07-03 12:16:15 -0400 | [diff] [blame] | 223 | public void testCheckedArithmetic() throws Exception { |
laurentlb | 2195b1c | 2018-02-16 04:14:46 -0800 | [diff] [blame] | 224 | new SkylarkTest() |
laurentlb | 34bbdcf | 2017-07-03 12:16:15 -0400 | [diff] [blame] | 225 | .testIfErrorContains("integer overflow", "2000000000 + 2000000000") |
| 226 | .testIfErrorContains("integer overflow", "1234567890 * 987654321") |
| 227 | .testIfErrorContains("integer overflow", "- 2000000000 - 2000000000") |
| 228 | |
| 229 | // literal 2147483648 is not allowed, so we compute it |
| 230 | .setUp("minint = - 2147483647 - 1") |
| 231 | .testIfErrorContains("integer overflow", "-minint"); |
| 232 | } |
| 233 | |
| 234 | @Test |
Laurent Le Brun | 8a52826 | 2015-04-15 14:23:35 +0000 | [diff] [blame] | 235 | public void testOperatorPrecedence() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 236 | newTest() |
| 237 | .testStatement("2 + 3 * 4", 14) |
laurentlb | 58dab6c | 2018-11-12 11:34:30 -0800 | [diff] [blame] | 238 | .testStatement("2 + 3 // 4", 2) |
| 239 | .testStatement("2 * 3 + 4 // -2", 4); |
Laurent Le Brun | 8a52826 | 2015-04-15 14:23:35 +0000 | [diff] [blame] | 240 | } |
| 241 | |
| 242 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 243 | public void testConcatStrings() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 244 | newTest().testStatement("'foo' + 'bar'", "foobar"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 245 | } |
| 246 | |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 247 | @SuppressWarnings("unchecked") |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 248 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 249 | public void testConcatLists() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 250 | // TODO(fwe): cannot be handled by current testing suite |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 251 | // list |
| 252 | Object x = eval("[1,2] + [3,4]"); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 253 | assertThat((Iterable<Object>) x).containsExactly(1, 2, 3, 4).inOrder(); |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 254 | assertThat(x).isEqualTo(MutableList.of(thread, 1, 2, 3, 4)); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 255 | assertThat(EvalUtils.isImmutable(x)).isFalse(); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 256 | |
| 257 | // tuple |
| 258 | x = eval("(1,2) + (3,4)"); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 259 | assertThat(x).isEqualTo(Tuple.of(1, 2, 3, 4)); |
| 260 | assertThat(EvalUtils.isImmutable(x)).isTrue(); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 261 | |
Francois-Rene Rideau | 93ed7f1 | 2015-10-20 15:39:33 +0000 | [diff] [blame] | 262 | checkEvalError("unsupported operand type(s) for +: 'tuple' and 'list'", |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 263 | "(1,2) + [3,4]"); // list + tuple |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 264 | } |
| 265 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 266 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 267 | public void testListComprehensions() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 268 | newTest() |
| 269 | .testExactOrder("['foo/%s.java' % x for x in []]") |
| 270 | .testExactOrder("['foo/%s.java' % y for y in ['bar', 'wiz', 'quux']]", "foo/bar.java", |
| 271 | "foo/wiz.java", "foo/quux.java") |
laurentlb | c3a1af6 | 2017-06-16 14:37:43 +0200 | [diff] [blame] | 272 | .testExactOrder("['%s/%s.java' % (z, t) for z in ['foo', 'bar'] " |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 273 | + "for t in ['baz', 'wiz', 'quux']]", |
| 274 | "foo/baz.java", |
| 275 | "foo/wiz.java", |
| 276 | "foo/quux.java", |
| 277 | "bar/baz.java", |
| 278 | "bar/wiz.java", |
| 279 | "bar/quux.java") |
laurentlb | c3a1af6 | 2017-06-16 14:37:43 +0200 | [diff] [blame] | 280 | .testExactOrder("['%s/%s.java' % (b, b) for a in ['foo', 'bar'] " |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 281 | + "for b in ['baz', 'wiz', 'quux']]", |
| 282 | "baz/baz.java", |
| 283 | "wiz/wiz.java", |
| 284 | "quux/quux.java", |
| 285 | "baz/baz.java", |
| 286 | "wiz/wiz.java", |
| 287 | "quux/quux.java") |
laurentlb | c3a1af6 | 2017-06-16 14:37:43 +0200 | [diff] [blame] | 288 | .testExactOrder("['%s/%s.%s' % (c, d, e) for c in ['foo', 'bar'] " |
| 289 | + "for d in ['baz', 'wiz', 'quux'] for e in ['java', 'cc']]", |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 290 | "foo/baz.java", |
| 291 | "foo/baz.cc", |
| 292 | "foo/wiz.java", |
| 293 | "foo/wiz.cc", |
| 294 | "foo/quux.java", |
| 295 | "foo/quux.cc", |
| 296 | "bar/baz.java", |
| 297 | "bar/baz.cc", |
| 298 | "bar/wiz.java", |
| 299 | "bar/wiz.cc", |
| 300 | "bar/quux.java", |
laurentlb | c3a1af6 | 2017-06-16 14:37:43 +0200 | [diff] [blame] | 301 | "bar/quux.cc") |
| 302 | .testExactOrder("[i for i in (1, 2)]", 1, 2) |
| 303 | .testExactOrder("[i for i in [2, 3] or [1, 2]]", 2, 3); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 304 | } |
| 305 | |
Laurent Le Brun | 741824b | 2015-03-20 15:10:19 +0000 | [diff] [blame] | 306 | @Test |
Laurent Le Brun | 5202166 | 2015-05-18 09:28:26 +0000 | [diff] [blame] | 307 | public void testNestedListComprehensions() throws Exception { |
laurentlb | 0d5d3a5 | 2018-12-18 05:26:35 -0800 | [diff] [blame] | 308 | newTest() |
| 309 | .testExactOrder("li = [[1, 2], [3, 4]]\n" + "[j for i in li for j in i]", 1, 2, 3, 4) |
| 310 | .testExactOrder( |
| 311 | "input = [['abc'], ['def', 'ghi']]\n" |
| 312 | + "['%s %s' % (b, c) for a in input for b in a for c in b.elems()]", |
| 313 | "abc a", "abc b", "abc c", "def d", "def e", "def f", "ghi g", "ghi h", "ghi i"); |
Laurent Le Brun | 5202166 | 2015-05-18 09:28:26 +0000 | [diff] [blame] | 314 | } |
| 315 | |
| 316 | @Test |
Laurent Le Brun | 741824b | 2015-03-20 15:10:19 +0000 | [diff] [blame] | 317 | public void testListComprehensionsMultipleVariables() throws Exception { |
Francois-Rene Rideau | 93ed7f1 | 2015-10-20 15:39:33 +0000 | [diff] [blame] | 318 | newTest().testEval("[x + y for x, y in [(1, 2), (3, 4)]]", "[3, 7]") |
| 319 | .testEval("[z + t for (z, t) in [[1, 2], [3, 4]]]", "[3, 7]"); |
Laurent Le Brun | 741824b | 2015-03-20 15:10:19 +0000 | [diff] [blame] | 320 | } |
| 321 | |
| 322 | @Test |
| 323 | public void testListComprehensionsMultipleVariablesFail() throws Exception { |
brandjon | 2b51f78 | 2017-07-25 21:05:04 +0200 | [diff] [blame] | 324 | newTest().testIfErrorContains( |
| 325 | "assignment length mismatch: left-hand side has length 3, but right-hand side evaluates to " |
| 326 | + "value of length 2", |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 327 | "[x + y for x, y, z in [(1, 2), (3, 4)]]").testIfExactError( |
| 328 | "type 'int' is not a collection", "[x + y for x, y in (1, 2)]"); |
Laurent Le Brun | 741824b | 2015-03-20 15:10:19 +0000 | [diff] [blame] | 329 | } |
| 330 | |
| 331 | @Test |
Laurent Le Brun | b4c5474 | 2015-05-18 13:11:05 +0000 | [diff] [blame] | 332 | public void testListComprehensionsWithFiltering() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 333 | newTest() |
| 334 | .setUp("range3 = [0, 1, 2]") |
| 335 | .testEval("[a for a in (4, None, 2, None, 1) if a != None]", "[4, 2, 1]") |
| 336 | .testEval("[b+c for b in [0, 1, 2] for c in [0, 1, 2] if b + c > 2]", "[3, 3, 4]") |
| 337 | .testEval("[d+e for d in range3 if d % 2 == 1 for e in range3]", "[1, 2, 3]") |
| 338 | .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 ]", |
| 339 | "[[2, 6], [3, 8], [4, 6]]") |
| 340 | .testEval("[h for h in [4, 2, 0, 1] if h]", "[4, 2, 1]"); |
Laurent Le Brun | b4c5474 | 2015-05-18 13:11:05 +0000 | [diff] [blame] | 341 | } |
| 342 | |
| 343 | @Test |
| 344 | public void testListComprehensionDefinitionOrder() throws Exception { |
laurentlb | 3a979f7 | 2018-11-20 07:25:11 -0800 | [diff] [blame] | 345 | new BuildTest() |
| 346 | .testIfErrorContains("name 'y' is not defined", "[x for x in (1, 2) if y for y in (3, 4)]"); |
| 347 | |
| 348 | new SkylarkTest() |
| 349 | .testIfErrorContains( |
| 350 | "local variable 'y' is referenced before assignment", |
| 351 | "[x for x in (1, 2) if y for y in (3, 4)]"); |
Laurent Le Brun | b4c5474 | 2015-05-18 13:11:05 +0000 | [diff] [blame] | 352 | } |
| 353 | |
| 354 | @Test |
Laurent Le Brun | 741824b | 2015-03-20 15:10:19 +0000 | [diff] [blame] | 355 | public void testTupleDestructuring() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 356 | newTest() |
| 357 | .setUp("a, b = 1, 2") |
| 358 | .testLookup("a", 1) |
| 359 | .testLookup("b", 2) |
| 360 | .setUp("c, d = {'key1':2, 'key2':3}") |
| 361 | .testLookup("c", "key1") |
| 362 | .testLookup("d", "key2"); |
Laurent Le Brun | 741824b | 2015-03-20 15:10:19 +0000 | [diff] [blame] | 363 | } |
| 364 | |
| 365 | @Test |
Laurent Le Brun | 29ad862 | 2015-09-18 10:45:07 +0000 | [diff] [blame] | 366 | public void testSingleTuple() throws Exception { |
Laurent Le Brun | b639ca8 | 2017-01-17 11:18:23 +0000 | [diff] [blame] | 367 | newTest().setUp("(a,) = [1]").testLookup("a", 1); |
Laurent Le Brun | 29ad862 | 2015-09-18 10:45:07 +0000 | [diff] [blame] | 368 | } |
| 369 | |
| 370 | @Test |
Laurent Le Brun | 2e78d61 | 2015-04-15 09:06:46 +0000 | [diff] [blame] | 371 | public void testHeterogeneousDict() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 372 | newTest().setUp("d = {'str': 1, 2: 3}", "a = d['str']", "b = d[2]").testLookup("a", 1) |
| 373 | .testLookup("b", 3); |
Laurent Le Brun | 2e78d61 | 2015-04-15 09:06:46 +0000 | [diff] [blame] | 374 | } |
| 375 | |
| 376 | @Test |
Laurent Le Brun | 6824d86 | 2015-09-11 13:51:41 +0000 | [diff] [blame] | 377 | public void testAccessDictWithATupleKey() throws Exception { |
| 378 | newTest().setUp("x = {(1, 2): 3}[1, 2]").testLookup("x", 3); |
| 379 | } |
| 380 | |
| 381 | @Test |
laurentlb | 8880cf2 | 2017-06-23 16:05:36 +0200 | [diff] [blame] | 382 | public void testDictWithDuplicatedKey() throws Exception { |
laurentlb | 2195b1c | 2018-02-16 04:14:46 -0800 | [diff] [blame] | 383 | new SkylarkTest() |
laurentlb | 8880cf2 | 2017-06-23 16:05:36 +0200 | [diff] [blame] | 384 | .testIfErrorContains( |
| 385 | "Duplicated key \"str\" when creating dictionary", "{'str': 1, 'x': 2, 'str': 3}"); |
| 386 | } |
| 387 | |
| 388 | @Test |
Laurent Le Brun | 741824b | 2015-03-20 15:10:19 +0000 | [diff] [blame] | 389 | public void testRecursiveTupleDestructuring() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 390 | newTest() |
| 391 | .setUp("((a, b), (c, d)) = [(1, 2), (3, 4)]") |
| 392 | .testLookup("a", 1) |
| 393 | .testLookup("b", 2) |
| 394 | .testLookup("c", 3) |
| 395 | .testLookup("d", 4); |
Laurent Le Brun | 741824b | 2015-03-20 15:10:19 +0000 | [diff] [blame] | 396 | } |
| 397 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 398 | @Test |
laurentlb | ee250c6 | 2017-09-07 19:17:52 +0200 | [diff] [blame] | 399 | public void testListComprehensionAtTopLevel() throws Exception { |
| 400 | // It is allowed to have a loop variable with the same name as a global variable. |
| 401 | newTest().update("x", 42).setUp("y = [x + 1 for x in [1,2,3]]") |
| 402 | .testExactOrder("y", 2, 3, 4); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 403 | } |
| 404 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 405 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 406 | public void testDictComprehensions() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 407 | newTest() |
| 408 | .testStatement("{a : a for a in []}", Collections.emptyMap()) |
| 409 | .testStatement("{b : b for b in [1, 2]}", ImmutableMap.of(1, 1, 2, 2)) |
| 410 | .testStatement("{c : 'v_' + c for c in ['a', 'b']}", |
| 411 | ImmutableMap.of("a", "v_a", "b", "v_b")) |
| 412 | .testStatement("{'k_' + d : d for d in ['a', 'b']}", |
| 413 | ImmutableMap.of("k_a", "a", "k_b", "b")) |
| 414 | .testStatement("{'k_' + e : 'v_' + e for e in ['a', 'b']}", |
| 415 | ImmutableMap.of("k_a", "v_a", "k_b", "v_b")) |
| 416 | .testStatement("{x+y : x*y for x, y in [[2, 3]]}", ImmutableMap.of(5, 6)); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 417 | } |
| 418 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 419 | @Test |
Laurent Le Brun | 2e78d61 | 2015-04-15 09:06:46 +0000 | [diff] [blame] | 420 | public void testDictComprehensionOnNonIterable() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 421 | newTest().testIfExactError("type 'int' is not iterable", "{k : k for k in 3}"); |
Laurent Le Brun | 2e78d61 | 2015-04-15 09:06:46 +0000 | [diff] [blame] | 422 | } |
| 423 | |
| 424 | @Test |
Florian Weikert | ffd8a5a | 2015-09-18 11:51:01 +0000 | [diff] [blame] | 425 | public void testDictComprehension_ManyClauses() throws Exception { |
| 426 | new SkylarkTest().testStatement( |
| 427 | "{x : x * y for x in range(1, 10) if x % 2 == 0 for y in range(1, 10) if y == x}", |
| 428 | ImmutableMap.of(2, 4, 4, 16, 6, 36, 8, 64)); |
| 429 | } |
| 430 | |
| 431 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 432 | public void testDictComprehensions_MultipleKey() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 433 | newTest().testStatement("{x : x for x in [1, 2, 1]}", ImmutableMap.of(1, 1, 2, 2)) |
| 434 | .testStatement("{y : y for y in ['ab', 'c', 'a' + 'b']}", |
| 435 | ImmutableMap.of("ab", "ab", "c", "c")); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 436 | } |
| 437 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 438 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 439 | public void testListConcatenation() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 440 | newTest() |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 441 | .testStatement("[1, 2] + [3, 4]", MutableList.of(thread, 1, 2, 3, 4)) |
Francois-Rene Rideau | 93ed7f1 | 2015-10-20 15:39:33 +0000 | [diff] [blame] | 442 | .testStatement("(1, 2) + (3, 4)", Tuple.of(1, 2, 3, 4)) |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 443 | .testIfExactError( |
| 444 | "unsupported operand type(s) for +: 'list' and 'tuple'", "[1, 2] + (3, 4)") |
| 445 | .testIfExactError( |
| 446 | "unsupported operand type(s) for +: 'tuple' and 'list'", "(1, 2) + [3, 4]"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 447 | } |
| 448 | |
Andreas Bergmeier | a397340 | 2016-12-15 15:33:41 +0000 | [diff] [blame] | 449 | @Test |
| 450 | public void testListMultiply() throws Exception { |
| 451 | newTest() |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 452 | .testStatement("[1, 2, 3] * 1", MutableList.of(thread, 1, 2, 3)) |
| 453 | .testStatement("[1, 2] * 2", MutableList.of(thread, 1, 2, 1, 2)) |
| 454 | .testStatement("[1, 2] * 3", MutableList.of(thread, 1, 2, 1, 2, 1, 2)) |
| 455 | .testStatement("[1, 2] * 4", MutableList.of(thread, 1, 2, 1, 2, 1, 2, 1, 2)) |
| 456 | .testStatement("[8] * 5", MutableList.of(thread, 8, 8, 8, 8, 8)) |
brandjon | a2d101a | 2017-08-01 18:54:28 +0200 | [diff] [blame] | 457 | .testStatement("[ ] * 10", MutableList.empty()) |
| 458 | .testStatement("[1, 2] * 0", MutableList.empty()) |
| 459 | .testStatement("[1, 2] * -4", MutableList.empty()) |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 460 | .testStatement("2 * [1, 2]", MutableList.of(thread, 1, 2, 1, 2)) |
brandjon | a2d101a | 2017-08-01 18:54:28 +0200 | [diff] [blame] | 461 | .testStatement("10 * []", MutableList.empty()) |
laurentlb | 17f8d4e | 2018-05-24 07:32:52 -0700 | [diff] [blame] | 462 | .testStatement("0 * [1, 2]", MutableList.empty()) |
brandjon | a2d101a | 2017-08-01 18:54:28 +0200 | [diff] [blame] | 463 | .testStatement("-4 * [1, 2]", MutableList.empty()); |
Andreas Bergmeier | a397340 | 2016-12-15 15:33:41 +0000 | [diff] [blame] | 464 | } |
| 465 | |
brandjon | 3d40360 | 2017-08-06 20:01:01 +0200 | [diff] [blame] | 466 | @Test |
| 467 | public void testTupleMultiply() throws Exception { |
| 468 | newTest() |
| 469 | .testStatement("(1, 2, 3) * 1", Tuple.of(1, 2, 3)) |
| 470 | .testStatement("(1, 2) * 2", Tuple.of(1, 2, 1, 2)) |
| 471 | .testStatement("(1, 2) * 3", Tuple.of(1, 2, 1, 2, 1, 2)) |
| 472 | .testStatement("(1, 2) * 4", Tuple.of(1, 2, 1, 2, 1, 2, 1, 2)) |
| 473 | .testStatement("(8,) * 5", Tuple.of(8, 8, 8, 8, 8)) |
| 474 | .testStatement("( ) * 10", Tuple.empty()) |
| 475 | .testStatement("(1, 2) * 0", Tuple.empty()) |
| 476 | .testStatement("(1, 2) * -4", Tuple.empty()) |
laurentlb | 17f8d4e | 2018-05-24 07:32:52 -0700 | [diff] [blame] | 477 | .testStatement("2 * (1, 2)", Tuple.of(1, 2, 1, 2)) |
brandjon | 3d40360 | 2017-08-06 20:01:01 +0200 | [diff] [blame] | 478 | .testStatement("10 * ()", Tuple.empty()) |
laurentlb | 17f8d4e | 2018-05-24 07:32:52 -0700 | [diff] [blame] | 479 | .testStatement("0 * (1, 2)", Tuple.empty()) |
brandjon | 3d40360 | 2017-08-06 20:01:01 +0200 | [diff] [blame] | 480 | .testStatement("-4 * (1, 2)", Tuple.empty()); |
| 481 | } |
| 482 | |
Greg Estren | b3dece0 | 2015-05-14 17:18:41 +0000 | [diff] [blame] | 483 | @SuppressWarnings("unchecked") |
| 484 | @Test |
| 485 | public void testSelectorListConcatenation() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 486 | // TODO(fwe): cannot be handled by current testing suite |
Greg Estren | b3dece0 | 2015-05-14 17:18:41 +0000 | [diff] [blame] | 487 | SelectorList x = (SelectorList) eval("select({'foo': ['FOO'], 'bar': ['BAR']}) + []"); |
| 488 | List<Object> elements = x.getElements(); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 489 | assertThat(elements).hasSize(2); |
Greg Estren | b3dece0 | 2015-05-14 17:18:41 +0000 | [diff] [blame] | 490 | assertThat(elements.get(0)).isInstanceOf(SelectorValue.class); |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 491 | assertThat((Iterable<Object>) elements.get(1)).isEmpty(); |
Greg Estren | b3dece0 | 2015-05-14 17:18:41 +0000 | [diff] [blame] | 492 | } |
| 493 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 494 | @Test |
Laurent Le Brun | a58dcd1 | 2015-11-05 13:51:53 +0000 | [diff] [blame] | 495 | public void testAddSelectIncompatibleType() throws Exception { |
| 496 | newTest() |
| 497 | .testIfErrorContains( |
| 498 | "'+' operator applied to incompatible types (select of list, int)", |
| 499 | "select({'foo': ['FOO'], 'bar': ['BAR']}) + 1"); |
| 500 | } |
| 501 | |
| 502 | @Test |
| 503 | public void testAddSelectIncompatibleType2() throws Exception { |
| 504 | newTest() |
| 505 | .testIfErrorContains( |
| 506 | "'+' operator applied to incompatible types (select of list, select of int)", |
| 507 | "select({'foo': ['FOO']}) + select({'bar': 2})"); |
| 508 | } |
| 509 | |
| 510 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 511 | public void testListComprehensionFailsOnNonSequence() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 512 | newTest().testIfErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 513 | } |
| 514 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 515 | @Test |
laurentlb | a752d8b | 2017-07-12 22:12:04 +0200 | [diff] [blame] | 516 | public void testListComprehensionOnStringIsForbidden() throws Exception { |
laurentlb | 26f843d | 2019-02-20 06:44:05 -0800 | [diff] [blame] | 517 | newTest().testIfErrorContains("type 'string' is not iterable", "[x for x in 'abc']"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 518 | } |
| 519 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 520 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 521 | public void testInvalidAssignment() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 522 | newTest().testIfErrorContains( |
Jon Brandvein | 162f9eb | 2016-11-10 00:22:43 +0000 | [diff] [blame] | 523 | "cannot assign to 'x + 1'", "x + 1 = 2"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 524 | } |
| 525 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 526 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 527 | public void testListComprehensionOnDictionary() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 528 | newTest().testExactOrder("val = ['var_' + n for n in {'a':1,'b':2}] ; val", "var_a", "var_b"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 529 | } |
| 530 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 531 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 532 | public void testListComprehensionOnDictionaryCompositeExpression() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 533 | new BuildTest() |
| 534 | .setUp("d = {1:'a',2:'b'}", "l = [d[x] for x in d]") |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 535 | .testLookup("l", MutableList.of(thread, "a", "b")); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 536 | } |
| 537 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 538 | @Test |
Jon Brandvein | 79aa779 | 2016-07-25 16:57:54 +0000 | [diff] [blame] | 539 | public void testListComprehensionUpdate() throws Exception { |
| 540 | new BuildTest() |
| 541 | .setUp("xs = [1, 2, 3]") |
Jon Brandvein | 65e3ae6 | 2016-09-27 19:57:40 +0000 | [diff] [blame] | 542 | .testIfErrorContains("trying to mutate a locked object", |
| 543 | "[xs.append(4) for x in xs]"); |
| 544 | } |
| 545 | |
| 546 | @Test |
| 547 | public void testNestedListComprehensionUpdate() throws Exception { |
| 548 | new BuildTest() |
| 549 | .setUp("xs = [1, 2, 3]") |
| 550 | .testIfErrorContains("trying to mutate a locked object", |
| 551 | "[xs.append(4) for x in xs for y in xs]"); |
| 552 | } |
| 553 | |
| 554 | @Test |
| 555 | public void testListComprehensionUpdateInClause() throws Exception { |
| 556 | new BuildTest() |
| 557 | .setUp("xs = [1, 2, 3]") |
| 558 | .testIfErrorContains("trying to mutate a locked object", |
| 559 | // Use short-circuiting to produce valid output in the event |
| 560 | // the exception is not raised. |
| 561 | "[y for x in xs for y in (xs.append(4) or xs)]"); |
| 562 | } |
| 563 | |
| 564 | @Test |
| 565 | public void testDictComprehensionUpdate() throws Exception { |
| 566 | new BuildTest() |
| 567 | .setUp("xs = {1:1, 2:2, 3:3}") |
| 568 | .testIfErrorContains("trying to mutate a locked object", |
| 569 | "[xs.popitem() for x in xs]"); |
Jon Brandvein | 79aa779 | 2016-07-25 16:57:54 +0000 | [diff] [blame] | 570 | } |
| 571 | |
| 572 | @Test |
Laurent Le Brun | ab0ca1a | 2015-03-31 17:13:25 +0000 | [diff] [blame] | 573 | public void testInOperator() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 574 | newTest() |
| 575 | .testStatement("'b' in ['a', 'b']", Boolean.TRUE) |
| 576 | .testStatement("'c' in ['a', 'b']", Boolean.FALSE) |
| 577 | .testStatement("'b' in ('a', 'b')", Boolean.TRUE) |
| 578 | .testStatement("'c' in ('a', 'b')", Boolean.FALSE) |
| 579 | .testStatement("'b' in {'a' : 1, 'b' : 2}", Boolean.TRUE) |
| 580 | .testStatement("'c' in {'a' : 1, 'b' : 2}", Boolean.FALSE) |
| 581 | .testStatement("1 in {'a' : 1, 'b' : 2}", Boolean.FALSE) |
| 582 | .testStatement("'b' in 'abc'", Boolean.TRUE) |
| 583 | .testStatement("'d' in 'abc'", Boolean.FALSE); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 584 | } |
| 585 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 586 | @Test |
Laurent Le Brun | e3f4ed7 | 2015-05-08 14:47:26 +0000 | [diff] [blame] | 587 | public void testNotInOperator() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 588 | newTest() |
| 589 | .testStatement("'b' not in ['a', 'b']", Boolean.FALSE) |
| 590 | .testStatement("'c' not in ['a', 'b']", Boolean.TRUE) |
| 591 | .testStatement("'b' not in ('a', 'b')", Boolean.FALSE) |
| 592 | .testStatement("'c' not in ('a', 'b')", Boolean.TRUE) |
| 593 | .testStatement("'b' not in {'a' : 1, 'b' : 2}", Boolean.FALSE) |
| 594 | .testStatement("'c' not in {'a' : 1, 'b' : 2}", Boolean.TRUE) |
| 595 | .testStatement("1 not in {'a' : 1, 'b' : 2}", Boolean.TRUE) |
| 596 | .testStatement("'b' not in 'abc'", Boolean.FALSE) |
| 597 | .testStatement("'d' not in 'abc'", Boolean.TRUE); |
Laurent Le Brun | e3f4ed7 | 2015-05-08 14:47:26 +0000 | [diff] [blame] | 598 | } |
| 599 | |
| 600 | @Test |
Laurent Le Brun | ab0ca1a | 2015-03-31 17:13:25 +0000 | [diff] [blame] | 601 | public void testInFail() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 602 | newTest() |
Laurent Le Brun | 8351464 | 2017-03-02 14:12:53 +0000 | [diff] [blame] | 603 | .testIfErrorContains( |
| 604 | "'in <string>' requires string as left operand, not 'int'", "1 in '123'") |
| 605 | .testIfErrorContains("'int' is not iterable. in operator only works on ", "'a' in 1"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 606 | } |
| 607 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 608 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 609 | public void testInCompositeForPrecedence() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 610 | newTest().testStatement("not 'a' in ['a'] or 0", 0); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 611 | } |
| 612 | |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 613 | private SkylarkValue createObjWithStr() { |
| 614 | return new SkylarkValue() { |
| 615 | @Override |
| 616 | public void repr(SkylarkPrinter printer) { |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 617 | printer.append("<str marker>"); |
| 618 | } |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 619 | }; |
| 620 | } |
| 621 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 622 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 623 | public void testPercOnObject() throws Exception { |
vladmos | cd6d8ae | 2017-10-12 15:35:17 +0200 | [diff] [blame] | 624 | newTest() |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 625 | .update("obj", createObjWithStr()) |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 626 | .testStatement("'%s' % obj", "<str marker>"); |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 627 | newTest() |
vladmos | d4cc4b6 | 2017-07-14 19:04:55 +0200 | [diff] [blame] | 628 | .update("unknown", new Object()) |
| 629 | .testStatement("'%s' % unknown", "<unknown object java.lang.Object>"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 630 | } |
| 631 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 632 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 633 | public void testPercOnObjectList() throws Exception { |
vladmos | cd6d8ae | 2017-10-12 15:35:17 +0200 | [diff] [blame] | 634 | newTest() |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 635 | .update("obj", createObjWithStr()) |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 636 | .testStatement("'%s %s' % (obj, obj)", "<str marker> <str marker>"); |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 637 | newTest() |
vladmos | d4cc4b6 | 2017-07-14 19:04:55 +0200 | [diff] [blame] | 638 | .update("unknown", new Object()) |
| 639 | .testStatement( |
| 640 | "'%s %s' % (unknown, unknown)", |
| 641 | "<unknown object java.lang.Object> <unknown object java.lang.Object>"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 642 | } |
| 643 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 644 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 645 | public void testPercOnObjectInvalidFormat() throws Exception { |
vladmos | cd6d8ae | 2017-10-12 15:35:17 +0200 | [diff] [blame] | 646 | newTest() |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 647 | .update("obj", createObjWithStr()) |
| 648 | .testIfExactError("invalid argument <str marker> for format pattern %d", "'%d' % obj"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 649 | } |
| 650 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 651 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 652 | public void testDictKeys() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 653 | newTest().testExactOrder("v = {'a': 1}.keys() + ['b', 'c'] ; v", "a", "b", "c"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 654 | } |
| 655 | |
Han-Wen Nienhuys | ccf19ea | 2015-02-27 15:53:24 +0000 | [diff] [blame] | 656 | @Test |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 657 | public void testDictKeysTooManyArgs() throws Exception { |
cparsons | 0303514 | 2019-01-15 13:35:22 -0800 | [diff] [blame] | 658 | newTest() |
| 659 | .testIfExactError( |
| 660 | "expected no more than 0 positional arguments, but got 1, " |
| 661 | + "for call to method keys() of 'dict'", |
| 662 | "{'a': 1}.keys('abc')"); |
Francois-Rene Rideau | 76023b9 | 2015-04-17 15:31:59 +0000 | [diff] [blame] | 663 | } |
| 664 | |
| 665 | @Test |
| 666 | public void testDictKeysTooManyKeyArgs() throws Exception { |
cparsons | 0303514 | 2019-01-15 13:35:22 -0800 | [diff] [blame] | 667 | newTest() |
| 668 | .testIfExactError( |
| 669 | "unexpected keyword 'arg', for call to method keys() of 'dict'", |
| 670 | "{'a': 1}.keys(arg='abc')"); |
Francois-Rene Rideau | 76023b9 | 2015-04-17 15:31:59 +0000 | [diff] [blame] | 671 | } |
| 672 | |
| 673 | @Test |
| 674 | public void testDictKeysDuplicateKeyArgs() throws Exception { |
fzaiser | 8c27a89 | 2017-08-11 17:26:44 +0200 | [diff] [blame] | 675 | newTest().testIfExactError("duplicate keywords 'arg', 'k' in call to {\"a\": 1}.keys", |
Francois-Rene Rideau | 76023b9 | 2015-04-17 15:31:59 +0000 | [diff] [blame] | 676 | "{'a': 1}.keys(arg='abc', arg='def', k=1, k=2)"); |
| 677 | } |
| 678 | |
| 679 | @Test |
| 680 | public void testArgBothPosKey() throws Exception { |
cparsons | 0303514 | 2019-01-15 13:35:22 -0800 | [diff] [blame] | 681 | newTest() |
| 682 | .testIfErrorContains( |
cparsons | 65ecfea | 2019-08-28 14:37:23 -0700 | [diff] [blame] | 683 | "got multiple values for keyword argument 'base', " |
| 684 | + "for call to function int(x, base = unbound)", |
| 685 | "int('2', 3, base=3)"); |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 686 | } |
laurentlb | ab06291 | 2019-04-12 07:00:20 -0700 | [diff] [blame] | 687 | |
| 688 | @Test |
laurentlb | ed63333 | 2019-04-12 16:34:08 -0700 | [diff] [blame] | 689 | public void testStaticNameResolution() throws Exception { |
laurentlb | fdc2da7 | 2019-06-13 08:43:24 -0700 | [diff] [blame] | 690 | newTest().testIfErrorContains("name 'foo' is not defined", "[foo for x in []]"); |
laurentlb | ed63333 | 2019-04-12 16:34:08 -0700 | [diff] [blame] | 691 | } |
| 692 | |
| 693 | @Test |
laurentlb | ab06291 | 2019-04-12 07:00:20 -0700 | [diff] [blame] | 694 | public void testDefInBuild() throws Exception { |
| 695 | new BuildTest() |
| 696 | .testIfErrorContains( |
| 697 | "function definitions are not allowed in BUILD files", "def func(): pass"); |
| 698 | } |
| 699 | |
| 700 | @Test |
| 701 | public void testForStatementForbiddenInBuild() throws Exception { |
| 702 | new BuildTest().testIfErrorContains("for loops are not allowed", "for _ in []: pass"); |
| 703 | } |
| 704 | |
| 705 | @Test |
| 706 | public void testIfStatementForbiddenInBuild() throws Exception { |
laurentlb | cc7a6e0 | 2019-05-22 14:30:47 -0700 | [diff] [blame] | 707 | new BuildTest().testIfErrorContains("if statements are not allowed", "if False: pass"); |
laurentlb | ab06291 | 2019-04-12 07:00:20 -0700 | [diff] [blame] | 708 | } |
| 709 | |
| 710 | @Test |
| 711 | public void testKwargsForbiddenInBuild() throws Exception { |
| 712 | new BuildTest() |
laurentlb | cc7a6e0 | 2019-05-22 14:30:47 -0700 | [diff] [blame] | 713 | .testIfErrorContains("**kwargs arguments are not allowed in BUILD files", "print(**dict)"); |
laurentlb | 3b7e294 | 2019-06-13 09:51:47 -0700 | [diff] [blame] | 714 | |
| 715 | new BuildTest() |
| 716 | .testIfErrorContains( |
| 717 | "**kwargs arguments are not allowed in BUILD files", "len(dict(**{'a': 1}))"); |
laurentlb | ab06291 | 2019-04-12 07:00:20 -0700 | [diff] [blame] | 718 | } |
| 719 | |
| 720 | @Test |
| 721 | public void testArgsForbiddenInBuild() throws Exception { |
| 722 | new BuildTest() |
laurentlb | cc7a6e0 | 2019-05-22 14:30:47 -0700 | [diff] [blame] | 723 | .testIfErrorContains("*args arguments are not allowed in BUILD files", "print(*['a'])"); |
laurentlb | ab06291 | 2019-04-12 07:00:20 -0700 | [diff] [blame] | 724 | } |
Ulf Adams | 89f012d | 2015-02-26 13:39:28 +0000 | [diff] [blame] | 725 | } |