feat: 多轮运行 + GCOV 合并 + JSON 出力 + DesignDataGenerator
This commit is contained in:
+88
-15
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user