1# Tests of Starlark 'list'
2
3load("assert.star", "assert", "freeze")
4
5# literals
6assert.eq([], [])
7assert.eq([1], [1])
8assert.eq([1], [1])
9assert.eq([1, 2], [1, 2])
10assert.ne([1, 2, 3], [1, 2, 4])
11
12# truth
13assert.true([0])
14assert.true(not [])
15
16# indexing, x[i]
17abc = list("abc".elems())
18assert.fails(lambda: abc[-4], "list index -4 out of range \\[-3:2]")
19assert.eq(abc[-3], "a")
20assert.eq(abc[-2], "b")
21assert.eq(abc[-1], "c")
22assert.eq(abc[0], "a")
23assert.eq(abc[1], "b")
24assert.eq(abc[2], "c")
25assert.fails(lambda: abc[3], "list index 3 out of range \\[-3:2]")
26
27# x[i] = ...
28x3 = [0, 1, 2]
29x3[1] = 2
30x3[2] += 3
31assert.eq(x3, [0, 2, 5])
32
33def f2():
34 x3[3] = 4
35
36assert.fails(f2, "out of range")
37freeze(x3)
38
39def f3():
40 x3[0] = 0
41
42assert.fails(f3, "cannot assign to element of frozen list")
43assert.fails(x3.clear, "cannot clear frozen list")
44
45# list + list
46assert.eq([1, 2, 3] + [3, 4, 5], [1, 2, 3, 3, 4, 5])
47assert.fails(lambda: [1, 2] + (3, 4), "unknown.*list \\+ tuple")
48assert.fails(lambda: (1, 2) + [3, 4], "unknown.*tuple \\+ list")
49
50# list * int, int * list
51assert.eq(abc * 0, [])
52assert.eq(abc * -1, [])
53assert.eq(abc * 1, abc)
54assert.eq(abc * 3, ["a", "b", "c", "a", "b", "c", "a", "b", "c"])
55assert.eq(0 * abc, [])
56assert.eq(-1 * abc, [])
57assert.eq(1 * abc, abc)
58assert.eq(3 * abc, ["a", "b", "c", "a", "b", "c", "a", "b", "c"])
59
60# list comprehensions
61assert.eq([2 * x for x in [1, 2, 3]], [2, 4, 6])
62assert.eq([2 * x for x in [1, 2, 3] if x > 1], [4, 6])
63assert.eq(
64 [(x, y) for x in [1, 2] for y in [3, 4]],
65 [(1, 3), (1, 4), (2, 3), (2, 4)],
66)
67assert.eq([(x, y) for x in [1, 2] if x == 2 for y in [3, 4]], [(2, 3), (2, 4)])
68assert.eq([2 * x for x in (1, 2, 3)], [2, 4, 6])
69assert.eq([x for x in "abc".elems()], ["a", "b", "c"])
70assert.eq([x for x in {"a": 1, "b": 2}], ["a", "b"])
71assert.eq([(y, x) for x, y in {1: 2, 3: 4}.items()], [(2, 1), (4, 3)])
72
73# corner cases of parsing:
74assert.eq([x for x in range(12) if x % 2 == 0 if x % 3 == 0], [0, 6])
75assert.eq([x for x in [1, 2] if lambda: None], [1, 2])
76assert.eq([x for x in [1, 2] if (lambda: 3 if True else 4)], [1, 2])
77
78# list function
79assert.eq(list(), [])
80assert.eq(list("ab".elems()), ["a", "b"])
81
82# A list comprehension defines a separate lexical block,
83# whether at top-level...
84a = [1, 2]
85b = [a for a in [3, 4]]
86assert.eq(a, [1, 2])
87assert.eq(b, [3, 4])
88
89# ...or local to a function.
90def listcompblock():
91 c = [1, 2]
92 d = [c for c in [3, 4]]
93 assert.eq(c, [1, 2])
94 assert.eq(d, [3, 4])
95
96listcompblock()
97
98# list.pop
99x4 = [1, 2, 3, 4, 5]
100assert.fails(lambda: x4.pop(-6), "index -6 out of range \\[-5:4]")
101assert.fails(lambda: x4.pop(6), "index 6 out of range \\[-5:4]")
102assert.eq(x4.pop(), 5)
103assert.eq(x4, [1, 2, 3, 4])
104assert.eq(x4.pop(1), 2)
105assert.eq(x4, [1, 3, 4])
106assert.eq(x4.pop(0), 1)
107assert.eq(x4, [3, 4])
108assert.eq(x4.pop(-2), 3)
109assert.eq(x4, [4])
110assert.eq(x4.pop(-1), 4)
111assert.eq(x4, [])
112
113# TODO(adonovan): test uses of list as sequence
114# (for loop, comprehension, library functions).
115
116# x += y for lists is equivalent to x.extend(y).
117# y may be a sequence.
118# TODO: Test that side-effects of 'x' occur only once.
119def list_extend():
120 a = [1, 2, 3]
121 b = a
122 a = a + [4] # creates a new list
123 assert.eq(a, [1, 2, 3, 4])
124 assert.eq(b, [1, 2, 3]) # b is unchanged
125
126 a = [1, 2, 3]
127 b = a
128 a += [4] # updates a (and thus b) in place
129 assert.eq(a, [1, 2, 3, 4])
130 assert.eq(b, [1, 2, 3, 4]) # alias observes the change
131
132 a = [1, 2, 3]
133 b = a
134 a.extend([4]) # updates existing list
135 assert.eq(a, [1, 2, 3, 4])
136 assert.eq(b, [1, 2, 3, 4]) # alias observes the change
137
138list_extend()
139
140# Unlike list.extend(iterable), list += iterable makes its LHS name local.
141a_list = []
142
143def f4():
144 a_list += [1] # binding use => a_list is a local var
145
146assert.fails(f4, "local variable a_list referenced before assignment")
147
148# list += <not iterable>
149def f5():
150 x = []
151 x += 1
152
153assert.fails(f5, "unknown binary op: list \\+ int")
154
155# frozen list += iterable
156def f6():
157 x = []
158 freeze(x)
159 x += [1]
160
161assert.fails(f6, "cannot apply \\+= to frozen list")
162
163# list += hasfields (hasfields is not iterable but defines list+hasfields)
164def f7():
165 x = []
166 x += hasfields()
167 return x
168
169assert.eq(f7(), 42) # weird, but exercises a corner case in list+=x.
170
171# append
172x5 = [1, 2, 3]
173x5.append(4)
174x5.append("abc")
175assert.eq(x5, [1, 2, 3, 4, "abc"])
176
177# extend
178x5a = [1, 2, 3]
179x5a.extend("abc".elems()) # string
180x5a.extend((True, False)) # tuple
181assert.eq(x5a, [1, 2, 3, "a", "b", "c", True, False])
182
183# list.insert
184def insert_at(index):
185 x = list(range(3))
186 x.insert(index, 42)
187 return x
188
189assert.eq(insert_at(-99), [42, 0, 1, 2])
190assert.eq(insert_at(-2), [0, 42, 1, 2])
191assert.eq(insert_at(-1), [0, 1, 42, 2])
192assert.eq(insert_at(0), [42, 0, 1, 2])
193assert.eq(insert_at(1), [0, 42, 1, 2])
194assert.eq(insert_at(2), [0, 1, 42, 2])
195assert.eq(insert_at(3), [0, 1, 2, 42])
196assert.eq(insert_at(4), [0, 1, 2, 42])
197
198# list.remove
199def remove(v):
200 x = [3, 1, 4, 1]
201 x.remove(v)
202 return x
203
204assert.eq(remove(3), [1, 4, 1])
205assert.eq(remove(1), [3, 4, 1])
206assert.eq(remove(4), [3, 1, 1])
207assert.fails(lambda: [3, 1, 4, 1].remove(42), "remove: element not found")
208
209# list.index
210bananas = list("bananas".elems())
211assert.eq(bananas.index("a"), 1) # bAnanas
212assert.fails(lambda: bananas.index("d"), "value not in list")
213
214# start
215assert.eq(bananas.index("a", -1000), 1) # bAnanas
216assert.eq(bananas.index("a", 0), 1) # bAnanas
217assert.eq(bananas.index("a", 1), 1) # bAnanas
218assert.eq(bananas.index("a", 2), 3) # banAnas
219assert.eq(bananas.index("a", 3), 3) # banAnas
220assert.eq(bananas.index("b", 0), 0) # Bananas
221assert.eq(bananas.index("n", -3), 4) # banaNas
222assert.fails(lambda: bananas.index("n", -2), "value not in list")
223assert.eq(bananas.index("s", -2), 6) # bananaS
224assert.fails(lambda: bananas.index("b", 1), "value not in list")
225
226# start, end
227assert.eq(bananas.index("s", -1000, 7), 6) # bananaS
228assert.fails(lambda: bananas.index("s", -1000, 6), "value not in list")
229assert.fails(lambda: bananas.index("d", -1000, 1000), "value not in list")
230
231# slicing, x[i:j:k]
232assert.eq(bananas[6::-2], list("snnb".elems()))
233assert.eq(bananas[5::-2], list("aaa".elems()))
234assert.eq(bananas[4::-2], list("nnb".elems()))
235assert.eq(bananas[99::-2], list("snnb".elems()))
236assert.eq(bananas[100::-2], list("snnb".elems()))
237# TODO(adonovan): many more tests
238
239# iterator invalidation
240def iterator1():
241 list = [0, 1, 2]
242 for x in list:
243 list[x] = 2 * x
244 return list
245
246assert.fails(iterator1, "assign to element.* during iteration")
247
248def iterator2():
249 list = [0, 1, 2]
250 for x in list:
251 list.remove(x)
252
253assert.fails(iterator2, "remove.*during iteration")
254
255def iterator3():
256 list = [0, 1, 2]
257 for x in list:
258 list.append(3)
259
260assert.fails(iterator3, "append.*during iteration")
261
262def iterator4():
263 list = [0, 1, 2]
264 for x in list:
265 list.extend([3, 4])
266
267assert.fails(iterator4, "extend.*during iteration")
268
269def iterator5():
270 def f(x):
271 x.append(4)
272
273 list = [1, 2, 3]
274 _ = [f(list) for x in list]
275
276assert.fails(iterator5, "append.*during iteration")
View as plain text