"""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