Files
cobol-java-v3/cobol_testgen/gcov.py
T
hangshuo652 40e8a50ab4 fix: gcov Windows本地化 + runner合并 + 决策覆盖率修复
- gcov从WSL 15.2改为Windows MinGW 9.2.0,消除.gcno格式不匹配
- coverage.py去除leaf_stats复位bug(决策覆盖率恢复)
- runner.py合并V3编译+SOURCE分组执行+出力保存
- __init__.py gcov条件/outdir解析修复
2026-07-02 21:26:51 +08:00

128 lines
4.2 KiB
Python

"""gcov 覆盖率数据解析和分支标记"""
import re
import logging
import subprocess
from pathlib import Path
logger = logging.getLogger(__name__)
def parse_cbl_gcov(gcov_path: str) -> dict[int, int]:
"""解析 .cbl.gcov 文件,返回 {COBOL行号: 执行次数}。
gcov 行格式:
#####: 6: 源码行 → 未执行(0 次)
75*: 12: 源码行 → 执行 75 次
1*: 14: 源码行 → 执行 1 次
-: 17: 源码行 → 不可执行(注释/声明行,跳过)
"""
counts = {}
with open(gcov_path, encoding='utf-8', errors='replace') as f:
for line in f:
m = re.match(r'^\s*(#####|\d+\*?|-):\s*(\d+):', line)
if not m:
continue
count_str = m.group(1)
lineno = int(m.group(2))
if count_str == '#####':
counts[lineno] = 0
elif count_str == '-':
continue
else:
counts[lineno] = int(count_str.rstrip('*'))
return counts
def run_gcov(program_name: str, work_dir: str) -> dict[int, int]:
"""在 work_dir 中执行 gcov 并解析 COBOL 行计数。
使用 Windows 本地的 gcov(与 GnuCOBOL 内置 MinGW 同版本)。
不要通过 WSL 调用 gcov,否则 .gcno/.gcda 版本不匹配。
Args:
program_name: 程序名(不含扩展名),如 "ALLCMDS"
work_dir: 包含 .gcda/.gcno 的目录(Windows 路径)
Returns:
{COBOL行号: 执行次数} 字典。失败时返回空 dict。
"""
try:
result = subprocess.run(
['gcov', f'{program_name}.cbl'],
capture_output=True, text=True,
encoding='utf-8', errors='replace',
timeout=30,
cwd=work_dir,
)
except FileNotFoundError:
logger.warning("gcov 命令未找到(未安装 MinGW gcov 或不在 PATH 中)")
return {}
if result.returncode != 0:
logger.warning(f"gcov 失败 (exit={result.returncode}): {result.stderr.strip()}")
return {}
cbl_gcov = Path(work_dir) / f'{program_name}.cbl.gcov'
if not cbl_gcov.exists():
cbl_gcov = Path(work_dir) / f'{program_name}.gcov'
if not cbl_gcov.exists():
logger.warning(f"gcov 输出不存在 (tried .cbl.gcov / .gcov)")
return {}
gcov_data = parse_cbl_gcov(str(cbl_gcov))
logger.info(f"gcov 解析: {len(gcov_data)} 行, "
f"{sum(1 for v in gcov_data.values() if v > 0)} 行已执行")
return gcov_data
def _wsl_path(windows_path: str) -> str:
path = Path(windows_path).resolve()
drive = path.drive.lower().rstrip(':')
rest = str(path.relative_to(path.anchor)).replace('\\', '/')
return f'/mnt/{drive}/{rest}'
def mark_from_gcov(decision_points: list, gcov_data: dict[int, int],
branch_tree) -> None:
"""用 gcov 行执行计数推断决策点分支覆盖,直接修改 decision_points 的 active_branches。
推断规则(简化版,先覆盖主要场景):
IF (条件行 L):
- 条件行 L 在 gcov 中 count == 0 → 不可到达,不标记
- 条件行 L 在 gcov 中 count > 0 → 标记 T 和 F 都覆盖
EVALUATE:
- subject 行 count > 0 → 标记所有 WHEN 为已覆盖
PERFORM UNTIL (条件行 L):
- count == 1 → 条件初始即为真,循环体未进入 → Skip 覆盖
- count > 1 → 循环体至少进入一次 → Enter 覆盖
- Skip 总视为覆盖(无论进入与否,最终都会跳出)
"""
for dp in decision_points:
ln = dp.source_line
if ln <= 0 or ln not in gcov_data:
continue
count = gcov_data.get(ln)
if count is None:
continue
if dp.kind == 'IF':
if count == 0:
continue
dp.active_branches.add('T')
dp.active_branches.add('F')
elif dp.kind == 'EVALUATE':
if count == 0:
continue
for bn in dp.branch_names:
dp.active_branches.add(bn)
elif dp.kind == 'PERFORM':
if count > 1:
dp.active_branches.add('Enter')
dp.active_branches.add('Skip')