Files
troupe-standup/wizard-work/null/minisql/test_diff_sqlite_guards.py

154 lines
5.8 KiB
Python

#!/usr/bin/env python3
"""
test_diff_sqlite_guards.py -- regression guards for the two blind spots this lane closed.
Wizard: null (lane: Codeforge -- hard coding and agentic reasoning).
Both bugs were silent for the same reason: the mutation control only asks "did the mutant
change any output", and a mutant that is never exercised changes nothing. A surviving
mutant was reported, but nothing forced the *generator* to reach the semantics the mutant
patches. These guards make that false.
G1 the generator itself must emit the NULL-tri-value operand families
(`FALSE AND NULL`, `TRUE AND NULL`, a NULL-producing right operand of AND, and a
LEFT JOIN `ON ... AND FALSE`);
G2 the shape schedule must give every shape a fixed, wide slice -- not `i % 7`, which
leaves a shape at 8 queries in a 28-query run;
G3 `check_report()` must refuse to pass on a thin slice, must enforce the mutant-queries
floor on a report that declares it runs mutants, and must tolerate a report built by
hand that declares neither (the KeyError that made the first run of this check exit 1).
Run: python3 test_diff_sqlite_guards.py (exit 0 = guards hold)
"""
from __future__ import annotations
import os
import random
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import diff_sqlite as D # noqa: E402
def g1_generator_emits_the_families():
"""Draw many predicates and assert each family appears, not merely that some did."""
rng = random.Random(7)
spec, rows = D.make_schema(rng)
gen = D.Gen(rng, spec, rows)
gen.set_scope(list(spec))
and_false = and_null_right = or_false = on_false = 0
n = 4000
for _ in range(n):
p = gen.predicate(False)
if "1 = 0" in p:
and_false += 1
if " IS NULL" in p or " IN (" in p or " BETWEEN " in p or " LIKE " in p:
and_null_right += 1
if " OR " in p:
or_false += 1
# the ON shape is drawn inside query(), so count it over whole queries
gen2 = D.Gen(rng, spec, rows)
for _ in range(n):
q = gen2.query("join")
if "ON" in q and "1 = 0" in q.split("ON", 1)[1][:60]:
on_false += 1
checks = {
"AND with a literal FALSE operand": and_false,
"AND with a NULL-producing right operand": and_null_right,
"OR present": or_false,
"LEFT JOIN ON with AND FALSE": on_false,
}
return checks, n
def g2_shape_schedule_is_even():
"""Every shape must get an identical slice for any query count that is a multiple of 7."""
rng = random.Random(11)
counts = dict.fromkeys(D.SHAPES, 0)
total = D._QUERIES_PER_SHAPE * len(D.SHAPES) * 5
for i in range(total):
shape = D.SHAPES[(i // D._QUERIES_PER_SHAPE) % len(D.SHAPES)]
counts[shape] += 1
rng.random()
return counts, total
def g3_check_report_refuses_thin_slice():
"""A report whose shape counts are thin must produce a failure string.
Also covers the mutant-queries floor: `check_mutant_queries()` was reachable only
through main(), so a thin mutant slice read as PASS. Both directions are asserted --
below the floor is refused, at or above the floor it is not.
"""
report = {
"queries": 28, "seed": 0, "shape_counts": dict.fromkeys(D.SHAPES, 4),
"rows_compared": 30, "nonempty": 20, "ordered": 20, "mismatches": [], "errors": [],
}
bad = D.check_report(report)
thin = [b for b in bad if "thin slice" in b]
floor = D.mutant_floor()
below = D.check_mutant_queries(floor - 1)
at_floor = D.check_mutant_queries(floor)
if not any("below the measured floor" in b for b in below):
bad.append("check_mutant_queries(%d) accepted a slice below the floor %d"
% (floor - 1, floor))
if at_floor:
bad.append("check_mutant_queries(%d) refused the floor width itself: %r"
% (floor, at_floor))
# and a full report that declares it runs mutants must be enforced, not ignored
carried = dict(report)
carried["shape_counts"] = dict.fromkeys(D.SHAPES, D.MIN_PER_SHAPE)
carried["mutant_queries"] = floor - 1
carried["skip_mutant_floor"] = False
if not any("below the measured floor" in b for b in D.check_report(carried)):
bad.append("check_report ignored the mutant_queries width it was handed")
# ...and a report that declares it runs mutants but names no width is refused, not waved through
nowidth = dict(carried)
del nowidth["mutant_queries"]
if not any("carries no mutant-queries width" in b for b in D.check_report(nowidth)):
bad.append("check_report waved through a mutant run that names no slice width")
return bad, thin
def main():
failures = []
checks, n = g1_generator_emits_the_families()
print("G1 generator emits the NULL-tri-value families (%d draws)" % n)
for name, count in checks.items():
state = "ok" if count else "MISSING"
print(" %-40s %6d %s" % (name, count, state))
if not count:
failures.append("G1: %s never generated" % name)
counts, total = g2_shape_schedule_is_even()
print("\nG2 shape schedule over %d queries: %s" % (total, counts))
if len(set(counts.values())) != 1:
failures.append("G2: shape slices are not even: %r" % counts)
bad, thin = g3_check_report_refuses_thin_slice()
print("\nG3 check_report() on a thin slice: %d failure(s), %d mention a thin slice"
% (len(bad), len(thin)))
for b in bad:
print(" " + b)
if not thin:
failures.append("G3: check_report accepted a thin slice")
print()
if failures:
for f in failures:
print("FAIL: " + f)
print("RESULT: FAIL")
return 1
print("RESULT: PASS")
return 0
if __name__ == "__main__":
sys.exit(main())