Files
cobol-java-v3/tests/cobol_testgen/test_kin07_coordinate.py
T

217 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""KIN07DAI 型多文件照合程序的前缀检测与前置序列构造测试。
复现根因:_coordinate_multi_file_keys 从 01 记录名推导前缀(R01INNREC→'R01'),
但 COPY REPLACING 把字段前缀改为 SW/SR/SL,导致键对匹配不到、跨文件键协调失效。
修复后应生成前置匹配序列,运行时 merge-join 应覆盖全匹配/单明细匹配等组合。
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
import pytest
from cobol_testgen.__init__ import (
_coordinate_multi_file_keys,
_coordinate_tertiary_fd,
_find_key_pairs_inner,
)
def _group(name):
return {'name': name, 'level': 1, 'pic': None,
'pic_info': {'type': 'group', 'digits': 0, 'decimal': 0, 'length': 80, 'signed': False},
'section': 'FILE', 'is_filler': False, 'redefines': None, 'usage': 'DISPLAY',
'occurs': 0, 'occurs_depending': None, 'value': None, 'values': None}
def _field(name, level, pic, ftype, length):
return {'name': name, 'level': level, 'pic': pic,
'pic_info': {'type': ftype, 'digits': 0, 'decimal': 0, 'length': length, 'signed': False},
'section': 'FILE', 'is_filler': False, 'redefines': None, 'usage': 'DISPLAY',
'occurs': 0, 'occurs_depending': None, 'value': None, 'values': None}
def _make_data_fields():
# 字段顺序与源文件一致:01 记录紧跟其子字段
return [
_group('R01INNREC'),
_field('SWEMP-ID', 3, 'X(008)', 'alphanumeric', 8),
_field('SWDATE', 3, '9(008)', 'numeric', 8),
_group('R02INNREC'),
_field('SREMP-ID', 3, 'X(008)', 'alphanumeric', 8),
_field('SRWORK-DATE', 3, '9(008)', 'numeric', 8),
_field('SRLEAVE-TYPE', 3, 'X(002)', 'alphanumeric', 2),
_group('R03INNREC'),
_field('SLEMP-ID', 3, 'X(008)', 'alphanumeric', 8),
_field('SLDATE', 3, '9(008)', 'numeric', 8),
_field('SLLEAVE-TYPE', 3, 'X(002)', 'alphanumeric', 2),
_field('SLSTART-TIME', 3, '9(004)', 'numeric', 4),
_field('SLEND-TIME', 3, '9(004)', 'numeric', 4),
]
def _make_records(n=12):
recs = []
for i in range(n):
recs.append({
'SWEMP-ID': f'R{i:07d}', 'SWDATE': f'{20000101 + i:08d}',
'SREMP-ID': f'C{i:07d}', 'SRWORK-DATE': f'{20000101 + i:08d}',
'SRLEAVE-TYPE': '99',
'SLEMP-ID': f'I{i:07d}', 'SLDATE': f'{20000101 + i:08d}',
'SLLEAVE-TYPE': '99',
'SLSTART-TIME': '0822', 'SLEND-TIME': '0922',
})
return recs
FILE_SEC = {
'R01INNFIL': ['R01INNREC'],
'R02INNFIL': ['R02INNREC'],
'R03INNFIL': ['R03INNREC'],
'W01OUTFIL': ['W01OUTREC'],
}
OPEN_DIR = {
'R01INNFIL': 'INPUT',
'R02INNFIL': 'INPUT',
'R03INNFIL': 'INPUT',
'W01OUTFIL': 'OUTPUT',
}
def test_find_key_pairs_uses_field_prefix_not_record_prefix():
"""字段前缀 SW/SR/SL 应能配对。"""
fields = _make_data_fields()
assert _find_key_pairs_inner('SW', 'SR', fields), 'SW-SR 应配对'
assert _find_key_pairs_inner('SW', 'SL', fields), 'SW-SL 应配对'
def test_coordinate_multi_file_keys_builds_front_sequence():
"""KIN07DAI 形态(记录名 R01INNREC + 字段 SW/SR/SL)下协调应生效:
记录0 三文件键一致、R03 同键重复块 + 休暇种别注入、键单调递增。"""
fields = _make_data_fields()
records = _make_records(12)
_coordinate_multi_file_keys(
records, [], fields, {}, FILE_SEC,
term_types=['normal'] * len(records), open_dir=OPEN_DIR,
)
r0 = records[0]
assert r0['SWEMP-ID'] == r0['SREMP-ID'] == r0['SLEMP-ID'], '记录0 三文件键应一致'
assert r0['SWDATE'] == r0['SRWORK-DATE'] == r0['SLDATE'], '记录0 三文件日期应一致'
# R03 同键重复块(休暇种别 01-04 + WHEN OTHER '99'
r03_keys = [records[i]['SLEMP-ID'] for i in range(5)]
assert all(k == r03_keys[0] for k in r03_keys), 'R03 前5条记录键应一致'
lts = [records[i]['SLLEAVE-TYPE'] for i in range(5)]
assert lts == ['01', '02', '03', '04', '99'], "休暇种别应注入 01-04+'99', 实际 {lts}".format(lts=lts)
# 键单调递增(避免 merge-join 指针跳过)
k0, k1 = records[0]['SWEMP-ID'], records[1]['SWEMP-ID']
assert k0 < k1, f'前置序列主键应递增: {k0} < {k1}'
def _simulate_merge(R01, R02, R03):
"""模拟 KIN07DAI 的 3 文件 merge-join,返回 (pattern_counts, match_flags)。
pattern 集合: A=全匹配, B=仅R02, C=仅R03, D=无匹配
match_flags: (r02_match, r03_match, leave_calc, lt_seen)
"""
def key(e, d):
return (str(e).strip(), str(d))
n1, n2, n3 = len(R01), len(R02), len(R03)
r1 = r2 = r3 = 0
e1 = e2 = e3 = False
def rdR1():
nonlocal r1, e1
if r1 < n1:
r1 += 1
else:
e1 = True
def rdR2():
nonlocal r2, e2
if r2 < n2:
r2 += 1
else:
e2 = True
def rdR3():
nonlocal r3, e3
if r3 < n3:
r3 += 1
else:
e3 = True
rdR1(); rdR2(); rdR3()
pat = {'A': 0, 'B': 0, 'C': 0, 'D': 0}
r02m = r03m = calcs = 0
lt_seen = set()
while not e1:
r01k = key(R01[r1 - 1][0], R01[r1 - 1][1])
ptn = 4
while not e2:
if key(R02[r2 - 1][0], R02[r2 - 1][1]) >= r01k:
break
rdR2()
if not e2 and key(R02[r2 - 1][0], R02[r2 - 1][1]) == r01k:
ptn = 2
r02m += 1
rdR2()
while not e3:
if key(R03[r3 - 1][0], R03[r3 - 1][1]) >= r01k:
break
rdR3()
if not e3 and key(R03[r3 - 1][0], R03[r3 - 1][1]) == r01k:
r03m += 1
while True:
calcs += 1
lt_seen.add(R03[r3 - 1][3])
rdR3()
if e3:
break
if key(R03[r3 - 1][0], R03[r3 - 1][1]) != r01k:
break
ptn = 1 if ptn == 2 else 3
pat[['A', 'B', 'C', 'D'][ptn - 1]] += 1
rdR1()
return pat, (r02m, r03m, calcs, lt_seen)
def test_coordination_reaches_all_match_combinations_at_runtime():
"""协调后运行时 merge-join 应覆盖全匹配/仅R02/仅R03 三种组合。"""
fields = _make_data_fields()
records = _make_records(12)
_coordinate_multi_file_keys(
records, [], fields, {}, FILE_SEC,
term_types=['normal'] * len(records), open_dir=OPEN_DIR,
)
R01 = [(r['SWEMP-ID'], r['SWDATE']) for r in records]
R02 = [(r['SREMP-ID'], r['SRWORK-DATE']) for r in records]
R03 = [(r['SLEMP-ID'], r['SLDATE'], r['SLLEAVE-TYPE'], r['SLLEAVE-TYPE']) for r in records]
pat, (r02m, r03m, calcs, lt_seen) = _simulate_merge(R01, R02, R03)
assert r02m >= 1, 'R02 照合应至少发生 1 次'
assert r03m >= 1, 'R03 照合应至少发生 1 次'
assert calcs >= 4, '休暇計算应至少 4 次(EVALUATE 全 WHEN'
assert lt_seen >= {'01', '02', '03', '04'}, f'休暇種別应全覆盖, 实际 {lt_seen}'
assert pat['A'] >= 1, f'全匹配(PATTERN-A)应覆盖, 实际 {pat}'
assert pat['B'] >= 1, f'仅R02匹配(PATTERN-B)应覆盖, 实际 {pat}'
assert pat['C'] >= 1, f'仅R03匹配(PATTERN-C)应覆盖, 实际 {pat}'
def test_coordinate_tertiary_fd_long_leave_for_cap():
"""PATTERN-C 用判别块应注入长休暇时长(覆盖 8h 上限 T 分支)。"""
fields = _make_data_fields()
records = _make_records(12)
_coordinate_tertiary_fd(records, ['SW', 'SR', 'SL'], fields)
# 块2(索引 max(3,5)=5..9):长时长 0600-2200
for i in range(5, 10):
assert records[i]['SLSTART-TIME'] == '0600', f'rec{i} 长休暇开始时间'
assert records[i]['SLEND-TIME'] == '2200', f'rec{i} 长休暇结束时间'