"""决策点行号定位测试(T4 修复)。 注入 SQLCODE 映射代码后,gixpp 输出的 pp.cbl 中 `IF SQLCODE = -803` 等语句行号发生偏移。`locate_decision_lines` 必须精确定位到 `IF SQLCODE = -803` 所在行,而非 `IF SQLCODE = -1555` 或映射块的 END-IF。 """ import sys, os, re sys.stdout.reconfigure(encoding='utf-8') sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) from cobol_testgen.coverage import _build_search_patterns, _normalize, locate_decision_lines class D: kind = 'IF' label = 'IF SQLCODE = -803' condition = None def test_if_pattern_does_not_duplicate_if_keyword(): """IF 决策点模式不能是 'IF IF ...'(label 已含 IF)""" dp = D() pats = _build_search_patterns(dp) assert pats, "patterns should not be empty" for p in pats: assert "IF\\b\\s+IF" not in p, f"duplicated IF keyword in pattern: {p}" def test_if_pattern_matches_exact_condition_line(): """模式应匹配 'IF SQLCODE = -803' 行,而不匹配 -1555/-19""" dp = D() pats = _build_search_patterns(dp) assert pats[0] is not None assert re.search(pats[0], "IF SQLCODE = -803") is not None assert re.search(pats[0], "IF SQLCODE = -1555") is None assert re.search(pats[0], "IF SQLCODE = -19") is None assert re.search(pats[0], "IF SQLCODE = -2067") is None def test_locate_finds_shifted_line_in_pp_source(): """pp.cbl 中注入映射后,决策点定位到正确的 -803 行""" pp = ( " CALL \"GIXSQLEndSQL\"\n" " END-CALL.\n" " IF SQLCODE = -1555\n" " MOVE -803 TO SQLCODE\n" " END-IF\n" " IF SQLCODE = -2067\n" " MOVE -803 TO SQLCODE\n" " END-IF\n" " IF SQLCODE = -19\n" " MOVE -803 TO SQLCODE\n" " END-IF\n" " IF SQLCODE = 0\n" " ADD 1 TO CUN-DB-INS\n" " END-IF.\n" " IF SQLCODE = -803\n" " PERFORM 2000MAJSOR-UPD\n" " END-IF.\n" ) dp = D() dp.source_line = 0 locate_decision_lines([dp], pp) assert dp.source_line == 15, f"expected line 15 (IF SQLCODE = -803), got {dp.source_line}" class MultiLineD: kind = 'IF' label = 'MERGE-REC-TYPE = CNS-PAY-TYPE-SALARY' condition = None def test_locate_multiline_if_condition(): """条件被换行拆分的 IF 应定位到 'IF <首字段>' 起始行。 MERGE 输出过程的 IF MERGE-REC-TYPE / = CNS-PAY-TYPE-SALARY 分两行, 单行模式无法匹配;应回退到按首字段定位 IF 行(gcov 分支标记依赖源行)。 """ pp = ( " PERFORM WITH TEST AFTER\n" " UNTIL WRK-MERGE-EOF-Y\n" " RETURN MERGE-FILE\n" " INTO MERGE-REC\n" " AT END\n" " SET WRK-MERGE-EOF-Y TO TRUE\n" " END-RETURN\n" " IF NOT WRK-MERGE-EOF-Y\n" " MOVE MERGE-EMP-ID\n" " TO WRK-CURRENT-EMP\n" " IF MERGE-REC-TYPE\n" " = CNS-PAY-TYPE-SALARY\n" " MOVE MERGE-PAY-AMOUNT\n" " TO WRK-SALARY-AMOUNT\n" " ELSE\n" " MOVE MERGE-PAY-AMOUNT\n" " TO WRK-BONUS-AMOUNT\n" " END-IF\n" " END-IF\n" " END-PERFORM.\n" ) dp = MultiLineD() dp.source_line = 0 locate_decision_lines([dp], pp) assert dp.source_line == 11, f"expected line 11 (IF MERGE-REC-TYPE), got {dp.source_line}" def test_locate_multiline_if_does_not_mislocate_other_if(): """首字段定位不应误命中其它字段相同的 IF(如 IF NOT WRK-MERGE-EOF-Y 前的 WRK 前缀)。""" pp = ( " IF NOT WRK-MERGE-EOF-Y\n" " IF MERGE-REC-TYPE\n" " = CNS-PAY-TYPE-SALARY\n" " MOVE MERGE-PAY-AMOUNT\n" " TO WRK-SALARY-AMOUNT\n" " END-IF\n" " END-IF.\n" ) dp = MultiLineD() dp.source_line = 0 locate_decision_lines([dp], pp) assert dp.source_line == 2, f"expected line 2 (IF MERGE-REC-TYPE), got {dp.source_line}"