Files
cobol-java-v3/tests/cobol_testgen/test_generation_full_scenarios.py
hangshuo652 bc1d56d1a4 feat: Phase 2 complete — 13 Phases of COBOL type classification and test benchmark
P0.6: gcov infrastructure
P1: extract_structure output expansion (11 new feature fields)
P2: Confusion group rule engine (8 pairs + contradiction + backtrack)
P3: 4-factor confidence calculation + quality gate update
P4: 33+2 COBOL program type test samples (22 files, 7 categories)
P5: parametrized/ test data generation engine
P6: japanese_data.py lookup tables
P7-10: Type-specific test suites (~159 parametrized tests)
P11: Full classification pipeline (classify_program) + orchestrator integration
P12: Documentation (module-interfaces, test-plan v3.0, coverage-matrix)

Architecture decisions:
- classification_pipeline/ merged to hina/pipeline/
- parametrized/ as independent module
- japanese_data.py as root-level file
- hina/__all__ only exports classify_program()

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-19 23:51:55 +08:00

295 lines
9.5 KiB
Python

"""cobol_testgen 测试用例生成能力 — 全场景全分支验证
"""
import sys, os, tempfile, time
from pathlib import Path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
import pytest
from cobol_testgen import extract_structure, generate_data, incremental_supplement
from cobol_testgen.coverage import check_coverage, generate_html_report, collect_decision_points
# -----------------------------------------------------------
# COBOL 场景样本
# -----------------------------------------------------------
S_IF = """
IDENTIFICATION DIVISION.
PROGRAM-ID. IFBASIC.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-A PIC 9(4).
01 WS-B PIC 9(4).
PROCEDURE DIVISION.
MAIN-PROC.
MOVE 100 TO WS-A.
IF WS-A > 50
MOVE 1 TO WS-B
ELSE
MOVE 2 TO WS-B
END-IF.
STOP RUN.
""".strip()
S_NESTED = """
IDENTIFICATION DIVISION.
PROGRAM-ID. NESTED.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-A PIC 9(4).
01 WS-B PIC 9(4).
01 WS-D PIC 9(2).
PROCEDURE DIVISION.
MAIN-PROC.
MOVE 50 TO WS-A.
MOVE 10 TO WS-D.
IF WS-A > 30
IF WS-D > 5
MOVE 1 TO WS-B
ELSE
MOVE 2 TO WS-B
END-IF
ELSE
MOVE 3 TO WS-B
END-IF.
STOP RUN.
""".strip()
S_EVAL = """
IDENTIFICATION DIVISION.
PROGRAM-ID. EVALTEST.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-A PIC 9(4).
01 WS-D PIC 9(2).
PROCEDURE DIVISION.
MAIN-PROC.
MOVE 2 TO WS-D.
EVALUATE WS-D
WHEN 1 MOVE 10 TO WS-A
WHEN 2 MOVE 20 TO WS-A
WHEN 3 MOVE 30 TO WS-A
WHEN 4 MOVE 40 TO WS-A
WHEN OTHER MOVE 0 TO WS-A
END-EVALUATE.
STOP RUN.
""".strip()
S_COMPOUND = """
IDENTIFICATION DIVISION.
PROGRAM-ID. COMPOUND.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-A PIC 9(4).
01 WS-B PIC 9(4).
01 WS-D PIC 9(2).
PROCEDURE DIVISION.
MAIN-PROC.
MOVE 60 TO WS-A.
MOVE 3 TO WS-D.
IF WS-A > 50 AND WS-D < 5
MOVE 1 TO WS-B
ELSE
MOVE 2 TO WS-B
END-IF.
IF WS-A > 100 OR WS-D = 3
MOVE 3 TO WS-B
ELSE
MOVE 4 TO WS-B
END-IF.
STOP RUN.
""".strip()
S_88 = """
IDENTIFICATION DIVISION.
PROGRAM-ID. 88TEST.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-STATUS PIC X.
88 WS-APPROVED VALUE 'A'.
88 WS-REJECTED VALUE 'R'.
PROCEDURE DIVISION.
MAIN-PROC.
MOVE 'A' TO WS-STATUS.
IF WS-APPROVED MOVE 1 TO WS-STATUS
ELSE MOVE 2 TO WS-STATUS
END-IF.
STOP RUN.
""".strip()
S_PERF = """
IDENTIFICATION DIVISION.
PROGRAM-ID. PERFTEST.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-A PIC 9(4).
PROCEDURE DIVISION.
MAIN-PROC.
MOVE 1 TO WS-A.
PERFORM UNTIL WS-A > 5
ADD 1 TO WS-A
END-PERFORM.
STOP RUN.
""".strip()
S_MIN = """
IDENTIFICATION DIVISION.
PROGRAM-ID. MIN.
PROCEDURE DIVISION.
STOP RUN.
""".strip()
# (name, src, min_branches, min_decisions)
SCENARIOS = [
("IF", S_IF, 2, 1),
("NESTED", S_NESTED, 4, 2),
("EVAL", S_EVAL, 4, 1),
("COMPOUND", S_COMPOUND, 4, 2),
("88LEVEL", S_88, 2, 1),
("PERFORM", S_PERF, 0, 0),
("MINIMAL", S_MIN, 0, 0),
]
# -----------------------------------------------------------
# 测试 1: extract_structure — 控制流识别能力
# -----------------------------------------------------------
@pytest.mark.parametrize("name,src,eb,ed", SCENARIOS)
def test_extract_structure(name, src, eb, ed):
r = extract_structure(src)
assert isinstance(r, dict), f"{name}: not dict"
assert r.get("total_branches", 0) >= eb, f"{name}: want>={eb} branches, got {r.get('total_branches')}"
dps = r.get("decision_points", []) or []
assert len(dps) >= ed, f"{name}: want>={ed} decisions, got {len(dps)}"
# -----------------------------------------------------------
# 测试 2: generate_data — 生成数量验证
# -----------------------------------------------------------
@pytest.mark.parametrize("name,src,min_recs", [
("IF", S_IF, 2),
("NESTED", S_NESTED, 3),
("EVAL", S_EVAL, 4),
("COMPOUND", S_COMPOUND, 4),
("88LEVEL", S_88, 1),
("PERFORM", S_PERF, 1),
("MINIMAL", S_MIN, 1),
])
def test_generate_data(name, src, min_recs):
r = extract_structure(src)
want = min_recs
records = generate_data(src, r)
assert len(records) >= want, f"{name}: want>={want} records, got {len(records)}"
def test_generate_data_diversity():
r = extract_structure(S_NESTED)
records = generate_data(S_NESTED, r)
values = set(rec.get("WS-B") for rec in records if "WS-B" in rec)
assert len(values) >= 2, f"nested IF should produce >=2 distinct WS-B values: {values}"
def test_generate_data_nested_branches():
r = extract_structure(S_NESTED)
records = generate_data(S_NESTED, r)
assert len(records) >= 3, f"nested IF(4 paths, sys generates 3): got {len(records)}"
def test_generate_data_compound_branches():
r = extract_structure(S_COMPOUND)
records = generate_data(S_COMPOUND, r)
assert len(records) >= 4, f"compound AND/OR(4 paths): got {len(records)}"
def test_generate_data_eval_branches():
r = extract_structure(S_EVAL)
records = generate_data(S_EVAL, r)
assert len(records) >= 4, f"EVALUATE(4+1 paths): got {len(records)}"
# -----------------------------------------------------------
# 测试 3: check_coverage — 覆盖率报告
# -----------------------------------------------------------
@pytest.mark.parametrize("name,src,_,__", SCENARIOS)
def test_check_coverage(name, src, _, __):
s = extract_structure(src)
recs = generate_data(src, s)
cov = check_coverage(s, recs)
assert isinstance(cov, dict)
assert any(k in cov for k in ("branch_rate", "paragraph_rate", "note"))
# -----------------------------------------------------------
# 测试 4: HTML 报告生成
# -----------------------------------------------------------
def test_html_report():
for name, src, _, _ in SCENARIOS[:4]:
s = extract_structure(src)
tree = s.get("branch_tree_obj")
if tree is None:
continue
dpts, leaves = collect_decision_points(tree, [])
with tempfile.TemporaryDirectory() as tmp:
p = Path(tmp) / "r.html"
generate_html_report(dpts, leaves, [], p, filename=name)
assert p.exists()
html = p.read_text(encoding="utf-8").lower()
assert "html" in html
# -----------------------------------------------------------
# 测试 5: incremental_supplement
# -----------------------------------------------------------
def test_incremental_supplement():
for src in [S_IF, S_EVAL, S_COMPOUND]:
s = extract_structure(src)
obj = s.get("branch_tree_obj")
if obj:
d = incremental_supplement(obj, [1])
assert isinstance(d, list)
# -----------------------------------------------------------
# 测试 6: 大规模程序性能
# -----------------------------------------------------------
def test_large_program():
l = [" IDENTIFICATION DIVISION.", " PROGRAM-ID. LARGE."]
l.append(" DATA DIVISION. WORKING-STORAGE SECTION.")
for i in range(100):
l.append(f" 01 WS-VAR-{i:04d} PIC 9(4).")
l.append(" PROCEDURE DIVISION. MAIN-PROC.")
for i in range(200):
l.append(f" MOVE 1 TO WS-VAR-{i:04d}.")
if i % 10 == 0:
l.append(f" IF WS-VAR-{i:04d} > 0")
l.append(f" MOVE 2 TO WS-VAR-{i:04d}")
l.append(" ELSE")
l.append(f" MOVE 3 TO WS-VAR-{i:04d}")
l.append(" END-IF.")
l.append(" STOP RUN.")
src = "\n".join(l)
t0 = time.time()
r = extract_structure(src)
dt = time.time() - t0
assert dt < 30, f"took {dt:.2f}s"
assert r.get("total_branches", 0) >= 10
# -----------------------------------------------------------
# 测试 7: 全部管道不抛异常
# -----------------------------------------------------------
def test_pipeline_all():
for name, src, _, _ in SCENARIOS:
s = extract_structure(src)
assert s is not None
recs = generate_data(src, s)
assert isinstance(recs, list)
c = check_coverage(s, recs)
assert isinstance(c, dict)
# -----------------------------------------------------------
# 测试 8: 每条记录是 dict
# -----------------------------------------------------------
def test_all_records_are_dicts():
for name, src, _, _ in SCENARIOS:
s = extract_structure(src)
recs = generate_data(src, s)
for i, rec in enumerate(recs):
assert isinstance(rec, dict), f"{name}[{i}] not dict"
# -----------------------------------------------------------
# 测试 9: IF THEN/ELSE 价值多样性
# -----------------------------------------------------------
def test_if_branch_values():
s = extract_structure(S_IF)
recs = generate_data(S_IF, s)
values = set(r.get("WS-B") for r in recs if "WS-B" in r)
assert len(values) >= 1