blob: 1aeb1e7c560ecd5c2eef32305a121653d3257889 [file] [log] [blame]
# Tests of Starlark 'dict'
load("assert.star", "assert", "freeze")
# literals
assert.eq({}, {})
assert.eq({"a": 1}, {"a": 1})
assert.eq({"a": 1,}, {"a": 1})
# truth
assert.true({False: False})
assert.true(not {})
# dict + dict is no longer supported.
assert.fails(lambda: {"a": 1} + {"b": 2}, 'unknown binary op: dict \\+ dict')
# dict comprehension
assert.eq({x: x*x for x in range(3)}, {0: 0, 1: 1, 2: 4})
# dict.pop
x6 = {"a": 1, "b": 2}
assert.eq(x6.pop("a"), 1)
assert.eq(str(x6), '{"b": 2}')
assert.fails(lambda: x6.pop("c"), "pop: missing key")
assert.eq(x6.pop("c", 3), 3)
assert.eq(x6.pop("c", None), None) # default=None tests an edge case of UnpackArgs
assert.eq(x6.pop("b"), 2)
assert.eq(len(x6), 0)
# dict.popitem
x7 = {"a": 1, "b": 2}
assert.eq([x7.popitem(), x7.popitem()], [("a", 1), ("b", 2)])
assert.fails(x7.popitem, "empty dict")
assert.eq(len(x7), 0)
# dict.keys, dict.values
x8 = {"a": 1, "b": 2}
assert.eq(x8.keys(), ["a", "b"])
assert.eq(x8.values(), [1, 2])
# equality
assert.eq({"a": 1, "b": 2}, {"a": 1, "b": 2})
assert.eq({"a": 1, "b": 2,}, {"a": 1, "b": 2})
assert.eq({"a": 1, "b": 2}, {"b": 2, "a": 1})
# insertion order is preserved
assert.eq(dict([("a", 0), ("b", 1), ("c", 2), ("b", 3)]).keys(), ["a", "b", "c"])
assert.eq(dict([("b", 0), ("a", 1), ("b", 2), ("c", 3)]).keys(), ["b", "a", "c"])
assert.eq(dict([("b", 0), ("a", 1), ("b", 2), ("c", 3)])["b"], 2)
# ...even after rehashing (which currently occurs after key 'i'):
small = dict([("a", 0), ("b", 1), ("c", 2)])
small.update([("d", 4), ("e", 5), ("f", 6), ("g", 7), ("h", 8), ("i", 9), ("j", 10), ("k", 11)])
assert.eq(small.keys(), ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"])
# Duplicate keys are not permitted in dictionary expressions (see b/35698444).
# (Nor in keyword args to function calls---checked by resolver.)
assert.fails(lambda: {"aa": 1, "bb": 2, "cc": 3, "bb": 4}, 'duplicate key: "bb"')
# Check that even with many positional args, keyword collisions are detected.
assert.fails(lambda: dict({'b': 3}, a=4, **dict(a=5)), 'dict: duplicate keyword arg: "a"')
assert.fails(lambda: dict({'a': 2, 'b': 3}, a=4, **dict(a=5)), 'dict: duplicate keyword arg: "a"')
# positional/keyword arg key collisions are ok
assert.eq(dict((['a', 2], ), a=4), {'a': 4})
assert.eq(dict((['a', 2], ['a', 3]), a=4), {'a': 4})
# index
def setIndex(d, k, v):
d[k] = v
x9 = {}
assert.fails(lambda: x9["a"], 'key "a" not in dict')
x9["a"] = 1
assert.eq(x9["a"], 1)
assert.eq(x9, {"a": 1})
assert.fails(lambda: setIndex(x9, [], 2), 'unhashable type: list')
freeze(x9)
assert.fails(lambda: setIndex(x9, "a", 3), 'cannot insert into frozen hash table')
x9a = {}
x9a[1, 2] = 3 # unparenthesized tuple is allowed here
assert.eq(x9a.keys()[0], (1, 2))
# dict.get
x10 = {"a": 1}
assert.eq(x10.get("a"), 1)
assert.eq(x10.get("b"), None)
assert.eq(x10.get("a", 2), 1)
assert.eq(x10.get("b", 2), 2)
# dict.clear
x11 = {"a": 1}
assert.contains(x11, "a")
assert.eq(x11["a"], 1)
x11.clear()
assert.fails(lambda: x11["a"], 'key "a" not in dict')
assert.true("a" not in x11)
freeze(x11)
assert.fails(x11.clear, "cannot clear frozen hash table")
# dict.setdefault
x12 = {"a": 1}
assert.eq(x12.setdefault("a"), 1)
assert.eq(x12["a"], 1)
assert.eq(x12.setdefault("b"), None)
assert.eq(x12["b"], None)
assert.eq(x12.setdefault("c", 2), 2)
assert.eq(x12["c"], 2)
assert.eq(x12.setdefault("c", 3), 2)
assert.eq(x12["c"], 2)
freeze(x12)
assert.eq(x12.setdefault("a", 1), 1) # no change, no error
assert.fails(lambda: x12.setdefault("d", 1), "cannot insert into frozen hash table")
# dict.update
x13 = {"a": 1}
x13.update(a=2, b=3)
assert.eq(x13, {"a": 2, "b": 3})
x13.update([("b", 4), ("c", 5)])
assert.eq(x13, {"a": 2, "b": 4, "c": 5})
x13.update({"c": 6, "d": 7})
assert.eq(x13, {"a": 2, "b": 4, "c": 6, "d": 7})
freeze(x13)
assert.fails(lambda: x13.update({"a": 8}), "cannot insert into frozen hash table")
# dict as a sequence
#
# for loop
x14 = {1:2, 3:4}
def keys(dict):
keys = []
for k in dict: keys.append(k)
return keys
assert.eq(keys(x14), [1, 3])
#
# comprehension
assert.eq([x for x in x14], [1, 3])
#
# varargs
def varargs(*args): return args
x15 = {"one": 1}
assert.eq(varargs(*x15), ("one",))
# kwargs parameter does not alias the **kwargs dict
def kwargs(**kwargs): return kwargs
x16 = kwargs(**x15)
assert.eq(x16, x15)
x15["two"] = 2 # mutate
assert.ne(x16, x15)
# iterator invalidation
def iterator1():
dict = {1:1, 2:1}
for k in dict:
dict[2*k] = dict[k]
assert.fails(iterator1, "insert.*during iteration")
def iterator2():
dict = {1:1, 2:1}
for k in dict:
dict.pop(k)
assert.fails(iterator2, "delete.*during iteration")
def iterator3():
def f(d):
d[3] = 3
dict = {1:1, 2:1}
_ = [f(dict) for x in dict]
assert.fails(iterator3, "insert.*during iteration")
# This assignment is not a modification-during-iteration:
# the sequence x should be completely iterated before
# the assignment occurs.
def f():
x = {1:2, 2:4}
a, x[0] = x
assert.eq(a, 1)
assert.eq(x, {1: 2, 2: 4, 0: 2})
f()
# Regression test for a bug in hashtable.delete
def test_delete():
d = {}
# delete tail first
d["one"] = 1
d["two"] = 2
assert.eq(str(d), '{"one": 1, "two": 2}')
d.pop("two")
assert.eq(str(d), '{"one": 1}')
d.pop("one")
assert.eq(str(d), '{}')
# delete head first
d["one"] = 1
d["two"] = 2
assert.eq(str(d), '{"one": 1, "two": 2}')
d.pop("one")
assert.eq(str(d), '{"two": 2}')
d.pop("two")
assert.eq(str(d), '{}')
# delete middle
d["one"] = 1
d["two"] = 2
d["three"] = 3
assert.eq(str(d), '{"one": 1, "two": 2, "three": 3}')
d.pop("two")
assert.eq(str(d), '{"one": 1, "three": 3}')
d.pop("three")
assert.eq(str(d), '{"one": 1}')
d.pop("one")
assert.eq(str(d), '{}')
test_delete()
# Regression test for github.com/google/starlark-go/issues/128.
assert.fails(lambda: dict(None), 'got NoneType, want iterable')
assert.fails(lambda: {}.update(None), 'got NoneType, want iterable')
---
# Verify position of an "unhashable key" error in a dict literal.
_ = {
"one": 1,
["two"]: 2, ### "unhashable type: list"
"three": 3,
}
---
# Verify position of a "duplicate key" error in a dict literal.
_ = {
"one": 1,
"one": 1, ### `duplicate key: "one"`
"three": 3,
}
---
# Verify position of an "unhashable key" error in a dict comprehension.
_ = {
k: v ### "unhashable type: list"
for k, v in [
("one", 1),
(["two"], 2),
("three", 3),
]
}