forge: checkin_check census 7 scans -> 1 Counter pass, byte-identical
This commit is contained in:
1 parent
1e3a3ea04b
commit
4c89084033
3 files changed
+425
-4
No files matched your search
+19
-4
@@ -108,6 +108,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
from datetime import date, datetime, timedelta, timezone
|
||||
|
||||
NAME_RE = re.compile(r"^(\d{4})-(\d{2})-(\d{2})-([a-z][a-z0-9]*)\.md$")
|
||||
@@ -494,18 +495,32 @@ def run(path: str, roster, today: date, show_gaps: bool,
|
||||
r["duplicate"] = None
|
||||
|
||||
fault_rows = [r for r in rows if r["class"] or r["duplicate"]]
|
||||
# One pass over `rows` replaces seven `sum(1 for r in rows ...)` scans --
|
||||
# five verdict passes (`faults`, one per FAULT class) plus two advisory
|
||||
# passes (`missing_headings`, `headings_ok`). Counter buckets verdicts by
|
||||
# presence: `set(r["class"])` folds any intra-row duplicate so a row counts
|
||||
# once per class, matching `c in r["class"]`; the two advisory counters are
|
||||
# incremented alongside, so each row is touched once, not seven times.
|
||||
fault_counts = Counter()
|
||||
missing_headings = 0
|
||||
headings_ok = 0
|
||||
for r in rows:
|
||||
fault_counts.update(set(r["class"]))
|
||||
if r["headings_missing"]:
|
||||
missing_headings += 1
|
||||
elif "EMPTY" not in r["class"]:
|
||||
headings_ok += 1
|
||||
rep = {
|
||||
"dir": path, "checked": len(files), "today": today.isoformat(),
|
||||
"roster_size": len(roster), "skipped": skipped,
|
||||
"since": since, "excluded_since": excluded_since,
|
||||
"faults": {c: sum(1 for r in rows if c in r["class"]) for c in FAULTS},
|
||||
"faults": {c: fault_counts.get(c, 0) for c in FAULTS},
|
||||
"fault_rows": len(fault_rows),
|
||||
"duplicate_groups": [{"date": d, "handle": h, "files": fl}
|
||||
for (d, h), fl in sorted(dup.items())],
|
||||
"advisory": {
|
||||
"missing_headings": sum(1 for r in rows if r["headings_missing"]),
|
||||
"headings_ok": sum(1 for r in rows
|
||||
if not r["headings_missing"] and "EMPTY" not in r["class"]),
|
||||
"missing_headings": missing_headings,
|
||||
"headings_ok": headings_ok,
|
||||
"note": "A1/A2 are advisory: the four-heading rule postdates the archive",
|
||||
},
|
||||
"rows": rows,
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# CHECKIN_CHECK_DRY_REFACTOR_VERIFICATION
|
||||
|
||||
Tool: `tools/checkin_check.py` (forge lane: DRY, behavior-preserving)
|
||||
Date: 2026-10-20
|
||||
Original: `git show HEAD~1:tools/checkin_check.py` (volt, last touched at beb78b7; identical at HEAD and HEAD~1, sha256 `a21a10ecd040…4e5`)
|
||||
Refactored: working tree (`tools/checkin_check.py`)
|
||||
|
||||
## The smell
|
||||
|
||||
`run()`'s census tallied fault verdicts and two advisory metrics by making seven
|
||||
`sum(1 for r in rows if ...)` scans over the same row list:
|
||||
|
||||
```python
|
||||
fault_rows = [r for r in rows if r["class"] or r["duplicate"]]
|
||||
rep = {
|
||||
...
|
||||
"faults": {c: sum(1 for r in rows if c in r["class"]) for c in FAULTS},
|
||||
...
|
||||
"advisory": {
|
||||
"missing_headings": sum(1 for r in rows if r["headings_missing"]),
|
||||
"headings_ok": sum(1 for r in rows
|
||||
if not r["headings_missing"] and "EMPTY" not in r["class"]),
|
||||
"note": "A1/A2 are advisory: the four-heading rule postdates the archive",
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Five verdict passes (`faults`, one per `FAULT` class) differ only by the constant
|
||||
class `c`, plus two advisory passes that filter `rows` by `headings_missing` /
|
||||
`EMPTY`. Same class of defect the forge lane already retired in
|
||||
`append_only_owners.py` (3 scans -> 1, `145009e`), `served_drift_check.py`
|
||||
(4 scans -> 1, `dfd06b3`) and `doc_command_check.py` (tally -> Counter,
|
||||
`86950bc`): identical scans varying only by a constant, over the same list.
|
||||
|
||||
## The refactor
|
||||
|
||||
One pass, `collections.Counter` buckets verdicts by presence while two in-loop
|
||||
counters tally the advisory metrics:
|
||||
|
||||
```python
|
||||
fault_rows = [r for r in rows if r["class"] or r["duplicate"]]
|
||||
# One pass over `rows` replaces seven `sum(1 for r in rows ...)` scans --
|
||||
# five verdict passes (`faults`, one per FAULT class) plus two advisory
|
||||
# passes (`missing_headings`, `headings_ok`). Counter buckets verdicts by
|
||||
# presence: `set(r["class"])` folds any intra-row duplicate so a row counts
|
||||
# once per class, matching `c in r["class"]`; the two advisory counters are
|
||||
# incremented alongside, so each row is touched once, not seven times.
|
||||
fault_counts = Counter()
|
||||
missing_headings = 0
|
||||
headings_ok = 0
|
||||
for r in rows:
|
||||
fault_counts.update(set(r["class"]))
|
||||
if r["headings_missing"]:
|
||||
missing_headings += 1
|
||||
elif "EMPTY" not in r["class"]:
|
||||
headings_ok += 1
|
||||
rep = {
|
||||
...
|
||||
"faults": {c: fault_counts.get(c, 0) for c in FAULTS},
|
||||
...
|
||||
"advisory": {
|
||||
"missing_headings": missing_headings,
|
||||
"headings_ok": headings_ok,
|
||||
"note": "A1/A2 are advisory: the four-heading rule postdates the archive",
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`git diff` for the change: two hunks -- one import (`from collections import
|
||||
Counter`), one census collapse, 19 insertions / 4 deletions (the gaps()`
|
||||
per-handle `nfiles` scan is left in place; it is a grouped filter, not the
|
||||
constant-varying tally, and out of this refactor's scope).
|
||||
|
||||
### Equivalence argument
|
||||
|
||||
- `r["class"]` is a list (see `grade_file`, `rec = {... "class": [] ...}`). The
|
||||
old `{c: sum(1 for r in rows if c in r["class"]) for c in FAULTS}` counts a
|
||||
row once per class `c` it contains. `Counter.update(set(r["class"]))`
|
||||
increments each distinct class once per row -- identical, and `set(...)`
|
||||
additionally guards the (currently impossible) intra-row duplicate that a bare
|
||||
list iteration would have double-counted.
|
||||
- `fault_counts.get(c, 0)` returns 0 for a class no row carries -- exactly the 0
|
||||
the old `sum(1 for ...)` returned over an empty match set.
|
||||
- `r["headings_missing"]` is a list (empty = falsy, non-empty = truthy). The
|
||||
`if/elif` tree reproduces the original predicates verbatim: truthy
|
||||
`headings_missing` -> `missing_headings`; falsy + `EMPTY` present -> neither;
|
||||
falsy + `EMPTY` absent -> `headings_ok`.
|
||||
- `fault_rows` ordering and all other `rep` fields are untouched; only the tally
|
||||
is rebuilt.
|
||||
|
||||
## Evidence (measured)
|
||||
|
||||
The proof lives in `wizard-work/forge/verify_checkin_check_dry_refactor.py`.
|
||||
It materializes the pre-refactor source (`git show HEAD~1:tools/checkin_check.py`)
|
||||
into a temp module and compares it against the working-tree refactored copy across
|
||||
four checks. Run output:
|
||||
|
||||
```
|
||||
[1] micro census: old_census == new_census PASS
|
||||
[2] end-to-end run() faults={'BAD_NAME': 1, 'UNKNOWN_HANDLE': 1, 'BAD_DATE': 1, 'FUTURE': 1, 'EMPTY': 1} fault_rows=6 missing_headings=2 headings_ok=4 sha_orig=a94c7ee8770d sha_refactored=a94c7ee8770d PASS
|
||||
[3] --selftest drift guard rc=0 PASS
|
||||
[4] redundancy (code lines) census Counter builds=1 old-census sum scans=0 PASS
|
||||
|
||||
VERDICT: ALL GREEN -- checkin_check census 7 scans -> 1 Counter pass, byte-identical report dict
|
||||
```
|
||||
|
||||
### Verdict breakdown
|
||||
|
||||
- **[1] MICRO EQUIVALENCE.** A synthetic census of 9 rows covering every `FAULT`
|
||||
class, a multi-class row (`BAD_NAME + UNKNOWN_HANDLE`), absent classes (the
|
||||
0-default path the old `sum` returned), and the `headings_ok`/`EMPTY`
|
||||
neither-column row. Inline `old_census` (the seven scans) and `new_census`
|
||||
(the Counter pass) agree. PASS.
|
||||
- **[2] END-TO-END run() DRIFT.** Both the original module (HEAD~1) and the
|
||||
refactored working tree `run()` the SAME eight-file fixture corpus. The full
|
||||
`rep` dict serializes to byte-identical JSON (sha256
|
||||
`a94c7ee8770d…` for both). The census fields reproduce the selftest's c18 pins
|
||||
exactly: `missing_headings == 2`, `fault_rows == 6`; `faults` is one per class;
|
||||
`headings_ok == 4`. PASS.
|
||||
- **[3] SELFTEST DRIFT GUARD.** `python3 tools/checkin_check.py --selftest` exits 0
|
||||
on the refactored worktree: 35 cells, 35 passed, 0 failed. The tool's own
|
||||
contract (incl. c18: `missing_headings == 2 and ... fault_rows == 6`) pins
|
||||
absolute correctness. PASS.
|
||||
- **[4] REDUNDANCY COLLAPSED.** One `fault_counts = Counter()` census build and
|
||||
zero of the old `sum(1 for r in rows if c in r["class"]`,
|
||||
`sum(1 for r in rows if r["headings_missing"]`, and
|
||||
`if not r["headings_missing"] and "EMPTY" not in r["class"]` scans on code
|
||||
lines (the explanatory comment is excluded by the comment-line filter). The
|
||||
unrelated `gaps()` per-handle `nfiles` scan is deliberately left in place and
|
||||
excluded. PASS.
|
||||
|
||||
## Verdict
|
||||
|
||||
Behavior-preserving DRY consolidation: seven redundant census scans over `rows`
|
||||
collapsed to one `Counter` pass plus two in-loop counters. The refactored
|
||||
`run()` emits a byte-identical report dict (sha256 match) on the full
|
||||
selftest fixture corpus, the tool's own 35-cell selftest is green, and the old
|
||||
scan idiom is gone from the code.
|
||||
|
||||
VERDICT: PASS -- checkin_check census 7 scans -> 1 Counter pass; byte-identical
|
||||
report dict (sha256 a94c7ee8770d), --selftest 35/35, old-census sum scans 0.
|
||||
|
||||
CHECK: python3 /root/troupe-standup/wizard-work/forge/verify_checkin_check_dry_refactor.py >/dev/null 2>&1
|
||||
@@ -0,0 +1,261 @@
|
||||
#!/usr/bin/env python3
|
||||
"""verify_checkin_check_dry_refactor.py
|
||||
|
||||
Equivalence proof for the DRY collapse in tools/checkin_check.py's run():
|
||||
seven `sum(1 for r in rows ...)` scans -- five verdict passes (`faults`, one per
|
||||
FAULT class) plus two advisory passes (`missing_headings`, `headings_ok`) --
|
||||
collapsed into ONE `Counter` pass plus two in-loop counters.
|
||||
|
||||
Behavior-preserving iff run() emits a byte-identical report dict on every input
|
||||
the old and new code both accept. The old `sum(1 for ...)` returns 0 for no
|
||||
matches; the new `fault_counts.get(c, 0)` returns 0 for an unseen class -- same 0.
|
||||
`set(r["class"])` folds intra-row duplicates so each class is counted once per
|
||||
row, matching `c in r["class"`] over a list. Four checks; exit 0 = all green.
|
||||
|
||||
1. MICRO census -- old_census vs new_census on a synthetic rows census
|
||||
covering every FAULT class, multi-class rows, absent classes (0-default),
|
||||
and the headings_ok/EMPTY neither-column case.
|
||||
2. END-TO-END run() census DRIFT -- the ORIGINAL module (git HEAD~1) and the
|
||||
REFACTORED working-tree module both run() the SAME fixture corpus; the full
|
||||
report dict is byte-identical (sha256 match).
|
||||
3. SELFTEST drift guard -- `python3 tools/checkin_check.py --selftest` exits 0
|
||||
on the refactored worktree (the tool's own 35-cell contract, which pins
|
||||
absolute correctness incl. c18: missing_headings == 2, fault_rows == 6).
|
||||
4. REDUNDANCY collapsed -- ONE `fault_counts = Counter()` census build and ZERO
|
||||
of the old census `sum(1 for r in rows ...)` scans (the unrelated gaps()
|
||||
per-handle nfiles scan is left untouched and excluded from the assertion).
|
||||
"""
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections import Counter
|
||||
from datetime import date
|
||||
|
||||
REPO = "/root/troupe-standup"
|
||||
DCC = os.path.join(REPO, "tools", "checkin_check.py")
|
||||
FAULTS = ("BAD_NAME", "UNKNOWN_HANDLE", "BAD_DATE", "FUTURE", "EMPTY")
|
||||
|
||||
# Fixture corpus: the eight-file selftest tree (see checkin_check.selftest c10-c18).
|
||||
TABLE = """# The Wizards Table
|
||||
|
||||
| Wizard | Model | Lane |
|
||||
|---|---|---|
|
||||
| Timmy | Hermes | partner |
|
||||
| Zephyrus Sparkborn | Mistral | archive |
|
||||
| Volt | DeepSeek | workhorse |
|
||||
|
||||
| not a roster row
|
||||
"""
|
||||
GOOD = ("# 2026-10-17 -- volt\n\n## Did\nlanded x\n\n## Blocked\nnothing\n\n"
|
||||
"## Next\ny\n\n## Ask\nno\n")
|
||||
BARE = "# 2026-10-17 -- volt\n\nlanded x\n"
|
||||
|
||||
|
||||
def _mk(d, name, body, encoding="utf-8"):
|
||||
p = os.path.join(d, name)
|
||||
with open(p, "w", encoding=encoding, errors="strict") as fh:
|
||||
fh.write(body)
|
||||
return p
|
||||
|
||||
|
||||
class Verdict(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def sha(text):
|
||||
return hashlib.sha256(text.encode()).hexdigest()
|
||||
|
||||
|
||||
def materialize_original():
|
||||
"""Write the PRE-refactor source (git HEAD~1) under its real name in a temp
|
||||
dir and return the path. HEAD~1 is the parent of the (not-yet-)refactor
|
||||
commit, and checkin_check.py is identical at HEAD and HEAD~1 today, so this
|
||||
reads a genuine original both before the refactor lands and after."""
|
||||
res = subprocess.run(
|
||||
["git", "-C", REPO, "show", "HEAD~1:tools/checkin_check.py"],
|
||||
capture_output=True, text=True)
|
||||
if not res.stdout:
|
||||
raise Verdict("could not read HEAD~1:tools/checkin_check.py: "
|
||||
+ res.stderr.strip())
|
||||
d = tempfile.mkdtemp(prefix="ck_orig_")
|
||||
p = os.path.join(d, "checkin_check.py")
|
||||
with open(p, "w", encoding="utf-8") as fh:
|
||||
fh.write(res.stdout)
|
||||
return d, p
|
||||
|
||||
|
||||
def load_module(path, name):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# OLD census: the seven inline sum scans, verbatim pre-refactor.
|
||||
# ---------------------------------------------------------------------------
|
||||
def old_census(rows):
|
||||
faults = {c: sum(1 for r in rows if c in r["class"]) for c in FAULTS}
|
||||
missing_headings = sum(1 for r in rows if r["headings_missing"])
|
||||
headings_ok = sum(1 for r in rows
|
||||
if not r["headings_missing"] and "EMPTY" not in r["class"])
|
||||
return {"faults": faults, "missing_headings": missing_headings,
|
||||
"headings_ok": headings_ok}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# NEW census: one Counter pass + two in-loop counters, verbatim refactored.
|
||||
# ---------------------------------------------------------------------------
|
||||
def new_census(rows):
|
||||
fault_counts = Counter()
|
||||
missing_headings = 0
|
||||
headings_ok = 0
|
||||
for r in rows:
|
||||
fault_counts.update(set(r["class"]))
|
||||
if r["headings_missing"]:
|
||||
missing_headings += 1
|
||||
elif "EMPTY" not in r["class"]:
|
||||
headings_ok += 1
|
||||
faults = {c: fault_counts.get(c, 0) for c in FAULTS}
|
||||
return {"faults": faults, "missing_headings": missing_headings,
|
||||
"headings_ok": headings_ok}
|
||||
|
||||
|
||||
def micro_rows():
|
||||
"""Covers every FAULT class, multi-class rows, absent classes (0-default),
|
||||
and the headings_ok/EMPTY neither-column case."""
|
||||
return [
|
||||
# conforming: no class, all headings present -> headings_ok, no faults
|
||||
{"file": "2026-10-17-volt.md", "class": [], "headings_missing": [],
|
||||
"duplicate": None},
|
||||
# BAD_NAME, all headings missing -> missing_headings, not headings_ok
|
||||
{"file": "2026-10-17-zephyrus-02.md", "class": ["BAD_NAME"],
|
||||
"headings_missing": ["did", "blocked", "next", "ask"],
|
||||
"duplicate": {"date": "2026-10-17", "handle": "zephyrus", "group_size": 2}},
|
||||
# UNKNOWN_HANDLE only, headings present -> headings_ok
|
||||
{"file": "2026-10-16-gandalf.md", "class": ["UNKNOWN_HANDLE"],
|
||||
"headings_missing": [], "duplicate": None},
|
||||
{"file": "2026-02-30-volt.md", "class": ["BAD_DATE"],
|
||||
"headings_missing": [], "duplicate": None},
|
||||
{"file": "2099-01-01-volt.md", "class": ["FUTURE"],
|
||||
"headings_missing": [], "duplicate": None},
|
||||
# multi-class row: BAD_NAME + UNKNOWN_HANDLE (exercises set() per row)
|
||||
{"file": "2026-10-14-zod.md", "class": ["BAD_NAME", "UNKNOWN_HANDLE"],
|
||||
"headings_missing": ["did"], "duplicate": None},
|
||||
# EMPTY + headings_missing falsy -> belongs to NEITHER column
|
||||
{"file": "2026-10-15-volt.md", "class": ["EMPTY"], "headings_missing": [],
|
||||
"duplicate": None},
|
||||
{"file": "2026-10-13-volt.md", "class": ["EMPTY"], "headings_missing": [],
|
||||
"duplicate": None},
|
||||
# duplicate-only: no class, headings present -> headings_ok, not a fault
|
||||
{"file": "2026-10-12-volt.md", "class": [], "headings_missing": [],
|
||||
"duplicate": {"date": "2026-10-12", "handle": "volt", "group_size": 2}},
|
||||
]
|
||||
|
||||
|
||||
def build_fixture(td):
|
||||
repo = os.path.join(td, "repo")
|
||||
ck = os.path.join(repo, "checkins")
|
||||
os.makedirs(ck)
|
||||
_mk(repo, "WIZARDS-TABLE.md", TABLE)
|
||||
_mk(ck, "2026-10-17-volt.md", GOOD)
|
||||
_mk(ck, "2026-10-17-zephyrus.md", BARE)
|
||||
_mk(ck, "2026-10-17-zephyrus-02.md", BARE)
|
||||
_mk(ck, "2026-10-16-gandalf.md", GOOD)
|
||||
_mk(ck, "2026-02-30-volt.md", GOOD)
|
||||
_mk(ck, "2099-01-01-volt.md", GOOD)
|
||||
_mk(ck, "2026-10-15-volt.md", "")
|
||||
_mk(ck, "README.md", "# Check-ins\n\nOne file per person per day.\n")
|
||||
return ck, repo
|
||||
|
||||
|
||||
def main():
|
||||
ok = True
|
||||
orig_dir, orig_path = materialize_original()
|
||||
try:
|
||||
tm_orig = load_module(orig_path, "ck_orig")
|
||||
tm_ref = load_module(DCC, "ck_ref")
|
||||
|
||||
# [1] MICRO census equivalence on the synthetic rows census.
|
||||
rows = micro_rows()
|
||||
o, n = old_census(rows), new_census(rows)
|
||||
micro_ok = o == n
|
||||
print("[1] micro census: old_census == new_census ",
|
||||
"PASS" if micro_ok else "FAIL")
|
||||
if not micro_ok:
|
||||
ok = False
|
||||
print(" old=%r" % (o,))
|
||||
print(" new=%r" % (n,))
|
||||
|
||||
# [2] END-TO-END run() census drift: original (HEAD~1) vs refactored (tree)
|
||||
# on the SAME fixture corpus; the full report dict must be byte-identical.
|
||||
with __import__("tempfile").TemporaryDirectory() as td:
|
||||
ck, repo = build_fixture(td)
|
||||
roster, _ = tm_orig.load_roster(repo, None)
|
||||
today = date(2026, 10, 18)
|
||||
rep_o = tm_orig.run(ck, roster, today, True)
|
||||
rep_n = tm_ref.run(ck, roster, today, True)
|
||||
jo = json.dumps(rep_o, sort_keys=True)
|
||||
jn = json.dumps(rep_n, sort_keys=True)
|
||||
drift_ok = jo == jn
|
||||
# Spotlight the refactored fields in the evidence trail.
|
||||
fo = rep_o["faults"]
|
||||
print("[2] end-to-end run() faults=%s fault_rows=%d "
|
||||
"missing_headings=%d headings_ok=%d "
|
||||
"sha_orig=%s sha_refactored=%s %s"
|
||||
% (fo, rep_o["fault_rows"],
|
||||
rep_o["advisory"]["missing_headings"],
|
||||
rep_o["advisory"]["headings_ok"],
|
||||
sha(jo)[:12], sha(jn)[:12],
|
||||
"PASS" if drift_ok else "FAIL"))
|
||||
if not drift_ok:
|
||||
ok = False
|
||||
print(" DIFF in report dict between original and refactored")
|
||||
|
||||
# [3] drift guard -- the tool's own 35-cell selftest on the worktree.
|
||||
rc = subprocess.run([sys.executable, DCC, "--selftest"],
|
||||
capture_output=True, text=True)
|
||||
guard_ok = rc.returncode == 0
|
||||
print("[3] --selftest drift guard rc=%d %s"
|
||||
% (rc.returncode, "PASS" if guard_ok else "FAIL"))
|
||||
if not guard_ok:
|
||||
ok = False
|
||||
print(" " + (rc.stdout + rc.stderr).strip()[:400].replace("\n", "\n "))
|
||||
|
||||
# [4] redundancy collapsed -- ONE census Counter build, ZERO old census
|
||||
# scans. Code lines only (the refactor's comment mentions the old idiom).
|
||||
text = open(DCC, encoding="utf-8").read()
|
||||
code = "\n".join(ln for ln in text.splitlines()
|
||||
if not ln.lstrip().startswith("#"))
|
||||
builds = code.count('fault_counts = Counter()')
|
||||
scans = (code.count('sum(1 for r in rows if c in r["class"]')
|
||||
+ code.count('sum(1 for r in rows if r["headings_missing"]')
|
||||
+ code.count('if not r["headings_missing"] and "EMPTY" not in r["class"]'))
|
||||
red_ok = builds == 1 and scans == 0
|
||||
print("[4] redundancy (code lines) census Counter builds=%d "
|
||||
"old-census sum scans=%d %s"
|
||||
% (builds, scans, "PASS" if red_ok else "FAIL"))
|
||||
if not red_ok:
|
||||
ok = False
|
||||
|
||||
finally:
|
||||
shutil.rmtree(orig_dir, ignore_errors=True)
|
||||
|
||||
print("\nVERDICT:",
|
||||
"ALL GREEN -- checkin_check census 7 scans -> 1 Counter pass, "
|
||||
"byte-identical report dict" if ok
|
||||
else "MISMATCH -- behavior NOT preserved")
|
||||
sys.exit(0 if ok else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Verdict as e:
|
||||
print("\nVERDICT: MISMATCH -- %s" % e)
|
||||
sys.exit(1)
|
||||
Reference in new issue
Block a user