You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
6.3 KiB
249 lines
6.3 KiB
# 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),
|
|
]
|
|
}
|