bc1d56d1a4
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>
153 lines
4.8 KiB
Python
153 lines
4.8 KiB
Python
"""gcov 覆盖率采集全链路测试
|
|
|
|
测试内容:
|
|
1. cobc --coverage 编译含 IF 分支的简单 COBOL 程序
|
|
2. 运行生成 .gcda 文件
|
|
3. collect_gcov() 解析 line_rate > 0
|
|
4. 清理中间产物
|
|
"""
|
|
|
|
import sys, os, subprocess, tempfile
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
|
|
import pytest
|
|
from hina.gcov_collector import collect_gcov
|
|
|
|
|
|
HAVE_COBC = None
|
|
|
|
|
|
def _check_cobc() -> bool:
|
|
"""检查 cobc 是否在 PATH 且支持 --coverage"""
|
|
global HAVE_COBC
|
|
if HAVE_COBC is not None:
|
|
return HAVE_COBC
|
|
try:
|
|
r = subprocess.run(["cobc", "--version"], capture_output=True, text=True, timeout=15)
|
|
HAVE_COBC = r.returncode == 0
|
|
except FileNotFoundError:
|
|
HAVE_COBC = False
|
|
return HAVE_COBC
|
|
|
|
|
|
# ── 嵌入一个简单的 COBOL 程序 (IF 分支) ──
|
|
|
|
SAMPLE_COBOL = """\
|
|
IDENTIFICATION DIVISION.
|
|
PROGRAM-ID. test-gcov.
|
|
DATA DIVISION.
|
|
WORKING-STORAGE SECTION.
|
|
01 WS-X PIC 9(2) VALUE 0.
|
|
01 WS-Y PIC 9(2) VALUE 0.
|
|
PROCEDURE DIVISION.
|
|
MOVE 10 TO WS-X.
|
|
IF WS-X > 5 THEN
|
|
MOVE 1 TO WS-Y
|
|
ELSE
|
|
MOVE 2 TO WS-Y
|
|
END-IF.
|
|
DISPLAY "Y=" WS-Y.
|
|
STOP RUN.
|
|
"""
|
|
|
|
|
|
# ── 夹具: 创建临时目录存放 COBOL 源和编译产物 ──
|
|
|
|
|
|
@pytest.fixture
|
|
def work_dir() -> Path:
|
|
"""创建临时工作目录"""
|
|
with tempfile.TemporaryDirectory(prefix="gcov_test_") as tmp:
|
|
yield Path(tmp)
|
|
|
|
|
|
# ── 辅助函数 ──
|
|
|
|
|
|
def _compile_with_coverage(src_path: Path, out_dir: Path) -> bool:
|
|
"""用 cobc --coverage 编译, 返回是否成功"""
|
|
r = subprocess.run(
|
|
["cobc", "-x", "--coverage", str(src_path), "-o", str(out_dir / "test-gcov.exe")],
|
|
capture_output=True, text=True, timeout=30,
|
|
cwd=str(out_dir),
|
|
)
|
|
if r.returncode != 0:
|
|
print(f"[compile] stderr: {r.stderr[:300]}")
|
|
return r.returncode == 0
|
|
|
|
|
|
def _run_executable(exe_path: Path, run_dir: Path) -> bool:
|
|
"""运行可执行文件, 返回是否成功"""
|
|
r = subprocess.run(
|
|
[str(exe_path)],
|
|
capture_output=True, text=True, timeout=15,
|
|
cwd=str(run_dir),
|
|
)
|
|
if r.returncode != 0:
|
|
print(f"[run] stderr: {r.stderr[:300]}")
|
|
print(f"[run] stdout: {r.stdout.strip()}")
|
|
return r.returncode == 0
|
|
|
|
|
|
# ── 测试用例 ──
|
|
|
|
|
|
@pytest.mark.skipif(not _check_cobc(), reason="cobc 未安装或不在 PATH 中")
|
|
def test_gcov_basic_collect(work_dir: Path) -> None:
|
|
"""全链路: 编译 → 运行 → collect_gcov → 验证 line_rate"""
|
|
|
|
# 1. 写入 COBOL 源文件
|
|
src = work_dir / "test-gcov.cbl"
|
|
src.write_text(SAMPLE_COBOL, encoding="utf-8")
|
|
|
|
# 2. 编译 (--coverage)
|
|
assert _compile_with_coverage(src, work_dir), "cobc --coverage 编译失败"
|
|
|
|
# 3. 确认 .gcno 已生成
|
|
gcno_files = list(work_dir.glob("*.gcno"))
|
|
assert len(gcno_files) > 0, "编译后未生成 .gcno 文件"
|
|
|
|
# 4. 运行程序 (生成 .gcda)
|
|
exe = work_dir / "test-gcov.exe"
|
|
assert _run_executable(exe, work_dir), "程序运行失败"
|
|
|
|
# 5. 确认 .gcda 已生成
|
|
gcda_files = list(work_dir.glob("*.gcda"))
|
|
assert len(gcda_files) > 0, "运行后未生成 .gcda 文件"
|
|
|
|
# 6. 调用 collect_gcov() 采集覆盖率
|
|
result = collect_gcov(cobol_src=src, work_dir=work_dir)
|
|
print(f"[gcov] collect_gcov returned: {result}")
|
|
|
|
# 7. 验证结果
|
|
assert result["available"] is True, f"覆盖率采集失败: {result.get('reason', 'unknown')}"
|
|
assert result["line_rate"] > 0, f"line_rate 应为正值, 实际: {result['line_rate']}"
|
|
assert result["total_lines"] > 0, f"total_lines 应为正值, 实际: {result['total_lines']}"
|
|
assert result["executed_lines"] > 0, f"executed_lines 应为正值, 实际: {result['executed_lines']}"
|
|
|
|
# 8. 验证分支覆盖 (IF 的两路应至少覆盖了一路)
|
|
assert result["line_rate"] <= 1.0, f"line_rate 不应超过 1.0"
|
|
print(f"[gcov] ✅ line_rate={result['line_rate']} ({result['executed_lines']}/{result['total_lines']})")
|
|
|
|
|
|
@pytest.mark.skipif(not _check_cobc(), reason="cobc 未安装或不在 PATH 中")
|
|
def test_gcov_no_gcda_graceful(work_dir: Path) -> None:
|
|
"""无 .gcda 文件时 collect_gcov 应优雅降级"""
|
|
|
|
src = work_dir / "test-gcov.cbl"
|
|
src.write_text(SAMPLE_COBOL, encoding="utf-8")
|
|
|
|
# 编译但不运行, 所以没有 .gcda
|
|
subprocess.run(
|
|
["cobc", "-x", "--coverage", str(src), "-o", str(work_dir / "test-gcov.exe")],
|
|
capture_output=True, text=True, timeout=30,
|
|
cwd=str(work_dir),
|
|
)
|
|
|
|
result = collect_gcov(cobol_src=src, work_dir=work_dir)
|
|
# 没有 .gcda 时应 graceful 返回 {available: False}
|
|
assert result["available"] is False
|
|
print(f"[gcov] 无 .gcda 降级正常: {result}")
|