def test_resize(scenario): hd = HopscotchDict() if scenario == "bad_size": with pytest.raises(ValueError): hd._resize(25) elif scenario == "too_large": with pytest.raises(ValueError): hd._resize(2**65) elif scenario == "nbhd_inc": for i in range(32): hd[f"test_resize_{i}"] = i hd._resize(512) assert hd._nbhd_size == 16 for i in range(32): assert hd[f"test_resize_{i}"] == i elif scenario == "rsz_col": hd[1] = "test_1" hd[17] = "test_17" hd._resize(16) assert hd[1] == "test_1" assert hd[17] == "test_17"
def test_symmetric_difference(): hd = HopscotchDict() keys = hd.keys() items = hd.items() hd["a"] = 1 hd["b"] = 2 hd["c"] = 3 s1 = ["a", "b"] s2 = ["a", "b", "c", "d"] s3 = ["d", "e", "f"] s4 = [1, 2] s5 = [1, 2, 3, 4] s6 = [4, 5, 6] assert keys ^ set(s1) == {"c"} assert keys.symmetric_difference(s2) == {"d"} assert items ^ zip(s1, s4) == {("c", 3)} assert items.symmetric_difference(zip(s2, s5)) == {("d", 4)} assert keys ^ set(s3) == keys | set(s3) assert keys.symmetric_difference(s3) == keys | set(s3) assert items ^ zip(s3, s6) == items | zip(s3, s6) assert items.symmetric_difference(zip(s3, s6)) == items | zip(s3, s6)
def test_contains_and_has_key(gen_dict): hd = HopscotchDict(gen_dict) for key in hd._keys: assert key in hd assert "test_contains" not in hd assert not hd.has_key("test_contains") # noqa
def test_intersection(): hd = HopscotchDict() keys = hd.keys() items = hd.items() hd["a"] = 1 hd["b"] = 2 hd["c"] = 3 s1 = ["a", "b"] s2 = ["a", "b", "c", "d"] s3 = ["d", "e", "f"] s4 = [1, 2] s5 = [1, 2, 3, 4] s6 = [4, 5, 6] assert keys & set(s1) == set(s1) assert keys.intersection(s2) == keys assert items & zip(s1, s4) == set(zip(s1, s4)) assert items.intersection(zip(s2, s5)) == items assert keys & set(s3) == set() assert keys.intersection(s3) == set() assert items & zip(s3, s6) == set() assert items.intersection(zip(s3, s6)) == set()
def test_copy(gen_dict): hd = HopscotchDict(gen_dict) hdc = hd.copy() for key in hd._keys: assert id(hd[key]) == id(hdc[key])
def test_subset(): hd = HopscotchDict() keys = hd.keys() items = hd.items() hd["a"] = 1 hd["b"] = 2 hd["c"] = 3 s1 = ["a", "b"] s2 = ["a", "b", "c"] s3 = ["a", "b", "c", "d"] s4 = [1, 2] s5 = [1, 2, 3] s6 = [1, 2, 3, 4] assert not keys <= set(s1) assert not keys.issubset(s1) assert keys <= set(s2) assert keys.issubset(s2) assert keys <= set(s3) assert keys.issubset(s3) assert not items <= set(list(zip(s1, s4))) assert not items.issubset(list(zip(s1, s4))) assert items <= set(list(zip(s2, s5))) assert items.issubset(list(zip(s2, s5))) assert items <= set(list(zip(s3, s6))) assert items.issubset(list(zip(s3, s6)))
def test_setdefault(existing_key): hd = HopscotchDict() val = None if existing_key: hd["test_setdefault"] = val = 1337 else: val = 1017 assert hd.setdefault("test_setdefault", 1017) == val
def test_get(valid_key): hd = HopscotchDict() val = None if valid_key: hd["test_get"] = val = 1337 else: val = 1017 assert hd.get("test_get", 1017) == val
def test_items(gen_dict): hd = HopscotchDict(gen_dict) items = hd.items() start_len = len(hd) assert len(items) == start_len assert all(hd[k] == v for (k, v) in items) hd["test_items"] = True assert len(items) == start_len + 1 assert ("test_items", True) in items
def test_values(gen_dict): hd = HopscotchDict(gen_dict) vals = hd.values() start_len = len(hd) assert len(vals) == start_len assert all(v in hd._values for v in vals) hd["test_values"] = "test_values" assert len(vals) == start_len + 1 assert "test_values" in vals
def test_keys(gen_dict): hd = HopscotchDict(gen_dict) keys = hd.keys() start_len = len(hd) assert len(keys) == start_len assert all(k in hd for k in keys) hd["test_keys"] = True assert len(keys) == start_len + 1 assert "test_keys" in keys
def test_reversed(gen_dict): hd = HopscotchDict(gen_dict) keys = hd.keys() if not isinstance(keys, list): keys = list(keys) rev_keys = list(reversed(hd)) assert len(keys) == len(rev_keys) for i in range(len(keys)): assert keys[i] == rev_keys[len(keys) - i - 1]
def test_reversed(gen_dict): hd = HopscotchDict(gen_dict) keys = hd.keys() vals = hd.values() items = hd.items() assert list(reversed(list(reversed(keys)))) == hd._keys assert list(reversed(list(reversed(vals)))) == hd._values for (i, (k, v)) in enumerate(reversed(list(reversed(items)))): assert k == hd._keys[i] assert v == hd._values[i] hd["test_keys"] = True
def test_setitem_happy_path(gen_dict): hd = HopscotchDict() for (k, v) in gen_dict.items(): hd[k] = v assert len(hd) == len(gen_dict) for key in gen_dict: assert hd[key] == gen_dict[key] expected_lookup_idx = abs(hash(key)) % hd._size _, neighbors = hd._get_lookup_index_info(expected_lookup_idx) lookup_idx, _ = hd._lookup(key) assert lookup_idx in neighbors
def test_pop(scenario): hd = HopscotchDict() val = None if scenario == "valid_key": hd["test_pop"] = val = 1337 else: val = 0 if scenario != "invalid_key": assert hd.pop("test_pop", 0) == val else: with pytest.raises(KeyError): hd.pop("test_pop")
def test_make_lookup_table(tbl_size): if tbl_size < 0: with pytest.raises(ValueError): HopscotchDict._make_lookup_table(tbl_size) else: tbl, fmt = HopscotchDict._make_lookup_table(tbl_size) if tbl_size.bit_length() < 8: assert fmt == "b B" elif tbl_size.bit_length() < 16: assert fmt == ">h H" elif tbl_size.bit_length() < 32: assert fmt == ">i I" else: assert fmt == ">l L"
def test_lookup(key): hd = HopscotchDict() hd[7] = "test_lookup_7" hd[15] = "test_lookup_15" assert hd._lookup(7) == (7, 0) assert hd._lookup(15) == (0, 1) del hd[7] del hd[15] lookup_idx = abs(hash(key)) % hd._size hd[key] = True assert hd._lookup(key)[0] == lookup_idx
def test_eq_and_neq(scenario): hd = HopscotchDict() dc = {} for i in range(5): hd[f"test_eq_and_neq_{i}"] = i dc[f"test_eq_and_neq_{i}"] = i if scenario == "bad_len" or scenario == "bad_keys": dc.pop("test_eq_and_neq_4") if scenario == "bad_keys": dc["test_eq_and_neq_5"] = 4 if scenario == "bad_vals": dc["test_eq_and_neq_0"] = False if scenario == "bad_type": assert hd != dc.items() elif scenario != "eq": assert hd != dc else: assert hd == dc
def _make_level_tables(levels: int) -> List[HopscotchDict]: """ Creates the dicts used when searching for a value in the trie :param levels: The number of levels in the trie :return: Search structures for each level of the trie """ return [HopscotchDict() for _ in range(levels)]
def test_iter_and_len(gen_dict): hd = HopscotchDict(gen_dict) count = 0 for key in hd: count += 1 assert count == len(hd) == len(gen_dict)
def clear(self) -> None: """ Remove all values from the trie and return it to its starting state """ self._count = 0 self._max: Optional[int] = None self._min: Optional[int] = None self._partitions = XFastTrie(self._maxlen) self._subtrees = HopscotchDict()
def test_getitem(valid_key): hd = HopscotchDict() if valid_key: hd["test_getitem"] = True assert hd["test_getitem"] else: with pytest.raises(KeyError): assert hd["test_getitem"]
def test_get_displaced_neighbors(with_collisions): hd = HopscotchDict() if with_collisions: hd[1] = "test_get_displaced_neighbors_1" hd[9] = "test_get_displaced_neighbors_9" hd[3] = "test_get_displaced_neighbors_3" hd[17] = "test_get_displaced_neighbors_17" hd[6] = "test_get_displaced_neighbors_6" hd[14] = "test_get_displaced_neighbors_14" assert hd._size == 8 lookup_offset = calcsize(hd._pack_fmt) * 1 _, nbhd = unpack_from(hd._pack_fmt, hd._lookup_table, lookup_offset) assert hd._get_displaced_neighbors(1, nbhd, hd._nbhd_size, hd._size) == [ 1, 2, 4, ] lookup_offset = calcsize(hd._pack_fmt) * 3 _, nbhd = unpack_from(hd._pack_fmt, hd._lookup_table, lookup_offset) assert hd._get_displaced_neighbors(3, nbhd, hd._nbhd_size, hd._size) == [3] lookup_offset = calcsize(hd._pack_fmt) * 6 _, nbhd = unpack_from(hd._pack_fmt, hd._lookup_table, lookup_offset) assert hd._get_displaced_neighbors(6, nbhd, hd._nbhd_size, hd._size) == [6, 7] else: with pytest.raises(ValueError): hd._get_displaced_neighbors(-1, 0, hd._nbhd_size, hd._size) with pytest.raises(ValueError): hd._get_displaced_neighbors(10, 0, hd._nbhd_size, hd._size) for i in range(6): hd[i] = f"test_get_displaced_neighbors_{i}" for i in range(6): lookup_offset = calcsize(hd._pack_fmt) * i _, nbhd = unpack_from(hd._pack_fmt, hd._lookup_table, lookup_offset) assert hd._get_displaced_neighbors(i, nbhd, hd._nbhd_size, hd._size) == [i]
def test_clear(): hd = HopscotchDict() for i in range(256): hd[f"test_clear_{i}"] = i hd.clear() assert hd._count == 0 assert hd._size == 8 assert hd._nbhd_size == 8 assert len(hd._keys) == 0 assert len(hd._values) == 0 for lookup_idx in range(hd._size): data_idx, neighbors = hd._get_lookup_index_info(lookup_idx) assert data_idx == hd.FREE_ENTRY assert len(neighbors) == 0
def test_union(): hd = HopscotchDict() keys = hd.keys() items = hd.items() hd["a"] = 1 hd["b"] = 2 hd["c"] = 3 s1 = ["a", "b"] s2 = ["a", "b", "c", "d"] s3 = [1, 2] s4 = [1, 2, 3, 4] assert keys | set(s1) == keys assert keys.union(s2) == set(s2) assert items | zip(s1, s3) == items assert items.union(zip(s2, s4)) == set(list(zip(s2, s4)))
class HopscotchStateMachine(RuleBasedStateMachine): def __init__(self): super().__init__() self.d = HopscotchDict() def teardown(self): keys = list(self.d.keys()) for key in keys: del self.d[key] @invariant() def valid_neighborhoods(self): for lookup_idx in range(self.d._size): _, neighbors = self.d._get_lookup_index_info(lookup_idx) for neighbor in neighbors: data_idx = self.d._get_lookup_index_info(neighbor)[0] assert abs(hash(self.d._keys[data_idx])) % self.d._size == lookup_idx @invariant() def no_missing_data(self): assert len(self.d._keys) == len(self.d._values) @invariant() def bounded_density(self): if self.d._count > 0: assert self.d._count / self.d._size <= self.d.MAX_DENSITY @rule(k=dict_keys, v=dict_values) def add_entry(self, k, v): self.d[k] = v @rule(k=dict_keys) def remove_entry(self, k): if k not in self.d._keys: with pytest.raises(KeyError): del self.d[k] else: del self.d[k]
def test_lookup_fails(scenario): hd = HopscotchDict() if scenario == "missing": assert hd._lookup("test_lookup") == (None, None) elif scenario == "free": hd[4] = "test_lookup" hd._set_lookup_index_info(4, data=hd.FREE_ENTRY) with pytest.raises(RuntimeError): hd._lookup(4)
def test_get_lookup_index_info(gen_dict, lookup_idx): hd = HopscotchDict(gen_dict) if lookup_idx < 0 or lookup_idx >= hd._size: with pytest.raises(ValueError): hd._get_lookup_index_info(lookup_idx) else: data_idx, neighbors = hd._get_lookup_index_info(lookup_idx) for idx in neighbors: data_idx, _ = hd._get_lookup_index_info(idx) assert abs(hash(hd._keys[data_idx])) % hd._size == lookup_idx
def test_popitem(gen_dict): hd = HopscotchDict() if not gen_dict: with pytest.raises(KeyError): hd.popitem() else: hd.update(gen_dict) key = hd._keys[-1] val = hd._values[-1] cur_len = len(hd) assert (key, val) == hd.popitem() assert len(hd) == cur_len - 1 assert key not in hd
def test_get_open_neighbor(gen_dict, lookup_idx): hd = HopscotchDict(gen_dict) if lookup_idx < 0 or lookup_idx >= hd._size: with pytest.raises(ValueError): hd._get_open_neighbor(lookup_idx) else: open_idx = hd._get_open_neighbor(lookup_idx) for idx in range( lookup_idx, open_idx if open_idx is not None else lookup_idx + hd._nbhd_size ): data_idx, _ = hd._get_lookup_index_info(idx) assert data_idx != hd.FREE_ENTRY