"""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)