1# Tests of Starlark 'dict' 2 3load("assert.star", "assert", "freeze") 4 5# literals 6assert.eq({}, {}) 7assert.eq({"a": 1}, {"a": 1}) 8assert.eq({"a": 1,}, {"a": 1}) 9 10# truth 11assert.true({False: False}) 12assert.true(not {}) 13 14# dict + dict is no longer supported. 15assert.fails(lambda: {"a": 1} + {"b": 2}, 'unknown binary op: dict \\+ dict') 16 17# dict comprehension 18assert.eq({x: x*x for x in range(3)}, {0: 0, 1: 1, 2: 4}) 19 20# dict.pop 21x6 = {"a": 1, "b": 2} 22assert.eq(x6.pop("a"), 1) 23assert.eq(str(x6), '{"b": 2}') 24assert.fails(lambda: x6.pop("c"), "pop: missing key") 25assert.eq(x6.pop("c", 3), 3) 26assert.eq(x6.pop("c", None), None) # default=None tests an edge case of UnpackArgs 27assert.eq(x6.pop("b"), 2) 28assert.eq(len(x6), 0) 29 30# dict.popitem 31x7 = {"a": 1, "b": 2} 32assert.eq([x7.popitem(), x7.popitem()], [("a", 1), ("b", 2)]) 33assert.fails(x7.popitem, "empty dict") 34assert.eq(len(x7), 0) 35 36# dict.keys, dict.values 37x8 = {"a": 1, "b": 2} 38assert.eq(x8.keys(), ["a", "b"]) 39assert.eq(x8.values(), [1, 2]) 40 41# equality 42assert.eq({"a": 1, "b": 2}, {"a": 1, "b": 2}) 43assert.eq({"a": 1, "b": 2,}, {"a": 1, "b": 2}) 44assert.eq({"a": 1, "b": 2}, {"b": 2, "a": 1}) 45 46# insertion order is preserved 47assert.eq(dict([("a", 0), ("b", 1), ("c", 2), ("b", 3)]).keys(), ["a", "b", "c"]) 48assert.eq(dict([("b", 0), ("a", 1), ("b", 2), ("c", 3)]).keys(), ["b", "a", "c"]) 49assert.eq(dict([("b", 0), ("a", 1), ("b", 2), ("c", 3)])["b"], 2) 50# ...even after rehashing (which currently occurs after key 'i'): 51small = dict([("a", 0), ("b", 1), ("c", 2)]) 52small.update([("d", 4), ("e", 5), ("f", 6), ("g", 7), ("h", 8), ("i", 9), ("j", 10), ("k", 11)]) 53assert.eq(small.keys(), ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]) 54 55# Duplicate keys are not permitted in dictionary expressions (see b/35698444). 56# (Nor in keyword args to function calls---checked by resolver.) 57assert.fails(lambda: {"aa": 1, "bb": 2, "cc": 3, "bb": 4}, 'duplicate key: "bb"') 58 59# Check that even with many positional args, keyword collisions are detected. 60assert.fails(lambda: dict({'b': 3}, a=4, **dict(a=5)), 'dict: duplicate keyword arg: "a"') 61assert.fails(lambda: dict({'a': 2, 'b': 3}, a=4, **dict(a=5)), 'dict: duplicate keyword arg: "a"') 62# positional/keyword arg key collisions are ok 63assert.eq(dict((['a', 2], ), a=4), {'a': 4}) 64assert.eq(dict((['a', 2], ['a', 3]), a=4), {'a': 4}) 65 66# index 67def setIndex(d, k, v): 68 d[k] = v 69 70x9 = {} 71assert.fails(lambda: x9["a"], 'key "a" not in dict') 72x9["a"] = 1 73assert.eq(x9["a"], 1) 74assert.eq(x9, {"a": 1}) 75assert.fails(lambda: setIndex(x9, [], 2), 'unhashable type: list') 76freeze(x9) 77assert.fails(lambda: setIndex(x9, "a", 3), 'cannot insert into frozen hash table') 78 79x9a = {} 80x9a[1, 2] = 3 # unparenthesized tuple is allowed here 81assert.eq(x9a.keys()[0], (1, 2)) 82 83# dict.get 84x10 = {"a": 1} 85assert.eq(x10.get("a"), 1) 86assert.eq(x10.get("b"), None) 87assert.eq(x10.get("a", 2), 1) 88assert.eq(x10.get("b", 2), 2) 89 90# dict.clear 91x11 = {"a": 1} 92assert.contains(x11, "a") 93assert.eq(x11["a"], 1) 94x11.clear() 95assert.fails(lambda: x11["a"], 'key "a" not in dict') 96assert.true("a" not in x11) 97freeze(x11) 98assert.fails(x11.clear, "cannot clear frozen hash table") 99 100# dict.setdefault 101x12 = {"a": 1} 102assert.eq(x12.setdefault("a"), 1) 103assert.eq(x12["a"], 1) 104assert.eq(x12.setdefault("b"), None) 105assert.eq(x12["b"], None) 106assert.eq(x12.setdefault("c", 2), 2) 107assert.eq(x12["c"], 2) 108assert.eq(x12.setdefault("c", 3), 2) 109assert.eq(x12["c"], 2) 110freeze(x12) 111assert.eq(x12.setdefault("a", 1), 1) # no change, no error 112assert.fails(lambda: x12.setdefault("d", 1), "cannot insert into frozen hash table") 113 114# dict.update 115x13 = {"a": 1} 116x13.update(a=2, b=3) 117assert.eq(x13, {"a": 2, "b": 3}) 118x13.update([("b", 4), ("c", 5)]) 119assert.eq(x13, {"a": 2, "b": 4, "c": 5}) 120x13.update({"c": 6, "d": 7}) 121assert.eq(x13, {"a": 2, "b": 4, "c": 6, "d": 7}) 122freeze(x13) 123assert.fails(lambda: x13.update({"a": 8}), "cannot insert into frozen hash table") 124 125# dict as a sequence 126# 127# for loop 128x14 = {1:2, 3:4} 129def keys(dict): 130 keys = [] 131 for k in dict: keys.append(k) 132 return keys 133assert.eq(keys(x14), [1, 3]) 134# 135# comprehension 136assert.eq([x for x in x14], [1, 3]) 137# 138# varargs 139def varargs(*args): return args 140x15 = {"one": 1} 141assert.eq(varargs(*x15), ("one",)) 142 143# kwargs parameter does not alias the **kwargs dict 144def kwargs(**kwargs): return kwargs 145x16 = kwargs(**x15) 146assert.eq(x16, x15) 147x15["two"] = 2 # mutate 148assert.ne(x16, x15) 149 150# iterator invalidation 151def iterator1(): 152 dict = {1:1, 2:1} 153 for k in dict: 154 dict[2*k] = dict[k] 155assert.fails(iterator1, "insert.*during iteration") 156 157def iterator2(): 158 dict = {1:1, 2:1} 159 for k in dict: 160 dict.pop(k) 161assert.fails(iterator2, "delete.*during iteration") 162 163def iterator3(): 164 def f(d): 165 d[3] = 3 166 dict = {1:1, 2:1} 167 _ = [f(dict) for x in dict] 168assert.fails(iterator3, "insert.*during iteration") 169 170# This assignment is not a modification-during-iteration: 171# the sequence x should be completely iterated before 172# the assignment occurs. 173def f(): 174 x = {1:2, 2:4} 175 a, x[0] = x 176 assert.eq(a, 1) 177 assert.eq(x, {1: 2, 2: 4, 0: 2}) 178f() 179 180# Regression test for a bug in hashtable.delete 181def test_delete(): 182 d = {} 183 184 # delete tail first 185 d["one"] = 1 186 d["two"] = 2 187 assert.eq(str(d), '{"one": 1, "two": 2}') 188 d.pop("two") 189 assert.eq(str(d), '{"one": 1}') 190 d.pop("one") 191 assert.eq(str(d), '{}') 192 193 # delete head first 194 d["one"] = 1 195 d["two"] = 2 196 assert.eq(str(d), '{"one": 1, "two": 2}') 197 d.pop("one") 198 assert.eq(str(d), '{"two": 2}') 199 d.pop("two") 200 assert.eq(str(d), '{}') 201 202 # delete middle 203 d["one"] = 1 204 d["two"] = 2 205 d["three"] = 3 206 assert.eq(str(d), '{"one": 1, "two": 2, "three": 3}') 207 d.pop("two") 208 assert.eq(str(d), '{"one": 1, "three": 3}') 209 d.pop("three") 210 assert.eq(str(d), '{"one": 1}') 211 d.pop("one") 212 assert.eq(str(d), '{}') 213 214test_delete() 215 216# Regression test for github.com/google/starlark-go/issues/128. 217assert.fails(lambda: dict(None), 'got NoneType, want iterable') 218assert.fails(lambda: {}.update(None), 'got NoneType, want iterable') 219 220--- 221# Verify position of an "unhashable key" error in a dict literal. 222 223_ = { 224 "one": 1, 225 ["two"]: 2, ### "unhashable type: list" 226 "three": 3, 227} 228 229--- 230# Verify position of a "duplicate key" error in a dict literal. 231 232_ = { 233 "one": 1, 234 "one": 1, ### `duplicate key: "one"` 235 "three": 3, 236} 237 238--- 239# Verify position of an "unhashable key" error in a dict comprehension. 240 241_ = { 242 k: v ### "unhashable type: list" 243 for k, v in [ 244 ("one", 1), 245 (["two"], 2), 246 ("three", 3), 247 ] 248} 249