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") 277