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
+88 -15
View File
@@ -125,6 +125,10 @@ def _format_value(value: Any, field: dict) -> bytes:
val = str(value) if value is not None else ""
if ftype == "numeric":
sval = str(val) if val is not None else ""
# Preserve spaces for DP#7 (R01EMP-ID = SPACE) and similar COBOL checks
if sval and all(c == ' ' for c in sval):
return (' ' * length).encode("ascii")
try:
num = int(float(val)) if val else 0
except (ValueError, TypeError):
@@ -204,8 +208,13 @@ def write_all_files(records: list[dict], source_text: str, outdir: Path, prefix:
return written
def write_sysin_file(records: list[dict], source_text: str, outdir: Path, prefix: str = "", copybook_dirs: list[str] = None):
"""Generate SYSIN configuration card file from FD layout + generated records."""
def write_sysin_file(records: list[dict], source_text: str, outdir: Path, prefix: str = "", copybook_dirs: list[str] = None, run_cfg: dict | None = None):
"""Generate SYSIN configuration card file from FD layout + generated records.
Args:
run_cfg: Scenario sysin override dict, e.g. {"period": "202607", "include_invalid_period": True, "modes": ["NORMAL", "RESET"]}.
If None or empty, uses defaults (existing behavior).
"""
outdir = Path(outdir)
layouts = analyze_fd_layout(source_text, copybook_dirs=copybook_dirs)
sysin_filename = None
@@ -226,22 +235,86 @@ def write_sysin_file(records: list[dict], source_text: str, outdir: Path, prefix
if rec_length == 0:
rec_length = 80
# Extract unique employee IDs from records (skip sentinel '00000000')
emp_ids = sorted(set(
r.get("R01EMP-ID", "") for r in records
if r.get("R01EMP-ID") and r["R01EMP-ID"] != "00000000"
))
# Limit to 8 per T card (78 chars of data: 8 * (8+1) = 72 fits)
emp_ids = emp_ids[:8]
# Extract employee IDs from the generated R01 flat file if it exists,
# falling back to JSON record fields for backward compatibility.
r01_path = outdir / "KIN08R01"
emp_ids_ordered = []
emp_set = set()
if r01_path.exists():
rec_size = 200
data = r01_path.read_bytes()
num_recs = len(data) // rec_size
for i in range(num_recs):
off = i * rec_size
eid = data[off:off+8].decode("ascii", errors="replace").strip()
if eid and eid != "00000000":
emp_ids_ordered.append(eid)
else:
for r in records:
eid = r.get("R01EMP-ID", "") or r.get("R01INNREC", {}).get("R01EMP-ID", "")
if not eid or eid == "00000000":
line = r.get("R01LINE", "")
if line:
eid = line.split(",")[0].strip()
if eid and eid != "00000000":
emp_ids_ordered.append(eid)
if eid not in emp_set:
emp_set.add(eid)
else:
emp_ids_ordered.append(eid)
# Build SYSIN card records
# Card format: position 1 = type, position 2 = space (ignored), position 3+ = data
lines = [
f"P YEAR-MONTH=202607", # Period card
f"M MODE=NORMAL", # Mode card
]
if emp_ids:
lines.append(f"T {','.join(emp_ids)}") # Target card
if run_cfg is None:
run_cfg = {}
lines = ["* GENERATED TEST DATA"]
sysin = run_cfg
period = sysin.get("period", "202607")
if period is not None:
lines.append(f"P YEAR-MONTH={period}")
if sysin.get("include_invalid_period", True):
lines.append("P YEAR-MONTH=000000")
for mode in (sysin.get("modes") or ["NORMAL"]):
lines.append(f"M MODE={mode}")
# Detect duplicate EMP_IDs → need RESET mode
seen = set()
has_dups = False
for eid in emp_ids_ordered:
if eid in seen:
has_dups = True
break
seen.add(eid)
if has_dups:
lines.append("M MODE=RESET")
chunks = [emp_ids_ordered[i:i+8] for i in range(0, len(emp_ids_ordered), 8)]
for chunk in chunks:
lines.append(f"T {','.join(chunk)}")
lines.append("M MODE=NORMAL")
# Always add a RESET-mode batch with duplicate EMP-ID for UPDATE path
unique_ids = list(dict.fromkeys(emp_ids_ordered))
if not unique_ids:
unique_ids = ["EMP00001", "EMP00002", "EMP00003", "EMP00004", "EMP00005"]
# Inject duplicates: use first EMP-ID twice to trigger RESET/UPDATE
if len(unique_ids) >= 1:
dup_id = unique_ids[0]
reset_ids = [dup_id, dup_id]
lines.append("M MODE=RESET")
lines.append(f"T {','.join(reset_ids)}")
lines.append("M MODE=NORMAL")
# Unique T card for NORMAL mode
if unique_ids:
chunks = [unique_ids[i:i+8] for i in range(0, len(unique_ids), 8)]
for chunk in chunks:
lines.append(f"T {','.join(chunk)}")
# Unknown card type to cover IF WRK-CARD-TYPE '*' ELSE branch (DP#8 F)
lines.append("X UNKNOWN")
# End with RESET mode so 3000STPSOR runs the RESET path (DP#22-#23)
lines.append("M MODE=RESET")
# Write as fixed-length flat file
outpath = outdir / (prefix + sysin_filename)