137 lines
6.0 KiB
Python
137 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Q121 cells for q60_enforce.py's fixture-absence leg (vol, 2026-10-29).
|
|
|
|
The defect Q121 filed: selftest cell 8 tested ``"".join(KNOWN_FIXTURE.values())``,
|
|
so with two table entries it tested a string that occurs nowhere, passed
|
|
vacuously, and checked neither value. These cells are black box -- the module is
|
|
run as a subprocess and nothing is imported from it -- and they are the cells
|
|
the module's own selftest cannot write: a module cannot put a second entry in
|
|
its own table at run time without shipping one.
|
|
|
|
Cells:
|
|
1. control the shipped module's selftest is green and reports 11 cells.
|
|
(10 at Q121 landing; 11 after Q117 added the probe cell.
|
|
The count pin moves with the cell count by design.)
|
|
2. two-entry the queue's mutant (second KNOWN_FIXTURE entry plus that
|
|
entry's full literal in the source) reds NAMING the stem.
|
|
3. join-legacy the pre-repair algorithm restored inside the cell reds --
|
|
the case exists, so the cell is not vacuous.
|
|
4. first-only a scan that reports only the first fault reds on the
|
|
both-inserted case.
|
|
5. pre-repair the code at the rev the item was filed against stays GREEN
|
|
over cell 2's mutant; skipped when that rev is not present.
|
|
|
|
Run: python3 wizard-work/zeph/test_q60_absence.py (from the repo root or anywhere)
|
|
"""
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
MODULE = os.path.join(HERE, "q60_enforce.py")
|
|
# The rev WORK-QUEUE.md's Q121 record measures at.
|
|
FILED_REV = "f17c936"
|
|
SECOND_STEM = "5e0c7a91"
|
|
SECOND_VALUE = "5e0c7a91-" "abcd-1234-5678-90abcdef1234"
|
|
TABLE_SRC = ('KNOWN_FIXTURE = {\n'
|
|
' "3d79ba82": "3d79ba82-" "1111-2222-3333-444444444444",\n'
|
|
'}')
|
|
TABLE_MUT = ('KNOWN_FIXTURE = {\n'
|
|
' "3d79ba82": "3d79ba82-" "1111-2222-3333-444444444444",\n'
|
|
' "5e0c7a91": "5e0c7a91-" "abcd-1234-5678-90abcdef1234",\n'
|
|
'}')
|
|
PER_ENTRY_BODY = (
|
|
' table = KNOWN_FIXTURE if table is None else table\n'
|
|
' faults = []\n'
|
|
' for stem in sorted(table):\n'
|
|
' if table[stem].encode("utf-8") in blob:\n'
|
|
' faults.append("%s (len %d)" % (stem, len(table[stem])))\n'
|
|
' return faults\n'
|
|
)
|
|
JOIN_BODY = (
|
|
' table = KNOWN_FIXTURE if table is None else table\n'
|
|
' joined = "".join(table[stem] for stem in sorted(table))\n'
|
|
' return ["joined (len %d)" % len(joined)] if joined.encode("utf-8") in blob else []\n'
|
|
)
|
|
FIRST_ONLY_BODY = (
|
|
' table = KNOWN_FIXTURE if table is None else table\n'
|
|
' for stem in sorted(table):\n'
|
|
' if table[stem].encode("utf-8") in blob:\n'
|
|
' return ["%s (len %d)" % (stem, len(table[stem]))]\n'
|
|
' return []\n'
|
|
)
|
|
|
|
|
|
def read(path):
|
|
with open(path, "r", encoding="utf-8") as fh:
|
|
return fh.read()
|
|
|
|
|
|
def run_selftest(path):
|
|
p = subprocess.run([sys.executable, path, "--selftest"], capture_output=True, text=True)
|
|
return p.returncode, p.stdout, p.stderr
|
|
|
|
|
|
def write_mutant(text):
|
|
d = tempfile.mkdtemp(prefix="q121-cell-")
|
|
path = os.path.join(d, "q60_enforce.py")
|
|
with open(path, "w", encoding="utf-8") as fh:
|
|
fh.write(text)
|
|
return path
|
|
|
|
|
|
def two_entry_mutant(base):
|
|
"""The queue's construction: second table entry + that literal in source."""
|
|
out = base.replace(TABLE_SRC, TABLE_MUT, 1)
|
|
assert out != base, "table pattern moved; cell cannot build its mutant"
|
|
return out.rstrip("\n") + "\n\n# stray re-commit of fixture: " + SECOND_VALUE + "\n"
|
|
|
|
|
|
class Q121Cells(unittest.TestCase):
|
|
def setUp(self):
|
|
self.base = read(MODULE)
|
|
|
|
def test_1_control_is_green(self):
|
|
rc, out, err = run_selftest(MODULE)
|
|
self.assertEqual(rc, 0, "shipped selftest not green: %s" % err[-300:])
|
|
self.assertIn("SELFTEST: 11 cells passed, 0 failed", out)
|
|
self.assertIn("SELFTEST PASS absence-scan-per-entry", out)
|
|
|
|
def test_2_two_entry_mutant_reds_naming_the_stem(self):
|
|
rc, out, err = run_selftest(write_mutant(two_entry_mutant(self.base)))
|
|
self.assertNotEqual(rc, 0, "two-entry mutant stayed green -- vacuous cell is back")
|
|
self.assertIn(SECOND_STEM, err, "red, but did not name the inserted stem")
|
|
|
|
def test_3_join_semantics_is_killed(self):
|
|
self.assertIn(PER_ENTRY_BODY, self.base, "per-entry body not found; test is stale")
|
|
rc, _out, err = run_selftest(write_mutant(self.base.replace(PER_ENTRY_BODY, JOIN_BODY, 1)))
|
|
self.assertNotEqual(rc, 0, "join-semantics scan survived -- cell 10 does not see it")
|
|
self.assertIn("want", err)
|
|
|
|
def test_4_first_fault_only_is_killed(self):
|
|
self.assertIn(PER_ENTRY_BODY, self.base, "per-entry body not found; test is stale")
|
|
rc, _out, err = run_selftest(write_mutant(self.base.replace(PER_ENTRY_BODY, FIRST_ONLY_BODY, 1)))
|
|
self.assertNotEqual(rc, 0, "first-fault-only scan survived the both-inserted case")
|
|
self.assertIn("both stems", err)
|
|
|
|
def test_5_pre_repair_code_was_green_on_the_mutant(self):
|
|
try:
|
|
old = subprocess.run(
|
|
["git", "-C", HERE, "show", "%s:wizard-work/zeph/q60_enforce.py" % FILED_REV],
|
|
capture_output=True, text=True, check=True).stdout
|
|
except (subprocess.CalledProcessError, OSError):
|
|
self.skipTest("rev %s not present in this checkout" % FILED_REV)
|
|
self.assertNotIn("fixture_literals_in_source", old, "rev %s already carries the repair" % FILED_REV)
|
|
mutant = old.replace(TABLE_SRC, TABLE_MUT, 1)
|
|
self.assertNotEqual(mutant, old, "table pattern moved in the filed rev")
|
|
mutant = mutant.rstrip("\n") + "\n\n# stray re-commit of fixture: " + SECOND_VALUE + "\n"
|
|
rc, out, _err = run_selftest(write_mutant(mutant))
|
|
self.assertEqual(rc, 0, "the filed rev already reds over the mutant -- defect not reproduced")
|
|
self.assertIn("9 cells passed, 0 failed", out)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|