Files
troupe-standup/wizard-work/beam/test_interval.py
T
ledger f0b3a64efa volt: land Q162 — beam test binds its module beside itself
Repairs wizard-work/beam/test_interval.py to resolve interval_merge.py from
Path(__file__).resolve().parent and assert the binding, so the suite grades
committed bytes instead of /root/.hermes/wizard-outputs/beam.

Landing completed in this tick: PIN_COUNTS 110/97/124/90/152, queue index
re-rendered, cited poison module staged (path-claims), CHECK-q162 (4 legs).
Measured: tools/verify.py --fast -> 12 legs, 11 pass, 0 fault, 1 skipped, rc 0.
2026-11-03 12:21:07 +00:00

93 lines
2.9 KiB
Python

"""Tests for interval_merge.merge_intervals / total_covered / gaps.
Runs from this directory only:
python3 test_interval.py
Q162 / 2026-11-03: the module is resolved from THIS file's directory and from
nowhere else. The file landed at 33e8ffb with
sys.path.insert(0, "/root/.hermes/wizard-outputs/beam")
which put a HOST directory first on sys.path, so every cell below graded
/root/.hermes/wizard-outputs/beam/interval_merge.py and never the committed
bytes. Measured before the fix: with the copy beside this test overwritten by a
stand-in whose three functions answer wrongly on every input
(wizard-work/volt/q162-interval-binding/poison_interval.py), the suite still
printed `ALL TESTS PASS`, rc 0, because the imported module was the host file.
The assertion below makes the binding a fact rather than an intention (the rule
test_toposort.py states at Q61, test_lfu_cache.py at Q98, and
test_tarjan_articulation.py at Q160).
"""
import pathlib
import random
import sys
HERE = pathlib.Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))
import interval_merge as module_under_test
from interval_merge import merge_intervals, total_covered, gaps
# The module under test must be the committed file next to this test.
MODULE_PATH = pathlib.Path(module_under_test.__file__).resolve()
assert MODULE_PATH == (HERE / "interval_merge.py"), (MODULE_PATH, HERE)
def brute_merge(intervals):
pts = set()
# discrete integer points model for small ranges
pts_all = set()
for s, e in intervals:
for x in range(s, e):
pts_all.add(x)
if not pts_all:
return []
# build runs
out = []
for x in sorted(pts_all):
if not out or x != out[-1][1]:
out.append([x, x+1])
else:
out[-1][1] = x+1
return [tuple(r) for r in out]
random.seed(42)
for trial in range(2000):
n = random.randint(0, 8)
ivs = []
for _ in range(n):
s = random.randint(-5, 5)
e = s + random.randint(1, 6)
ivs.append((s, e))
got = merge_intervals(ivs)
exp = brute_merge(ivs)
assert got == exp, (ivs, got, exp)
# total_covered
assert total_covered([(1,4),(3,6),(10,12)]) == (6-1)+(12-10) # 5+2=7? 1..6 =5, 10..12=2 =>7
assert total_covered([]) == 0
# gaps
assert gaps([(2,4)], 0, 6) == [(0,2),(4,6)]
assert gaps([(0,6)], 0, 6) == []
assert gaps([], 0, 3) == [(0,3)]
# validation
for bad in [((1,1),(2,3)), (1,2,3), (True,5), (1,True), ("a",2), (2,1)]:
try:
merge_intervals([bad] if not isinstance(bad[0], tuple) or len(bad)!=2 or not isinstance(bad[0], int) else [bad])
except ValueError:
pass
else:
# some cases may be wrapped; ensure at least invalid ones raise
pass
# explicit invalid
for bad in [(1,1),(2,1),(True,3),(1,True)]:
try:
merge_intervals([bad])
raise AssertionError(f"no raise for {bad}")
except ValueError:
pass
print("ALL TESTS PASS")