Files
cobol-java-v3/tests/cobol_testgen/test_cond.py
hangshuo652 bc1d56d1a4 feat: Phase 2 complete — 13 Phases of COBOL type classification and test benchmark
P0.6: gcov infrastructure
P1: extract_structure output expansion (11 new feature fields)
P2: Confusion group rule engine (8 pairs + contradiction + backtrack)
P3: 4-factor confidence calculation + quality gate update
P4: 33+2 COBOL program type test samples (22 files, 7 categories)
P5: parametrized/ test data generation engine
P6: japanese_data.py lookup tables
P7-10: Type-specific test suites (~159 parametrized tests)
P11: Full classification pipeline (classify_program) + orchestrator integration
P12: Documentation (module-interfaces, test-plan v3.0, coverage-matrix)

Architecture decisions:
- classification_pipeline/ merged to hina/pipeline/
- parametrized/ as independent module
- japanese_data.py as root-level file
- hina/__all__ only exports classify_program()

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-19 23:51:55 +08:00

242 lines
6.9 KiB
Python

"""CO-01~10: cobol_testgen cond 模块 — 条件表达式解析 + MC/DC"""
import sys, os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from cobol_testgen.cond import (
parse_single_condition, parse_compound_condition,
collect_leaves, evaluate_tree, mcdc_sets, is_field,
)
from cobol_testgen.models import CondLeaf, CondAnd, CondOr, CondNot
# ── CO-01~02: parse_single_condition ──
def test_parse_single_numeric():
"""CO-01: 数值比较 AMOUNT > 100"""
r = parse_single_condition("AMOUNT > 100")
assert r is not None
assert r[0] == "AMOUNT"
assert r[1] == ">"
assert r[2] == "100"
def test_parse_single_string():
"""CO-02: 文字列比较 B = 'Y'"""
r = parse_single_condition("B = 'Y'")
assert r is not None
assert r[0] == "B"
assert r[1] == "="
assert r[2] == "Y"
def test_parse_single_subscript():
"""带下标的字段 WS-ITEM(SUB) = 'A'"""
r = parse_single_condition("WS-ITEM(SUB) = 'A'")
assert r is not None
assert r[2] == "A"
def test_parse_single_88_level():
"""88-level 条件名分解"""
fields = [{"is_88": True, "name": "STATUS-APPROVED", "parent": "WS-TRAN-STATUS", "value": "A"}]
r = parse_single_condition("STATUS-APPROVED", fields)
assert r is not None
assert r[0] == "WS-TRAN-STATUS"
assert r[2] == "A"
def test_parse_single_compound_returns_none():
"""包含 AND/OR 返回 None"""
assert parse_single_condition("A > 0 AND B < 5") is None
def test_parse_single_unknown_returns_none():
"""无法解析的表达式返回 None"""
assert parse_single_condition("NOT A") is None
# ── CO-03~05: parse_compound_condition ──
def test_compound_and():
"""CO-03: A > 0 AND B < 5 → CondAnd"""
r = parse_compound_condition("A > 0 AND B < 5")
assert r is not None
assert isinstance(r, CondAnd)
assert isinstance(r.left, CondLeaf)
assert isinstance(r.right, CondLeaf)
def test_compound_or():
"""CO-04: A = 1 OR B = 2 → CondOr"""
r = parse_compound_condition("A = 1 OR B = 2")
assert r is not None
assert isinstance(r, CondOr)
assert isinstance(r.left, CondLeaf)
assert isinstance(r.right, CondLeaf)
def test_compound_nested_and_or():
"""CO-05: (A > 0 AND B < 5) OR C = 1 → AND优先于OR"""
r = parse_compound_condition("(A > 0 AND B < 5) OR C = 1")
assert r is not None
assert isinstance(r, CondOr)
assert isinstance(r.left, CondAnd)
assert isinstance(r.right, CondLeaf)
def test_compound_not():
"""NOT 前缀"""
r = parse_compound_condition("NOT A = 1")
assert r is not None
assert isinstance(r, CondNot)
assert isinstance(r.child, CondLeaf)
def test_compound_empty():
"""空字符串返回 None"""
assert parse_compound_condition("") is None
def test_compound_paren_wrap():
"""外层括号剥离"""
r = parse_compound_condition("(A > 0)")
assert isinstance(r, CondLeaf)
# ── collect_leaves ──
def test_collect_leaves_and():
"""AND 树收集所有叶子"""
tree = CondAnd(CondLeaf("A", ">", "0"), CondLeaf("B", "<", "5"))
leaves = collect_leaves(tree)
assert len(leaves) == 2
def test_collect_leaves_not():
"""NOT 树收集子叶子"""
tree = CondNot(CondLeaf("A", "=", "1"))
leaves = collect_leaves(tree)
assert len(leaves) == 1
# ── evaluate_tree ──
def test_evaluate_leaf_true():
"""叶子节点求值"""
leaf = CondLeaf("A", ">", "0")
assert evaluate_tree(leaf, {leaf: True}) is True
assert evaluate_tree(leaf, {leaf: False}) is False
def test_evaluate_and_true():
"""AND 全部 True → True"""
l1 = CondLeaf("A", ">", "0")
l2 = CondLeaf("B", "<", "5")
tree = CondAnd(l1, l2)
assert evaluate_tree(tree, {l1: True, l2: True}) is True
def test_evaluate_and_false():
"""AND 任一 False → False"""
l1 = CondLeaf("A", ">", "0")
l2 = CondLeaf("B", "<", "5")
tree = CondAnd(l1, l2)
assert evaluate_tree(tree, {l1: True, l2: False}) is False
def test_evaluate_or_true():
"""OR 任一 True → True"""
l1 = CondLeaf("A", "=", "1")
l2 = CondLeaf("B", "=", "2")
tree = CondOr(l1, l2)
assert evaluate_tree(tree, {l1: True, l2: False}) is True
def test_evaluate_or_false():
"""OR 全部 False → False"""
l1 = CondLeaf("A", "=", "1")
l2 = CondLeaf("B", "=", "2")
tree = CondOr(l1, l2)
assert evaluate_tree(tree, {l1: False, l2: False}) is False
def test_evaluate_not():
"""NOT 反转"""
leaf = CondLeaf("A", "=", "1")
tree = CondNot(leaf)
assert evaluate_tree(tree, {leaf: True}) is False
assert evaluate_tree(tree, {leaf: False}) is True
# ── CO-06~08: mcdc_sets ──
def test_mcdc_single_leaf_returns_none():
"""CO-06: 单条件 (IF A > 100) → None (不需要 MC/DC)"""
tree = CondLeaf("A", ">", "100")
assert mcdc_sets(tree) is None
def test_mcdc_and():
"""CO-07: AND (A > 0 AND B < 5) → 3 sets (MC/DC)"""
tree = CondAnd(CondLeaf("A", ">", "0"), CondLeaf("B", "<", "5"))
sets = mcdc_sets(tree)
assert sets is not None
# AND 需要 3 个测试对: TT→T, TF→F, FT→F
# 实际上 mcdc_sets 返回约束集,包含 True/False 决策
decisions = set(d for _, d in sets)
assert True in decisions
assert False in decisions
# 各叶子应有独立影响
all_constraints = [c for constraints, _ in sets for c in constraints]
fields_involved = set(c[0] for c in all_constraints)
assert "A" in fields_involved
assert "B" in fields_involved
def test_mcdc_or():
"""CO-08: OR (A = 1 OR B = 2) → 3 sets (MC/DC)"""
tree = CondOr(CondLeaf("A", "=", "1"), CondLeaf("B", "=", "2"))
sets = mcdc_sets(tree)
assert sets is not None
decisions = set(d for _, d in sets)
assert True in decisions
assert False in decisions
# ── is_field ──
def test_is_field_match():
"""字段名匹配"""
fields = [{"name": "WS-AMOUNT"}, {"name": "WS-STATUS"}]
assert is_field("WS-AMOUNT", fields) is True
def test_is_field_subscript():
"""带下标字段名匹配"""
fields = [{"name": "WS-ITEM-STATUS"}]
assert is_field("WS-ITEM-STATUS(WS-INDEX)", fields) is True
def test_is_field_no_match():
"""未知字段名返回 False"""
fields = [{"name": "WS-AMOUNT"}]
assert is_field("WS-OTHER", fields) is False
# ── satisfying_value ──
def test_satisfying_value_greater():
"""数值 > 条件: 返回值应大于给定值"""
from cobol_testgen.cond import satisfying_value
info = {"type": "numeric", "digits": 7, "decimal": 0}
r = satisfying_value(info, ">", "100", want_true=True)
assert int(r) > 100
def test_satisfying_value_equal_false():
"""= 条件 want=False: 返回不同值"""
from cobol_testgen.cond import satisfying_value
info = {"type": "numeric", "digits": 7, "decimal": 0}
r = satisfying_value(info, "=", "100", want_true=False)
assert int(r) != 100