fix: 覆盖率统计全面修复 + 5漏洞修正

## 修复内容

### C1: _mark_eval 反向操作符 (coverage.py)
- EVALUATE 约束匹配支持  操作符
- WHEN OTHER 的自动检测(全部 WHEN 被否定时)

### C2: _mark_perform 反向操作符 (coverage.py)
- PERFORM 同 _mark_if 的反向操作符匹配
- PERFORM UNTIL 条件截断后桥接器通过 branch_names 识别类型

### H1: parse_single_condition 传递 fields (coverage.py)
- collect_decision_points 调用时传 fields 参数
- NOT 前缀条件解析 (NOT WS-X > 50 → WS-X <= 50)

### H4: generate_data 输入约束 (__init__.py)
- 文档注明接收原始源码,非预处理后文本

### M1: not_map break (cond.py)
- NOT 操作符映射循环添加 break

## 覆盖测试结果
- IF: 100% (T/F)
- NOT IF: 100% (NOT_TRUE/NOT_FALSE)
- PERFORM UNTIL: 100% (ENTER/SKIP)
- EVALUATE: 100% (4 WHENs)
- Nested IF: 100% (4 branches)
- S15 回归: 17/17 PASS

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
NB-076
2026-06-24 21:14:50 +08:00
parent 7fb9304212
commit e2a8d53e60
6 changed files with 104 additions and 15 deletions
+26 -2
View File
@@ -29,7 +29,7 @@ from .pipeline_bridge import build_branch_tree_fallback
from .design_mcdc import enum_paths as mcdc_enum_paths, _filter_stop
from .design import enum_paths, generate_records, get_term_type, extend_abend_programs
from .output import output_json, output_input_files
from .coverage import run_coverage, generate_coverage_index
from .coverage import run_coverage, generate_coverage_index, collect_decision_points, mark_coverage
from japanese_data import generate_fullwidth_text, generate_halfwidth_katakana, generate_wareki_date
try:
@@ -935,7 +935,9 @@ def generate_data(cobol_source: str, structure: dict = None) -> list[dict]:
"""根据 COBOL 源码生成覆盖所有路径的测试数据。
Args:
cobol_source: COBOL 程序源码文本
cobol_source: COBOL 程序原始源码文本(未预处理)。
内部会调 preprocess + resolve_copybooks。
如果已预处理过,传进来会因 COPYBOOK 路径丢失导致字段不全。
structure: 可选,如果已调用 extract_structure() 可传入避免重复解析
Returns:
@@ -1010,6 +1012,28 @@ def generate_data(cobol_source: str, structure: dict = None) -> list[dict]:
records, kept_paths, term_types = generate_records(path_infos, fields_dict, assignments, file_sec=file_sec)
# ── Coverage marking: which decision branches are actually covered ──
if branch_tree and fields_dict:
try:
dp_list, leaf_stats = collect_decision_points(branch_tree, fields_dict)
cov_paths = [(pi[0], pi[1]) for pi in path_infos if isinstance(pi, (list, tuple)) and len(pi) >= 2]
mark_coverage(dp_list, leaf_stats, cov_paths, fields_dict)
if structure is not None:
structure['coverage'] = {
'decision_points': [{
'id': dp.id, 'kind': dp.kind,
'label': getattr(dp, 'label', '')[:60],
'branches': len(dp.branch_names),
'covered': len(dp.active_branches),
} for dp in dp_list],
'total': sum(len(dp.branch_names) for dp in dp_list),
'covered': sum(len(dp.active_branches) for dp in dp_list),
'pct': sum(len(dp.active_branches) for dp in dp_list) / max(sum(len(dp.branch_names) for dp in dp_list), 1) * 100,
}
except Exception as e:
if structure is not None:
structure['coverage'] = {'error': str(e)[:80]}
if records:
import re as _re
proc_upper = (proc_div or "").upper()