Files
cobol-java-v3/cobol_testgen/blackbox.py
T

139 lines
4.3 KiB
Python

"""黑盒(black-box-data-create)测试数据接入:JSON → cobol_testgen 内部 record。
黑盒 JSON 结构(每组一个文件):
{ "program": "ZAN04MAT",
"records": [ { "input": { "<FD>": { "<字段>": "<值>" } } } ] }
FD 键与字段名由 LLM 生成,存在多种写法(FD名 / DD名 / 识别子,字段带或不带 '-'),
因此这里做归一化容错匹配。
"""
import json
import logging
from dataclasses import dataclass, field
from pathlib import Path
logger = logging.getLogger(__name__)
@dataclass
class BlackBoxGroup:
label: str
records: list[dict]
term_types: list[str] = field(default_factory=list)
source_path: str = ''
def norm_name(name) -> str:
"""字段/FD 名归一化:大写 + 去除 '-' '_' 分隔符。"""
return str(name).upper().replace('-', '').replace('_', '')
def _fd_aliases(fd_name: str, select_info: dict) -> set:
aliases = {norm_name(fd_name)}
sel = select_info.get(fd_name, {})
if isinstance(sel, dict):
assign = sel.get('assign')
if assign:
aliases.add(norm_name(assign))
return aliases
def _resolve_fd(bb_key, fd_aliases: dict):
"""bb_key → 内部 fd_name;先精确别名,再前缀容错(R01 → R01INNFIL)。"""
nk = norm_name(bb_key)
for fd_name, aliases in fd_aliases.items():
if nk in aliases:
return fd_name
for fd_name, aliases in fd_aliases.items():
for a in aliases:
if a and (nk.startswith(a) or a.startswith(nk)):
return fd_name
return None
def _field_alias_map(data_fields: list[dict]) -> dict:
m = {}
for f in data_fields or []:
if f.get('is_88'):
continue
m.setdefault(norm_name(f['name']), f['name'])
return m
def _iter_group_files(path: Path) -> list[Path]:
p = Path(path)
if p.is_file() and p.suffix.lower() == '.json':
return [p]
if not p.is_dir():
return []
files = sorted(p.glob('*_g*.json'))
if files:
return files
bb = p / 'black_box'
if bb.is_dir():
return sorted(bb.rglob('*_g*.json'))
return []
def _group_label(json_path: Path) -> str:
for token in json_path.stem.split('_'):
if token.lower().startswith('g') and token[1:].isdigit():
return token
return json_path.stem
def _map_record(raw, fd_aliases, field_map, jf: Path) -> dict:
inp = raw.get('input')
if not isinstance(inp, dict):
return {}
rec = {}
for bb_fd, fields in inp.items():
fd_name = _resolve_fd(bb_fd, fd_aliases)
if fd_name is None:
logger.debug(f"黑盒 FD 未匹配: {bb_fd} ({jf.name})")
continue
if not isinstance(fields, dict):
continue
for bb_field, val in fields.items():
internal = field_map.get(norm_name(bb_field))
if internal is None:
logger.debug(f"黑盒字段未匹配: {bb_field} ({jf.name})")
continue
rec[internal] = '' if val is None else str(val)
return rec
def load_black_box_groups(path, fd_fields: dict, select_info: dict,
data_fields: list[dict]) -> list[BlackBoxGroup]:
"""读取黑盒 JSON,返回分组列表。
path 可以是:程序目录(自动找 black_box/)、black_box 目录、或单个 JSON 文件。
"""
fd_aliases = {fd: _fd_aliases(fd, select_info) for fd in fd_fields}
field_map = _field_alias_map(data_fields)
groups = []
for jf in _iter_group_files(Path(path)):
try:
obj = json.loads(jf.read_text(encoding='utf-8'))
except Exception as e: # noqa: BLE001
logger.warning(f"黑盒 JSON 解析失败 {jf}: {e}")
continue
label = _group_label(jf)
records = []
for raw in obj.get('records', []) or []:
rec = _map_record(raw, fd_aliases, field_map, jf)
if rec:
records.append(rec)
if not records:
logger.warning(f"黑盒组 {label} 无有效记录,跳过 ({jf.name})")
continue
groups.append(BlackBoxGroup(
label=label,
records=records,
term_types=['normal'] * len(records),
source_path=str(jf),
))
logger.info(f" 黑盒组 {label}: {len(records)} 条记录 ({jf.name})")
return groups