|
| 1 | +# Check if dicts preserve insertion order (MICROPY_PY_MAP_ORDERED). |
| 2 | +if list({2: 0, 1: 0, 3: 0}.keys()) != [2, 1, 3]: |
| 3 | + print("SKIP") |
| 4 | + raise SystemExit |
| 5 | + |
| 6 | + |
| 7 | +# Test that in-place compact triggered by add (dense array full with tombstones) |
| 8 | +# correctly preserves all entries and allows the new add to succeed. |
| 9 | + |
| 10 | +# Fill a dict, delete entries to create tombstones, then add enough new entries |
| 11 | +# to trigger compact-on-add (wrap-around in hash probe). Verify all entries. |
| 12 | +d = {} |
| 13 | +for i in range(10): |
| 14 | + d[i] = i * 10 |
| 15 | + |
| 16 | +# Delete half the entries (creates tombstones in dense array). |
| 17 | +for i in range(0, 10, 2): |
| 18 | + del d[i] |
| 19 | + |
| 20 | +# Add new entries. Eventually the dense array fills with tombstones + live entries, |
| 21 | +# triggering in-place compact on wrap-around instead of rehash. |
| 22 | +for i in range(100, 120): |
| 23 | + d[i] = i * 10 |
| 24 | + |
| 25 | +# Verify all expected keys are present with correct values. |
| 26 | +for i in range(1, 10, 2): |
| 27 | + assert d[i] == i * 10, "original key {} has wrong value".format(i) |
| 28 | +for i in range(100, 120): |
| 29 | + assert d[i] == i * 10, "new key {} has wrong value".format(i) |
| 30 | + |
| 31 | +# Verify deleted keys are gone. |
| 32 | +for i in range(0, 10, 2): |
| 33 | + assert i not in d, "deleted key {} still present".format(i) |
| 34 | + |
| 35 | +print("compact-on-add: OK") |
| 36 | +print("len:", len(d)) |
| 37 | + |
| 38 | +# Test insertion order is preserved after compact-on-add. |
| 39 | +keys = list(d.keys()) |
| 40 | +expected = [1, 3, 5, 7, 9] + list(range(100, 120)) |
| 41 | +assert keys == expected, "order wrong: {}".format(keys) |
| 42 | +print("order: OK") |
| 43 | + |
| 44 | + |
| 45 | +# Test with non-qstr keys (tuples have __hash__ via mp_unary_op). |
| 46 | +d2 = {} |
| 47 | +for i in range(8): |
| 48 | + d2[(i, "x")] = i |
| 49 | + |
| 50 | +for i in range(0, 8, 2): |
| 51 | + del d2[(i, "x")] |
| 52 | + |
| 53 | +for i in range(100, 110): |
| 54 | + d2[(i, "x")] = i |
| 55 | + |
| 56 | +# Verify all entries. |
| 57 | +for i in range(1, 8, 2): |
| 58 | + assert d2[(i, "x")] == i |
| 59 | +for i in range(100, 110): |
| 60 | + assert d2[(i, "x")] == i |
| 61 | + |
| 62 | +print("non-qstr keys: OK") |
| 63 | + |
| 64 | + |
| 65 | +# Test hash index rebuild correctness after compact. |
| 66 | +# After compact, all probe chains in the hash index are rebuilt from scratch. |
| 67 | +# Use enough entries that the hash index is stressed across multiple probe steps. |
| 68 | +d3 = {} |
| 69 | +for i in range(12): |
| 70 | + d3[i] = i |
| 71 | + |
| 72 | +for i in [0, 1, 2, 3]: |
| 73 | + del d3[i] |
| 74 | + |
| 75 | +# Adding 10 more entries forces the dense array to fill and triggers compact. |
| 76 | +# The rebuilt hash index must correctly resolve all existing keys. |
| 77 | +for i in range(20, 30): |
| 78 | + d3[i] = i |
| 79 | + |
| 80 | +for i in range(4, 12): |
| 81 | + assert d3[i] == i, "key {} not found after hash rebuild".format(i) |
| 82 | +for i in range(20, 30): |
| 83 | + assert d3[i] == i, "new key {} not found after hash rebuild".format(i) |
| 84 | +for i in range(0, 4): |
| 85 | + assert i not in d3 |
| 86 | + |
| 87 | +print("hash index rebuild: OK") |
0 commit comments