chore: SETUP.md + 测试报告脚本 + 文档更新

- SETUP.md: 完整环境搭建指南(同事用)
- SETUP_QUICK.md: 快速搭环境(4步)
- s22~s26: TNA端到端、覆盖率报告、回归检查
- procedure_grammar.lark: 实验性Lark语法

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
NB-076
2026-06-25 08:50:17 +08:00
parent 56d1cf5e78
commit 50995d3335
25 changed files with 6861 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
"""S23: Per-program branch coverage + code coverage report"""
import sys, os, re, subprocess, time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
ROOT_BENCH = "D:/cobol-java/cobol-test-programs/"
COPYBOOKS_BENCH = os.path.join(ROOT_BENCH, "common", "copybooks")
ROOT_TNA = "D:/cobol-java/cobol-tna-system/"
COPYBOOKS_TNA = os.path.join(ROOT_TNA, "cpy")
from cobol_testgen import extract_structure, generate_data
from cobol_testgen.read import preprocess, resolve_copybooks, extract_data_division, extract_procedure_division, parse_data_division
from cobol_testgen.design_mcdc import enum_paths
from cobol_testgen.pipeline_bridge import build_branch_tree_fallback
from cobol_testgen.flatfile import analyze_fd_layout
def find_main(d):
cbls = [f for f in os.listdir(d) if f.endswith('.cbl')]
ws = [f for f in cbls if re.match(r'main-\d{2}-', f, re.IGNORECASE)]
if ws: return max(ws, key=lambda f: os.path.getsize(os.path.join(d, f)))
return max(cbls, key=lambda f: os.path.getsize(os.path.join(d, f))) if cbls else None
def analyze_one(name, fpath, source_dir, copybook_dirs):
"""Return dict: {branches, dpoints, paths, records, flat_files, lines, code_lines, compile, run, error}"""
result = {"name": name, "branches": 0, "dpoints": 0, "paths": 0, "records": 0,
"flat_files": 0, "lines": 0, "code_lines": 0, "compile": "-", "run": "-", "error": ""}
try:
src = open(fpath, encoding="utf-8-sig").read()
result["lines"] = len(src.split("\n"))
result["code_lines"] = sum(1 for l in src.split("\n") if l.strip() and not l.strip().startswith("*"))
t0 = time.time()
st = extract_structure(src)
result["branches"] = st.get("total_branches", 0)
result["dpoints"] = len(st.get("decision_points", []))
pp = resolve_copybooks(src, source_dir, extra_search_paths=copybook_dirs)
pp = preprocess(pp)
recs = generate_data(pp, st)
result["records"] = len(recs)
# Coverage data from generate_data (mark_coverage result)
cov = st.get('coverage', {})
result["cov_total"] = cov.get('total', 0)
result["cov_covered"] = cov.get('covered', 0)
result["cov_pct"] = cov.get('pct', 0)
# Path count
dd = extract_data_division(pp)
fields = parse_data_division(dd) if dd else []
fdict = [{'name': f.name, 'pic_info': {'type': f.pic_info.type if f.pic_info else 'unknown'}} for f in fields]
proc = extract_procedure_division(pp)
tree, ass = build_branch_tree_fallback(proc, fdict)
paths = enum_paths(tree, fdict)
result["paths"] = len(paths)
layouts = analyze_fd_layout(pp)
result["flat_files"] = len(layouts)
result["time_ms"] = int((time.time()-t0)*1000)
except Exception as e:
result["error"] = str(e)[:80]
return result
def analyze_tna(name, fpath):
"""Analyze TNA program"""
return analyze_one(name, fpath, os.path.dirname(fpath), [COPYBOOKS_TNA])
def analyze_bench(name, fpath):
"""Analyze benchmark program"""
return analyze_one(name, fpath, os.path.dirname(fpath), [COPYBOOKS_BENCH])
# ── Run all benchmark programs ──
print("=" * 110)
print(f"{'Program':<28} {'Br':>4} {'DPs':>4} {'Paths':>5} {'Recs':>4} {'Flats':>4} {'CovBr':>5} {'Cov%':>5} {'Lines':>5} {'CodeL':>5} {'Time':>6}")
print("-" * 110)
bench_results = []
for d in sorted(os.listdir(ROOT_BENCH)):
dp = os.path.join(ROOT_BENCH, d)
if not os.path.isdir(dp) or d in ('common','docs','cross-cutting'): continue
fn = find_main(dp)
if not fn: continue
r = analyze_bench(d, os.path.join(dp, fn))
bench_results.append(r)
br_pct = r["paths"] / r["branches"] * 100 if r["branches"] > 0 else 0
cov_pct = r.get("cov_pct", 0)
codel = r["code_lines"]
status = r.get("error", "")[:8] if r.get("error") else ""
print(f" {r['name']:<28} {r['branches']:>4} {r['dpoints']:>4} {r['paths']:>5} {r['records']:>4} {r['flat_files']:>4} {r.get('cov_covered',0):>5} {cov_pct:>4.0f}% {r['lines']:>5} {r['code_lines']:>5} {r.get('time_ms',0):>5}ms {status}")
# ── Run all TNA programs ──
print("-" * 110)
for f in ["ZAN01CHK", "ZAN02CHK", "ZAN03CHK", "ZAN04MAT", "ZAN05CAL", "ZAN06UPD"]:
fpath = os.path.join(ROOT_TNA, "src", f + ".cbl")
if not os.path.exists(fpath): continue
r = analyze_tna(f, fpath)
bench_results.append(r)
cov_pct = r.get("cov_pct", 0)
codel = r["code_lines"]
status = r.get("error", "")[:8] if r.get("error") else ""
print(f" {r['name']:<28} {r['branches']:>4} {r['dpoints']:>4} {r['paths']:>5} {r['records']:>4} {r['flat_files']:>4} {r.get('cov_covered',0):>5} {cov_pct:>4.0f}% {r['lines']:>5} {r['code_lines']:>5} {r.get('time_ms',0):>5}ms {status}")
print("=" * 110)
# ── Totals ──
total_br = sum(r["branches"] for r in bench_results)
total_paths = sum(r["paths"] for r in bench_results)
total_recs = sum(r["records"] for r in bench_results)
total_lines = sum(r["code_lines"] for r in bench_results)
total_flats = sum(r["flat_files"] for r in bench_results)
total_cov = sum(r.get("cov_covered", 0) for r in bench_results)
total_cov_all = sum(r.get("cov_total", 0) for r in bench_results)
with_br = sum(1 for r in bench_results if r["branches"] > 0)
print(f"\n{'TOTAL':<28} {total_br:>4} {total_paths:>5} {total_recs:>4} {total_flats:>4} {total_cov:>5} {total_cov/max(total_cov_all,1)*100:>4.0f}%")
print(f"Programs with branch detection: {with_br}/{len(bench_results)}")
print(f"Total code lines (non-comment): {total_lines}")
print(f"\n{'='*110}")
print("NOTES:")
print(" Br = Decision branches detected by static analysis")
print(" DPs = Decision points (IF/EVAL/PERFORM)")
print(" Paths = Generated test paths (O(N) linear)")
print(" Recs = Generated data records")
print(" CovBr = Branches actually covered by generated data")
print(" Cov% = Real branch coverage via mark_coverage")
print(" Time = Parse + generate time in ms")