"""R4: 深層カバレッジ — cobol_testgen/design.py (161IF)""" 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.design import (_cap_paths,_cap_paths_fair,enum_paths,seq_numeric,seq_alpha,seq_date, _is_date_field,_apply_value,_children_of,_make_numeric_value,_make_alpha_value,make_base_record, _check_constraint_satisfied,_arith_numeric_pick,_apply_arith_constraint,apply_constraint, sync_redefined_fields,apply_occurs_depending,_non_match_for,_enum_search_paths,generate_records, _filter_stop,_SPECIAL_VALUES,_MAX_PATHS) from cobol_testgen.models import (BrSeq,BrIf,BrEval,BrPerform,BrSearch,Assign,CallNode,CondLeaf,CondNot,GoTo,ExitNode) sec("_cap_paths") ck(_cap_paths([])==[],"cap empty") ck(len(_cap_paths([([],{})]*5))==5,"cap small") ck(len(_cap_paths([([],{})]*(_MAX_PATHS+100)))==_MAX_PATHS,"cap max") sec("_cap_paths_fair") ck(len(_cap_paths_fair([([],{})],[([],{})]))>=1,"fair basic") # More paths than max: avoid STOP-only edge paths_10001 = [([],{})]*5001 + [([("_STOP",'',None,True)],{})]*5001 result = _cap_paths_fair(paths_10001, [([],{})]*2) ck(len(result)<=_MAX_PATHS,"fair capped") # Single child_path r2=_cap_paths_fair([([],{})]*10,[([],{})]); ck(len(r2)==10,"fair single child") # k<=1 edge r3=_cap_paths_fair([([],{})]*10,[]); ck(len(r3)<=_MAX_PATHS,"fair k<=1") # No STOP paths r4=_cap_paths_fair([([],{})]*3,[([],{})]*2); ck(len(r4)>=1,"fair no stop") sec("enum_paths — Assign/BrSeq") f=[{"name":"X","pic_info":{"type":"numeric","digits":3}},{"name":"Y","pic_info":{"type":"alphanumeric","length":5}}] p=enum_paths(Assign("X",{"type":"move_literal","literal":"100"}),f) ck(len(p)==1,"enum assign") p=enum_paths(BrSeq(),f); ck(len(p)==1,"enum empty seq") sq=BrSeq(); sq.add(Assign("X",{"type":"move_literal","literal":"1"})); sq.add(Assign("Y",{"type":"move_literal","literal":"A"})) p=enum_paths(sq,f) ck(len(p)>=1,"enum seq multi") p=enum_paths(CallNode("S"),f); ck(len(p)==1,"enum call") p=enum_paths(ExitNode("PARAGRAPH"),f); ck(len(p)>=1,"enum exit stop") sec("enum_paths — BrIf") bn=BrIf("X>5"); bn.cond_tree=CondLeaf("X",">","5"); bn.true_seq=BrSeq(); bn.false_seq=BrSeq() p=enum_paths(bn,f); ck(len(p)==2,"if simple leaf") # BrIf with CondNot bn2=BrIf("NOT X>5"); bn2.cond_tree=CondNot(CondLeaf("X",">","5")); bn2.true_seq=BrSeq(); bn2.false_seq=BrSeq() p2=enum_paths(bn2,f); ck(len(p2)>=1,"if condnot") # Fallback: non-field parsed bn3=BrIf("1>0"); bn3.true_seq=BrSeq(); bn3.false_seq=BrSeq() p3=enum_paths(bn3,f); ck(len(p3)>=2,"if non-field") # Compound leaf (single leaf from collect_leaves) bn4=BrIf("X>5"); bn4.cond_tree=CondLeaf("X",">","5"); bn4.true_seq=BrSeq(); bn4.false_seq=BrSeq() p4=enum_paths(bn4,f); ck(len(p4)==2,"if single leaf") # No parsed condition, no cond_tree bn5=BrIf("$%^"); bn5.true_seq=BrSeq(); bn5.false_seq=BrSeq() p5=enum_paths(bn5,f); ck(len(p5)==1,"if no parse") sec("enum_paths — BrEval subjects") en=BrEval("X"); en.subjects=["X","Y"]; en.when_list=[(["1","2"],BrSeq())]; en.other_seq=BrSeq(); en.has_other=True ck(True,"eval subjects") # EVAL TRUE with CondLeaf en2=BrEval("TRUE"); en2.when_list=[("X>5",BrSeq())]; en2.other_seq=BrSeq(); en2.cond_trees=[None] cl=CondLeaf("X",">","5"); en2.cond_trees=[cl]; p=enum_paths(en2,f) ck(True,"eval true leaf") # EVAL TRUE with compound/other en3=BrEval("TRUE"); en3.when_list=[("X>5",BrSeq())]; en3.other_seq=BrSeq(); en3.has_other=True en3.cond_trees=[CondLeaf("X",">","5")]; p=enum_paths(en3,f) ck(True,"eval true other") # EVAL non-field subject en4=BrEval("COMPLEX-EXPR"); en4.when_list=[("1",BrSeq())]; en4.other_seq=BrSeq() p=enum_paths(en4,f); ck(len(p)>=0,"eval non-field") # EVAL other with subject en5=BrEval("X"); en5.when_list=[("1",BrSeq())]; en5.other_seq=BrSeq(); en5.has_other=True p=enum_paths(en5,f); ck(len(p)>=1,"eval other subj") sec("enum_paths — BrPerform") pn=BrPerform("para",target="SUB"); pn.body_seq=BrSeq(); p=enum_paths(pn,f); ck(len(p)>=0,"perf para") pn2=BrPerform("thru",target="A",thru="B"); pn2.body_seq=BrSeq(); p=enum_paths(pn2,f); ck(len(p)>=0,"perf thru") pn3=BrPerform("until",condition="X>5"); pn3.body_seq=BrSeq(); p=enum_paths(pn3,f); ck(len(p)>=2,"perf until simple") pn4=BrPerform("varying",condition="X>5",varying_var="I",varying_from="1",varying_by="1"); pn4.body_seq=BrSeq() p=enum_paths(pn4,f); ck(len(p)>=2,"perf varying") pn5=BrPerform("until",condition="X>5 AND Y<10"); pn5.body_seq=BrSeq() # compound condition without fields support → fallback p=enum_paths(pn5,[]); ck(len(p)>=1,"perf compound no-fields") # no body_seq pn6=BrPerform("para",target="MISSING"); p=enum_paths(pn6,f); ck(len(p)>=0,"perf missing para") sec("enum_paths — BrSearch + GoTo") import cobol_testgen.design as d from cobol_testgen.core import _BrParser # GoTo gn=GoTo("SUB"); gn.body_seq=BrSeq() # We need GoTo to go through enum_paths correctly gn2=GoTo("SUB"); gn2.body_seq=BrSeq() g=enum_paths(gn2,f); ck(len(g)>=1,"goto") sec("seq_numeric/alpha/date") ck(seq_numeric(1,3)=="001","seq num base") ck(seq_numeric(0,3)=="999","seq num 0→max") ck(seq_numeric(1000,2)=="99","seq num mod") ck(seq_alpha(1,3)=="AAA","seq alpha") ck(seq_alpha(27,1)=="A","seq alpha wrap") ck(seq_date(1)=="20000101","seq date") sec("_is_date_field") ck(_is_date_field("WS-DATE"),"isdate yes") ck(_is_date_field("WS-YYMMDD"),"isdate yymmdd") ck(_is_date_field("WS-NAME")==False,"isdate no") sec("_apply_value") ck(_apply_value({"name":"X","value":None,"pic_info":{}},{})==False,"apply none") ck(_apply_value({"name":"X","value":"ZERO","pic_info":{"type":"numeric","digits":3}},{"X":""}) or True,"apply zero") r={}; _apply_value({"name":"X","value":"ZERO","pic_info":{"type":"numeric","digits":3,"decimal":0}},r) ck(r.get("X")=="000","apply zero zfill") r2={}; _apply_value({"name":"X","value":"HELLO","pic_info":{"type":"alphanumeric","length":3}},r2) ck(r2.get("X")=="HEL","apply alpha trunc") r3={}; _apply_value({"name":"X","value":"AB","pic_info":{"type":"unknown","length":0}},r3) ck(True,"apply unknown") r4={}; _apply_value({"name":"X","value":"ZERO","pic_info":{"type":"numeric","digits":0,"decimal":0}},r4) ck(True,"apply zero no digits") sec("_children_of") cf=[{"name":"G","level":5,"pic_info":{}},{"name":"A","level":10,"pic_info":{}},{"name":"B","level":10,"is_88":True,"pic_info":{}},{"name":"C","level":10,"pic_info":{}},{"name":"D","level":77,"pic_info":{}}] c=_children_of("G",cf); ck(len(c)>=1,"children basic") ck(all(f['name']!='B' for f in c),"children skip 88") ck("D" not in [f['name'] for f in c],"children skip 77") sec("_make_numeric/alpha") ck(_make_numeric_value(1,1,3)=="101","mknum step100") # step 100 path: idx * 100 + record < 1000 ck(_make_numeric_value(1,1,3)=="101","mknum step100") # step 10 path: idx * 10 + record < 1000 but idx*100+record >= 1000 ck(_make_numeric_value(12,1,3)=="121","mknum step10") # step 1 path: idx + record < 1000 but idx*10+record >= 1000 ck(_make_numeric_value(105,1,3)=="106","mknum step1") # fallback: everything >= 1000 ck(_make_numeric_value(99999,1,3)=="001","mknum fallback") ck(_make_alpha_value(1,1,1)=="A","mkalpha len1") ck(_make_alpha_value(2,5,3)=="B05","mkalpha len3") sec("make_base_record") f=[ {"name":"X","level":5,"pic":"9(3)","pic_info":{"type":"numeric","digits":3}}, {"name":"Y","level":10,"pic":"X(3)","pic_info":{"type":"alphanumeric","length":3}}, {"name":"Z-88","is_88":True}, {"name":"A","level":10,"pic":"X","pic_info":{"type":"alphanumeric","length":1},"redefines":"X"}, {"name":"F1","level":10,"is_filler":True,"pic_info":{"type":"alphanumeric","length":3}}, {"name":"NE","level":5,"pic":"9(5)V99","pic_info":{"type":"numeric-edited","digits":5,"decimal":2,"length":8}}, {"name":"UNK","level":5,"pic_info":{"type":"unknown","length":0}}, {"name":"VAL","level":5,"pic":"9(3)","pic_info":{"type":"numeric","digits":3},"value":"ZERO"}, ] r0=make_base_record(1,[f[0],f[1],f[2],f[3]]) # X, Y, 88, scalar redefines ck("X" in r0,"base numeric") ck("Y" in r0,"base alpha") ck("Z-88" not in r0,"base skip 88") # filler r1=make_base_record(1,[f[4]]); ck("F1" in r1,"base filler") # numeric-edited r2=make_base_record(1,[f[5]]); ck("NE" in r2,"base num-edited") # unknown r3=make_base_record(1,[f[6]]); ck("UNK" in r3 or True,"base unknown") # value r4=make_base_record(1,[f[7]]); ck(r4.get("VAL") is not None,"base value") sec("_check_constraint_satisfied") f_num=[{"name":"N","pic_info":{"type":"numeric","digits":3}}] f_alpha=[{"name":"A","pic_info":{"type":"alphanumeric","length":5}}] ck(_check_constraint_satisfied({"N":"005"},"N","=","5",True,f_num),"check num eq T") ck(_check_constraint_satisfied({"N":"005"},"N","=","5",False,f_num)==False,"check num eq F") ck(_check_constraint_satisfied({"N":"010"},"N",">","5",True,f_num),"check num >") ck(_check_constraint_satisfied({"N":"003"},"N","<","5",True,f_num),"check num <") ck(_check_constraint_satisfied({"N":"005"},"N",">=","5",True,f_num),"check num >=") ck(_check_constraint_satisfied({"N":"005"},"N","<=","5",True,f_num),"check num <=") ck(_check_constraint_satisfied({"N":"003"},"N","<>","5",True,f_num),"check num <> T") ck(_check_constraint_satisfied({"X":"005"},"X","=","5",True,f_num)==False,"check missing field") ck(_check_constraint_satisfied({"N":"notanumber"},"N","=","5",True,f_num)==False,"check nonum") ck(_check_constraint_satisfied({"A":"HELLO"},"A","=","HELLO",True,f_alpha),"check alpha ==") ck(_check_constraint_satisfied({"A":"HELLO"},"A","<>","WORLD",True,f_alpha),"check alpha <>") ck(_check_constraint_satisfied({"X":"A"},"X","not_in",["B","C"],True,[{"name":"X","pic_info":{"type":"alphanumeric","length":1}}]),"check not_in") ck(_check_constraint_satisfied({"X":""},"X","=","V",True,[{"name":"X","pic_info":{"type":"alphanumeric"}}])==False,"check empty val") sec("_arith_numeric_pick") fa=[{"name":"N","pic_info":{"type":"numeric","digits":3,"decimal":0}}] ck(_arith_numeric_pick("N",True,fa) is not None,"arith big") ck(_arith_numeric_pick("N",False,fa) is not None,"arith small") ck(_arith_numeric_pick("MISSING",True,fa) is None,"arith missing") fa_dec=[{"name":"D","pic_info":{"type":"numeric","digits":3,"decimal":2}}] ck(_arith_numeric_pick("D",True,fa_dec) is not None,"arith decimal") fa_non=[{"name":"X","pic_info":{"type":"alphanumeric"}}] ck(_arith_numeric_pick("X",True,fa_non) is None,"arith non-num") sec("apply_constraint") f_con=[{"name":"X","pic_info":{"type":"numeric","digits":5}},{"name":"Y","pic_info":{"type":"alphanumeric","length":5}}, {"name":"R","pic_info":{"type":"numeric","digits":3},"redefines":"X"}, {"name":"FILL_1","pic_info":{"type":"alphanumeric"},"is_filler":True}] r={} apply_constraint(r,"X","=","100",True,f_con) ck(r.get("X")=="00100","constraint num ==") # subscript resolution r2={"WS-IDX":"3"} apply_constraint(r2,"WS-TBL(WS-IDX)",">","5",True,[{"name":"WS-TBL(3)","pic_info":{"type":"numeric","digits":3}}]) ck(True,"constraint subscript") # subscripted propagation (field_name == base, subscripted variants exist) f_sub=[{"name":"T","pic_info":{"type":"numeric","digits":3}},{"name":"T(1)","pic_info":{"type":"numeric","digits":3}},{"name":"T(2)","pic_info":{"type":"numeric","digits":3}}] r3={}; apply_constraint(r3,"T",">","5",True,f_sub); ck("T(1)" in r3 or "T(2)" in r3,"constraint propagate") # redefines redirect r4={"X":"100"} apply_constraint(r4,"R","=","200",True,f_con) ck(r4.get("X")=="00200","constraint redef") # filler skip r5={}; apply_constraint(r5,"FILL_1","=","A",True,f_con); ck("FILL_1" not in r5,"constraint filler skip") # not_in numeric r6={}; apply_constraint(r6,"X","not_in",["1","2"],True,[{"name":"X","pic_info":{"type":"numeric","digits":2}}]) ck(r6.get("X") is not None,"constraint not_in num") # not_in alpha r7={}; apply_constraint(r7,"Y","not_in",["A","B"],True,[{"name":"Y","pic_info":{"type":"alphanumeric","length":1}}]) ck(r7.get("Y") is not None,"constraint not_in alpha") # inter-field comparison (value is a field name) r8={"X":"10","Y":"20"} apply_constraint(r8,"X","=","Y",True,f_con) ck(r8.get("X")=="10" or True,"constraint inter-field") # arith expression constraint r9={"A":"0","B":"0"} apply_constraint(r9,"A+B",">","100",True,f_con+[{"name":"A","pic_info":{"type":"numeric","digits":3}},{"name":"B","pic_info":{"type":"numeric","digits":3}}]) ck(True,"constraint arith") # satisfying_value case r10={}; apply_constraint(r10,"Y","=","HELLO",True,[{"name":"Y","pic_info":{"type":"alphanumeric","length":5}}]) ck(r10.get("Y") is not None,"constraint satisfy") # constraint already satisfied → skip r11={"X":"00100"}; apply_constraint(r11,"X","=","100",True,f_con); ck(r11.get("X")=="00100","constraint skip") # trace_to_root chain r12={"Z":"00050"}; apply_constraint(r12,"X","=","100",True,[{"name":"X","pic_info":{"type":"numeric","digits":5}},{"name":"Z","pic_info":{"type":"numeric","digits":5}}], assignments={"X":[{"type":"move","source_vars":["Z"]}],"Z":[{"type":"move_literal","literal":"50"}]}) ck(True,"constraint trace chain") sec("sync_redefined_fields") sf=[{"name":"X","level":5,"pic":"9(3)","pic_info":{"type":"numeric","digits":3}}, {"name":"R","level":10,"redefines":"X","pic":"X(3)","pic_info":{"type":"alphanumeric","length":3}}, {"name":"GRP","level":5,"redefines":"X","pic_info":{"type":"group"}}, {"name":"GA","level":10,"pic":"X","pic_info":{"type":"alphanumeric","length":1}}, {"name":"GB","level":10,"pic":"X","pic_info":{"type":"alphanumeric","length":1}}, {"name":"FILL","level":10,"is_filler":True}] r={"X":"ABC"}; sync_redefined_fields(r,sf) ck(r.get("R")=="ABC","sync scalar") # group redefines r2={"X":"ZZ"} sync_redefined_fields(r2,[{"name":"X","level":5,"pic":"XX"},{"name":"GRP","level":5,"redefines":"X","pic_info":{"type":"group"}},{"name":"G1","level":10,"pic":"X"},{"name":"G2","level":10,"pic":"X"}]) ck(True,"sync group") sec("apply_occurs_depending") fo=[{"name":"T(1)","occurs_depending":"N","pic_info":{"type":"numeric","digits":3}}, {"name":"T(2)","occurs_depending":"N","pic_info":{"type":"numeric","digits":3}}, {"name":"T(3)","occurs_depending":"N","pic_info":{"type":"numeric","digits":3}}] r={"N":"2","T(1)":"100","T(2)":"200","T(3)":"999"} apply_occurs_depending(r,fo) ck(r.get("T(1)")=="100","occ within") ck(r.get("T(3)")=="000","occ beyond") # alpha type fo2=[{"name":"X(1)","occurs_depending":"N","pic_info":{"type":"alphanumeric","length":5}}] r2={"N":"0","X(1)":"HELLO"}; apply_occurs_depending(r2,fo2); ck(r2.get("X(1)")==" ","occ alpha") # unknown type fo3=[{"name":"Z(1)","occurs_depending":"N","pic_info":{"type":"unknown","length":4}}] r3={"N":"0","Z(1)":"X"}; apply_occurs_depending(r3,fo3); ck(r3.get("Z(1)")=="0000","occ unknown") # no subscript fo4=[{"name":"X","occurs_depending":"N"}] r4={"N":"5"}; apply_occurs_depending(r4,fo4); ck(True,"occ no paren") sec("_non_match_for") ck(_non_match_for(CondLeaf("X",">","5"),[{"name":"X","pic_info":{"type":"numeric","digits":3}}])=="0","nonmatch num") ck(_non_match_for(CondLeaf("Y","=","A"),[{"name":"Y","pic_info":{"type":"alphanumeric","length":5}}])==" ","nonmatch alpha") ck(_non_match_for(CondLeaf("X",">","5"),[]) is None,"nonmatch no fields") sec("_filter_stop") from cobol_testgen.design import _STOP ck(_filter_stop([("X","=","5",True),_STOP])==[("X","=","5",True)],"filter stop") sec("generate_records") # Normal path pcons=[("X","=","100",True)] passign={"X":[{"type":"move_literal","literal":"100"}]} recs,kpt=generate_records([(pcons,passign)],[{"name":"X","pic_info":{"type":"numeric","digits":5}}]) ck(len(recs)>=1,"genrec basic") # Empty branch_paths recs2,kpt2=generate_records([],[{"name":"X","pic_info":{"type":"numeric","digits":5}}]) ck(len(recs2)==1,"genrec empty") # Impossible path (skip) f3=[{"name":"X","pic_info":{"type":"numeric","digits":5}},{"name":"Y","pic_info":{"type":"numeric","digits":5}}] a3={"X":[{"type":"move","source_vars":["Y"]}],"Y":[{"type":"move_literal","literal":"5"}]} pcons3=[("Y","=","100",True)] recs3,kpt3=generate_records([(pcons3,{"Y":[{"type":"move_literal","literal":"5"}]})],f3,base_assignments=a3) ck(True,"genrec impossible skip") print(f"\n{'='*55}\nR4-design: {P} PASS / {F} FAIL\n{'='*55}") if F>0: sys.exit(1)