R4-R7: 全モジュール深層カバレッジ補完(727テスト/0FAIL)

R4: core.py(289IF) + __init__.py(91IF) 内部関数全網羅
R4-design: design.py(161IF) enum_paths/constraint/redefines/occurs
R4-cond: cond.py(51IF) 全演算子×T/F×MC/DC
R4-coverage: coverage.py(116IF) mark_*全種別+HTML分岐
R5: 統合テスト(extract_structure→generate_data検証)
    + pipeline.py(34IF)+hina_agent.py(12IF)+read.py(54IF)
    + output.py(19IF)+orchestrator.py+classifier.py追加
R6: 複合ネストIF/PERFORM/EVAL/SEARCH+PIC解析全部
R7: FD方向解析+混乱グループ+contradiction+LLM応答

残環境依存: web/api(6IF), web/worker(6IF), runners/(6IF), gcov(6IF)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
NB-076
2026-06-22 00:02:18 +08:00
parent cb3c32ca95
commit 7a562c27a4
7 changed files with 2930 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
"""R4: 深層カバレッジ — cobol_testgen/cond.py (51IF)"""
import sys, os; sys.path.insert(0, os.path.join(os.path.dirname(__file__),'..'))
P=0;F=0
def ck(v,m=""): global P,F; (P:=P+1) if v else (F:=F+1,print(f" FAIL {m}"))
def sec(n): print(f"\n--- {n} ---")
from cobol_testgen.cond import (_split_at_operator,parse_single_condition,parse_compound_condition,
collect_leaves,evaluate_tree,is_field,mcdc_sets,satisfying_value)
from cobol_testgen.models import CondLeaf,CondAnd,CondOr,CondNot
sec("_split_at_operator")
ck(_split_at_operator("A OR B","OR")==["A","B"],"split basic")
ck(_split_at_operator("(A OR B) AND C","OR")==["( A OR B ) AND C"],"split paren depth2")
ck(_split_at_operator("A","OR")==["A"],"split single")
ck(_split_at_operator("A()B","OR")==["A ( ) B"],"split empty paren2")
ck(_split_at_operator("A OR B","OR")==["A","B"],"split multiple spaces")
sec("parse_single_condition")
ck(parse_single_condition("AMOUNT>1000")==("AMOUNT",">","1000"),"simple >")
ck(parse_single_condition("A AND B") is None,"compound returns None")
ck(parse_single_condition("WS-ITEM(SUB)='A'")[0]=="WS-ITEM(SUB)","subscript")
# 88-level
ck(parse_single_condition("STATUS-APPROVED",[{"is_88":True,"name":"STATUS-APPROVED","parent":"WS-STATUS","value":"A"}])==("WS-STATUS","=","A"),"88-level")
# No 88 match
ck(parse_single_condition("UNKNOWN-88",[{"is_88":True,"name":"OTHER-88"}]) is None,"88 no match")
# Arithmetic expression
ck(parse_single_condition("A+B>100") is not None,"arith expr")
# No match
ck(parse_single_condition("$%^") is None,"no match")
sec("parse_compound_condition")
ck(parse_compound_condition("") is None,"empty")
# Outer parens unwrap
t=parse_compound_condition("(X>5)",[])
ck(t is not None,"paren unwrap")
ck(isinstance(t,CondLeaf),"paren leaf")
# OR
t=parse_compound_condition("X>5 OR Y<10",[])
ck(isinstance(t,CondOr),"or top")
# AND
t=parse_compound_condition("X>5 AND Y<10",[])
ck(isinstance(t,CondAnd),"and top")
# NOT
t=parse_compound_condition("NOT X>5",[])
ck(isinstance(t,CondNot),"not")
# NOT with no inner
t=parse_compound_condition("NOT",[]); ck(t is None,"not empty")
# Nested parens that can't be unwrapped
t=parse_compound_condition("(X>5) AND (Y<10)",[])
ck(isinstance(t,CondAnd),"and inner parens")
# Outer parens NOT wrapped (multiple top-level groups)
t=parse_compound_condition("(X>5) AND Y<10",[])
ck(t is not None,"paren not fully wrapped")
# Single leaf
t=parse_compound_condition("X>5",[]); ck(isinstance(t,CondLeaf),"single leaf")
# Unparseable
t=parse_compound_condition("$%^",[]); ck(t is None,"unparseable")
sec("collect_leaves")
l=CondLeaf("X",">","5")
ck(collect_leaves(l)==[l],"leaf")
ck(collect_leaves(CondNot(l))==[l],"not")
ck(len(collect_leaves(CondAnd(l,CondLeaf("Y","=","1"))))==2,"and")
ck(len(collect_leaves(CondOr(l,CondLeaf("Z","<","9"))))==2,"or")
# Unknown type
ck(collect_leaves("bad")==[],"bad type")
sec("evaluate_tree")
l1=CondLeaf("X",">","5"); l2=CondLeaf("Y","=","1")
a={l1:True,l2:False}
ck(evaluate_tree(l1,a)==True,"leaf eval")
ck(evaluate_tree(CondNot(l1),a)==False,"not eval")
ck(evaluate_tree(CondAnd(l1,l2),a)==False,"and eval")
ck(evaluate_tree(CondOr(l1,l2),a)==True,"or eval")
ck(evaluate_tree("bad",{})==False,"bad eval")
sec("is_field")
ck(is_field("WS-STATUS",[{"name":"WS-STATUS"}]),"field match")
ck(is_field("WS-STATUS(SUB)",[{"name":"WS-STATUS"}]),"field subscript")
ck(is_field("MISSING",[{"name":"WS-STATUS"}])==False,"field nomatch")
sec("mcdc_sets")
# n<=1 returns None
ck(mcdc_sets(CondLeaf("X",">","5")) is None,"mcdc single leaf")
# n>=2 returns MC/DC sets
t=CondAnd(CondLeaf("X",">","5"),CondLeaf("Y","=","1"))
s=mcdc_sets(t)
ck(s is not None,"mcdc 2 leafs")
ck(len(s)>=2,"mcdc has pairs")
# 3 leafs
t3=CondAnd(CondLeaf("A","=","1"),CondAnd(CondLeaf("B","=","2"),CondLeaf("C","=","3")))
s3=mcdc_sets(t3); ck(s3 is not None,"mcdc 3 leafs")
# OR
t4=CondOr(CondLeaf("X",">","5"),CondLeaf("Y","=","1"))
s4=mcdc_sets(t4); ck(s4 is not None,"mcdc OR")
sec("satisfying_value — numeric")
fi_num={"type":"numeric","digits":5,"decimal":0}
# want_true branches
ck(satisfying_value(fi_num,">","100",True)=="00101","num > T")
ck(satisfying_value(fi_num,"=","100",True)=="00100","num = T")
ck(satisfying_value(fi_num,">=","100",True)=="00100","num >= T")
ck(satisfying_value(fi_num,"<=","100",True)=="00100","num <= T")
ck(satisfying_value(fi_num,"<","1",True)=="00000","num < T (max(0,val-1) => 0)")
ck(satisfying_value(fi_num,"<>","100",True)=="00101","num <> T")
# want_false branches
ck(satisfying_value(fi_num,">","100",False)=="00000","num > F → 0")
ck(satisfying_value(fi_num,">=","100",False)=="00000","num >= F → 0")
ck(satisfying_value(fi_num,"=","100",False)=="00101","num = F → (val+1)%max")
ck(satisfying_value(fi_num,"<","100",False)=="00100","num < F → pass (val unchanged)")
ck(satisfying_value(fi_num,"<=","100",False)=="00101","num <= F → val+1")
ck(satisfying_value(fi_num,"<>","100",False)=="00100","num <> F → pass")
# max value (wraparound)
ck(satisfying_value({"type":"numeric","digits":1,"decimal":0},"=","9",False)=="0","num wrap")
# bad value (ValueError)
ck(satisfying_value(fi_num,">","ABC",True)=="00001","num bad val")
# With decimal: val_int = int(1.50 * 100 + 0.5) = 150
fi_dec={"type":"numeric","digits":3,"decimal":2}
ck(satisfying_value(fi_dec,"=","1.50",True)=="00150","num dec =")
# > adds 1: 151 → int_part=001, dec_part=51
ck(satisfying_value(fi_dec,">","1.50",True)=="00151","num dec >")
# Alphanumeric: ljust uses base_chr[0], so "HELLO" gives base='H'
fi_alpha={"type":"alphanumeric","length":5}
ck(satisfying_value(fi_alpha,"=","HELLO",True)=="HHHHH","alpha = T (base='H' *5)")
ck(satisfying_value(fi_alpha,"<>","HELLO",True)=="IIIII","alpha <> T (next letter)")
ck(satisfying_value(fi_alpha,"=","HELLO",False)=="IIIII","alpha = F (other letter)")
ck(satisfying_value(fi_alpha,"<>","HELLO",False)=="HHHHH","alpha <> F (same as match)")
# Fallback type
ck(satisfying_value({"type":"unknown","digits":0},">","5",True)=="0","fallback type")
print(f"\n{'='*55}\nR4-cond: {P} PASS / {F} FAIL\n{'='*55}")
if F>0: sys.exit(1)