108 lines
3.6 KiB
Python
108 lines
3.6 KiB
Python
"""Unit tests for the cost model — trivial-glue tier and long-pole detection."""
|
||
|
||
from nix_estimator import costmodel, schedule
|
||
from nix_estimator.estimate import _recommend
|
||
|
||
|
||
def test_glue_derivations_are_trivial():
|
||
# NixOS assembly glue must not be costed like a real compile.
|
||
for name in [
|
||
"unit-dbus-broker.service",
|
||
"X-Restart-Triggers-dbus-broker",
|
||
"user-units",
|
||
"etc",
|
||
"activate",
|
||
"system-path",
|
||
"nixos-system-emmett-26.11",
|
||
"dbus-1",
|
||
"sshd.service",
|
||
]:
|
||
assert costmodel.is_trivial(name), name
|
||
mins, _ = costmodel.cost(f"/nix/store/hash-{name}.drv")
|
||
assert mins <= 0.1, (name, mins)
|
||
|
||
|
||
def test_real_packages_are_not_trivial():
|
||
for name in [
|
||
"onnxruntime-1.17.0",
|
||
"hello-2.12",
|
||
"openssl-3.0.0",
|
||
"python3.13-numpy-2.0",
|
||
]:
|
||
assert not costmodel.is_trivial(name), name
|
||
|
||
|
||
def test_glue_chain_does_not_dominate_span():
|
||
# A 100-deep glue chain used to fabricate a ~100-min span at DEFAULT=1min.
|
||
names = [f"unit-svc{i}.service" for i in range(100)]
|
||
dur = {n: costmodel.cost(f"/nix/store/h-{n}.drv")[0] for n in names}
|
||
preds = {n: ([names[i - 1]] if i else []) for i, n in enumerate(names)}
|
||
span, _ = schedule.critical_path(dur, preds)
|
||
assert span < 10.0 # 100 × 0.05 == 5 min, not 100
|
||
|
||
|
||
def _cost(name):
|
||
return costmodel.cost(f"/nix/store/hash-{name}.drv")
|
||
|
||
|
||
def test_heavy_substring_does_not_over_fire():
|
||
# Substring keys used to inflate ordinary libs/wrappers to HEAVY (issue #6).
|
||
for name in [
|
||
"python3.12-requests-2.31.0", # "python3" is a substring, not the pname
|
||
"util-linux-2.39", # "linux-" is a substring, not the pname
|
||
"gcc-wrapper-13", # toolchain shim
|
||
"rustc-wrapper", # toolchain shim
|
||
"clang-wrapper-16",
|
||
"grpcio-1.62", # not grpc
|
||
]:
|
||
mins, _ = _cost(name)
|
||
assert mins <= costmodel.DEFAULT[0], (name, mins)
|
||
|
||
|
||
def test_heavy_real_pnames_still_fire():
|
||
# Genuine heavy derivations must still hit the HEAVY table.
|
||
expected = {
|
||
"llvm-15.0.7": costmodel.HEAVY["llvm"],
|
||
"onnxruntime-1.16.0": costmodel.HEAVY["onnxruntime"],
|
||
"ghc-9.4.8": costmodel.HEAVY["ghc"],
|
||
"rustc-1.75.0": costmodel.HEAVY["rustc"], # the real compiler
|
||
"python3-3.12.7": costmodel.HEAVY["python3"], # the interpreter
|
||
"gcc-13.2.0": costmodel.HEAVY["gcc"],
|
||
"linux-6.6.30": costmodel.HEAVY["linux-"], # the kernel
|
||
}
|
||
for name, val in expected.items():
|
||
assert _cost(name) == val, name
|
||
|
||
|
||
def test_history_longest_prefix_wins_regardless_of_order():
|
||
# Overlapping prefixes must resolve deterministically to the longest match.
|
||
drv = "/nix/store/hash-gcc-13.2.0.drv"
|
||
for hist in (
|
||
{"gcc": 40.0, "gcc-13": 5.0},
|
||
{"gcc-13": 5.0, "gcc": 40.0},
|
||
):
|
||
mins, _ = costmodel.cost(drv, history=hist)
|
||
assert mins == 5.0, hist
|
||
|
||
|
||
def test_history_exact_name_wins_first():
|
||
drv = "/nix/store/hash-gcc-13.2.0.drv"
|
||
hist = {"gcc-13.2.0": 7.0, "gcc-13": 5.0, "gcc": 40.0}
|
||
mins, _ = costmodel.cost(drv, history=hist)
|
||
assert mins == 7.0
|
||
|
||
|
||
def test_recommendation_note_branches_on_dominator():
|
||
grid = {
|
||
(1, 8): 40.0,
|
||
(1, 16): 30.0,
|
||
(1, 32): 25.0,
|
||
(2, 8): 40.0,
|
||
(2, 16): 30.0,
|
||
(2, 32): 25.0,
|
||
}
|
||
heavy = _recommend(grid, 4, (8, 16, 32), (1, 2), 0.12, span_dominator_frac=0.8)
|
||
assert "one heavy derivation" in heavy["note"]
|
||
chain = _recommend(grid, 4, (8, 16, 32), (1, 2), 0.12, span_dominator_frac=0.05)
|
||
assert "long chain of small derivations" in chain["note"]
|