feat: 多轮运行 + GCOV 合并 + JSON 出力 + DesignDataGenerator

This commit is contained in:
hangshuo652
2026-07-12 21:04:58 +08:00
parent af37e33b98
commit f3be17e5eb
40 changed files with 4397 additions and 198 deletions
+101 -53
View File
@@ -351,9 +351,16 @@ def run_all(program_name: str, outdir: str, temp_dir: str,
expected_records: list[dict] | None = None,
source_dir: str | None = None,
path_infos: list | None = None,
multi_write_fds: set | None = None
) -> list[GroupResult]:
"""完整编排:编译 → 准备目录 → 逐组执行 → 出力保存。"""
multi_write_fds: set | None = None,
skip_records: list[dict] | None = None,
skip_term_types: list[str] | None = None
) -> tuple[list[GroupResult], dict[int, int] | None]:
"""完整编排:编译 → 准备目录 → 逐组执行 → 出力保存。
Returns:
(results_list, merged_gcov_data)
merged_gcov_data is None when no gcov runs.
"""
source_dir = source_dir or str(Path(outdir).parent)
work_dir = Path(temp_dir)
work_dir.mkdir(parents=True, exist_ok=True)
@@ -362,6 +369,11 @@ def run_all(program_name: str, outdir: str, temp_dir: str,
multi_write_fds = multi_write_fds or set()
fd_field_dicts = _build_fd_field_dicts(fd_fields, fields_dict)
assign_names = _input_assign_names(select_info, open_dir, fd_fields)
def _is_output_fd(fd_name: str) -> bool:
dir_val = open_dir.get(fd_name, '')
return dir_val in ('OUTPUT', 'I-O')
# ── 1. SUB 编译(V3)──
sub_dir = _resolve_sub_dir(source_dir)
@@ -373,63 +385,99 @@ def run_all(program_name: str, outdir: str, temp_dir: str,
program_name, source_dir, str(work_dir), sub_o, cpy_dir
)
# ── 3. 入力ファイル配置(V3──
input_dir = Path(outdir) / 'input'
assign_names = _input_assign_names(select_info, open_dir, fd_fields)
if input_dir.is_dir():
for assign in assign_names:
src = input_dir / assign
if src.exists():
shutil.copy2(str(src), str(work_dir / assign))
logger.info(f" INPUT: {assign} ({src.stat().st_size} bytes)")
# ── 3. 场景定义 ──
scenes = [("main", records, term_types, expected,
Path(outdir) / 'input', Path(outdir) / 'output')]
if skip_records:
skip_expected = [{}] * len(skip_records)
skip_term = skip_term_types or ['normal'] * len(skip_records)
scenes.append(("skip", skip_records, skip_term, skip_expected,
Path(outdir) / 'input_skip', Path(outdir) / 'run_skip' / 'output'))
# ── 4. 清理旧 gcda + 执行 ──
_clean_gcda(str(work_dir))
results = []
gcov_data_sets = []
gcov_root = work_dir / "gcov"
def _is_output_fd(fd_name: str) -> bool:
dir_val = open_dir.get(fd_name, '')
return dir_val in ('OUTPUT', 'I-O')
for scene_id, scene_recs, scene_terms, scene_expected, src_in_dir, dst_out_dir in scenes:
# ── 3a. 入力ファイル配置 ──
if src_in_dir.is_dir():
for assign in assign_names:
src = src_in_dir / assign
if src.exists():
shutil.copy2(str(src), str(work_dir / assign))
# output_input_files 只写入非 abend 记录,同步过滤 expected
filtered_expected = []
for i, rec in enumerate(expected):
term = term_types[i] if i < len(term_types) else 'normal'
if term != 'abend':
filtered_expected.append(rec)
expected = filtered_expected
# ── 3b. 清理旧 gcda ──
_clean_gcda(str(work_dir))
group = GroupInfo(
name=program_name,
records=records,
expected_outputs=expected,
expected_returncode=0,
fd_field_dicts=fd_field_dicts,
open_dir=open_dir,
select_info=select_info,
multi_write_fds=multi_write_fds,
)
# ── 3c. 过滤 non-abend ──
filtered_exp = []
for i, rec in enumerate(scene_expected):
term = scene_terms[i] if i < len(scene_terms) else 'normal'
if term != 'abend':
filtered_exp.append(rec)
r = run_group(group, exe_path, str(work_dir))
results = [r]
group = GroupInfo(
name=f"{program_name}_{scene_id}",
records=scene_recs,
expected_outputs=filtered_exp,
expected_returncode=0,
fd_field_dicts=fd_field_dicts,
open_dir=open_dir,
select_info=select_info,
multi_write_fds=multi_write_fds,
)
# 出力拷贝到 output_dir
output_dir = Path(outdir) / 'output'
output_dir.mkdir(parents=True, exist_ok=True)
for fd_name in fd_field_dicts:
if not _is_output_fd(fd_name):
continue
sel = select_info.get(fd_name, {})
assign = sel.get('assign', fd_name) if isinstance(sel, dict) else fd_name
src = os.path.join(str(work_dir), assign)
dst = output_dir / assign
if os.path.exists(src):
shutil.copy2(src, str(dst))
# ── 3d. 执行 ──
r = run_group(group, exe_path, str(work_dir))
results.append(r)
status = '' if r.passed else ''
logger.info(f"'{group.name}': returncode={r.returncode}, "
f"{len(r.details)} fields, {status}")
logger.info(f" EXIT={r.returncode}, 出力パス={work_dir}")
return results
status = '' if r.passed else ''
logger.info(f"'{group.name}': returncode={r.returncode}, {status}")
# ── 3e. 出力保存 ──
dst_out_dir.mkdir(parents=True, exist_ok=True)
for fd_name in fd_field_dicts:
if not _is_output_fd(fd_name):
continue
sel = select_info.get(fd_name, {})
assign = sel.get('assign', fd_name) if isinstance(sel, dict) else fd_name
src = os.path.join(str(work_dir), assign)
if os.path.exists(src):
shutil.copy2(src, str(dst_out_dir / assign))
# ── 3f. .gcda 隔离(.gcno 是共享的,COPY.gcda 是每场景独立的,MOVE)
scene_gcov_dir = gcov_root / f"run_{scene_id}"
scene_gcov_dir.mkdir(parents=True, exist_ok=True)
for f in work_dir.glob("*.gcda"):
if f.is_file() and f.stat().st_size > 0:
dst = scene_gcov_dir / f.name
if dst.exists():
dst.unlink()
shutil.move(str(f), str(dst))
for f in work_dir.glob("*.gcno"):
if f.is_file() and f.stat().st_size > 0:
dst = scene_gcov_dir / f.name
if not dst.exists():
shutil.copy2(str(f), str(dst))
# ── 3g. 收集该场景的 gcov 数据 ──
from .gcov import run_gcov as _run_gcov
scene_data = _run_gcov(program_name, str(scene_gcov_dir))
if scene_data:
gcov_data_sets.append(scene_data)
logger.info(f" {scene_id} 完了, output={dst_out_dir}")
# ── 4. 合并 gcov ──
merged_gcov = None
if gcov_data_sets:
merged_gcov = {}
for ds in gcov_data_sets:
for line, count in ds.items():
merged_gcov[line] = max(merged_gcov.get(line, 0), count)
logger.info(f" Merged gcov from {len(gcov_data_sets)} runs ({len(merged_gcov)} lines)")
return results, merged_gcov
# ── run_and_compare(被 --run 调用,SOURCE 兼容)──