129921a8fSScott Kruger#!/usr/bin/env python 229921a8fSScott Kruger""" 329921a8fSScott KrugerParse the test file and return a dictionary. 429921a8fSScott Kruger 529921a8fSScott KrugerQuick usage:: 629921a8fSScott Kruger 729921a8fSScott Kruger bin/maint/testparse.py -t src/ksp/ksp/examples/tutorials/ex1.c 829921a8fSScott Kruger 929921a8fSScott KrugerFrom the command line, it prints out the dictionary. 1029921a8fSScott KrugerThis is meant to be used by other scripts, but it is 1129921a8fSScott Krugeruseful to debug individual files. 1229921a8fSScott Kruger 1329921a8fSScott KrugerExample language 1429921a8fSScott Kruger---------------- 1529921a8fSScott Kruger 1629921a8fSScott Kruger/*TEST 17aec507c4SScott Kruger build: 18aec507c4SScott Kruger requires: moab 19e53dc769SScott Kruger # This is equivalent to test: 20e53dc769SScott Kruger testset: 2129921a8fSScott Kruger args: -pc_type mg -ksp_type fgmres -da_refine 2 -ksp_monitor_short -mg_levels_ksp_monitor_short -mg_levels_ksp_norm_type unpreconditioned -ksp_view -pc_mg_type full 2229921a8fSScott Kruger 23e53dc769SScott Kruger testset: 2429921a8fSScott Kruger suffix: 2 2529921a8fSScott Kruger nsize: 2 2629921a8fSScott Kruger args: -pc_type mg -ksp_type fgmres -da_refine 2 -ksp_monitor_short -mg_levels_ksp_monitor_short -mg_levels_ksp_norm_type unpreconditioned -ksp_view -pc_mg_type full 2729921a8fSScott Kruger 28e53dc769SScott Kruger testset: 29e53dc769SScott Kruger suffix: 2 30e53dc769SScott Kruger nsize: 2 31e53dc769SScott Kruger args: -pc_type mg -ksp_type fgmres -da_refine 2 -ksp_monitor_short -mg_levels_ksp_monitor_short -mg_levels_ksp_norm_type unpreconditioned -ksp_view -pc_mg_type full 32e53dc769SScott Kruger test: 33e53dc769SScott Kruger 3429921a8fSScott KrugerTEST*/ 3529921a8fSScott Kruger 3629921a8fSScott Kruger""" 3729921a8fSScott Kruger 3829921a8fSScott Krugerimport os, re, glob, types 3929921a8fSScott Krugerimport sys 4029921a8fSScott Krugerimport logging 4129921a8fSScott Krugersys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) 4229921a8fSScott Kruger 4329921a8fSScott Krugerimport inspect 4429921a8fSScott Krugerthisscriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 4529921a8fSScott Krugermaintdir=os.path.join(os.path.join(os.path.dirname(thisscriptdir),'bin'),'maint') 4629921a8fSScott Krugersys.path.insert(0,maintdir) 4729921a8fSScott Kruger 4829921a8fSScott Kruger# These are special keys describing build 4929921a8fSScott Krugerbuildkeys="requires TODO SKIP depends".split() 5029921a8fSScott Kruger 510a091e3eSScott Krugeracceptedkeys="test nsize requires command suffix args filter filter_output localrunfiles comments TODO SKIP output_file timeoutfactor".split() 5244776d8cSScott Krugerappendlist="args requires comments".split() 5368a9e459SScott Kruger 5468a9e459SScott Krugerimport re 5568a9e459SScott Kruger 5644776d8cSScott Krugerdef _stripIndent(block,srcfile,entireBlock=False,fileNums=[]): 5729921a8fSScott Kruger """ 5829921a8fSScott Kruger Go through and remove a level of indentation 5929921a8fSScott Kruger Also strip of trailing whitespace 6029921a8fSScott Kruger """ 6129921a8fSScott Kruger # The first entry should be test: but it might be indented. 6229921a8fSScott Kruger ext=os.path.splitext(srcfile)[1] 6344776d8cSScott Kruger stripstr=" " 6466db876fSScott Kruger if len(fileNums)>0: lineNum=fileNums[0]-1 658ccd5183SScott Kruger for lline in block.split("\n"): 6666db876fSScott Kruger if len(fileNums)>0: lineNum+=1 678ccd5183SScott Kruger line=lline[1:] if lline.startswith("!") else lline 6829921a8fSScott Kruger if not line.strip(): continue 69c4b80baaSScott Kruger if line.strip().startswith('#'): continue 7044776d8cSScott Kruger if entireBlock: 7144776d8cSScott Kruger var=line.split(":")[0].strip() 72aec507c4SScott Kruger if not var in ['test','testset','build']: 7366db876fSScott Kruger raise Exception("Formatting error: Cannot find test in file: "+srcfile+" at line: "+str(lineNum)+"\n") 7429921a8fSScott Kruger nspace=len(line)-len(line.lstrip(stripstr)) 7529921a8fSScott Kruger newline=line[nspace:] 7629921a8fSScott Kruger break 7729921a8fSScott Kruger 7829921a8fSScott Kruger # Strip off any indentation for the whole string and any trailing 7929921a8fSScott Kruger # whitespace for convenience 8029921a8fSScott Kruger newTestStr="\n" 8144776d8cSScott Kruger if len(fileNums)>0: lineNum=fileNums[0]-1 8244776d8cSScott Kruger firstPass=True 838ccd5183SScott Kruger for lline in block.split("\n"): 8444776d8cSScott Kruger if len(fileNums)>0: lineNum+=1 858ccd5183SScott Kruger line=lline[1:] if lline.startswith("!") else lline 8629921a8fSScott Kruger if not line.strip(): continue 87c4b80baaSScott Kruger if line.strip().startswith('#'): 88c4b80baaSScott Kruger newTestStr+=line+'\n' 89c4b80baaSScott Kruger else: 9029921a8fSScott Kruger newline=line[nspace:] 91c4b80baaSScott Kruger newTestStr+=newline.rstrip()+"\n" 9244776d8cSScott Kruger # Do some basic indentation checks 9344776d8cSScott Kruger if entireBlock: 9444776d8cSScott Kruger # Don't need to check comment lines 9544776d8cSScott Kruger if line.strip().startswith('#'): continue 9644776d8cSScott Kruger if not newline.startswith(" "): 9744776d8cSScott Kruger var=newline.split(":")[0].strip() 98aec507c4SScott Kruger if not var in ['test','testset','build']: 9944776d8cSScott Kruger err="Formatting error in file "+srcfile+" at line: " +line+"\n" 10044776d8cSScott Kruger if len(fileNums)>0: 10144776d8cSScott Kruger raise Exception(err+"Check indentation at line number: "+str(lineNum)) 10244776d8cSScott Kruger else: 10344776d8cSScott Kruger raise Exception(err) 10444776d8cSScott Kruger else: 10544776d8cSScott Kruger var=line.split(":")[0].strip() 106aec507c4SScott Kruger if var in ['test','testset','build']: 10744776d8cSScott Kruger subnspace=len(line)-len(line.lstrip(stripstr)) 10844776d8cSScott Kruger if firstPass: 10944776d8cSScott Kruger firstsubnspace=subnspace 11044776d8cSScott Kruger firstPass=False 11144776d8cSScott Kruger else: 11244776d8cSScott Kruger if firstsubnspace!=subnspace: 11344776d8cSScott Kruger err="Formatting subtest error in file "+srcfile+" at line: " +line+"\n" 11444776d8cSScott Kruger if len(fileNums)>0: 11544776d8cSScott Kruger raise Exception(err+"Check indentation at line number: "+str(lineNum)) 11644776d8cSScott Kruger else: 11744776d8cSScott Kruger raise Exception(err) 11844776d8cSScott Kruger 11929921a8fSScott Kruger 12029921a8fSScott Kruger return newTestStr 12129921a8fSScott Kruger 122aae9f2d9SScott Krugerdef parseLoopArgs(varset): 12344776d8cSScott Kruger """ 124aae9f2d9SScott Kruger Given: String containing loop variables 125aae9f2d9SScott Kruger Return: tuple containing separate/shared and string of loop vars 12644776d8cSScott Kruger """ 127aae9f2d9SScott Kruger keynm=varset.split("{{")[0].strip() 128aae9f2d9SScott Kruger if not keynm.strip(): keynm='nsize' 129aae9f2d9SScott Kruger lvars=varset.split('{{')[1].split('}')[0] 130aae9f2d9SScott Kruger suffx=varset.split('{{')[1].split('}')[1] 131aae9f2d9SScott Kruger ftype='separate' if suffx.startswith('separate') else 'shared' 132aae9f2d9SScott Kruger return keynm,lvars,ftype 13344776d8cSScott Kruger 134e53dc769SScott Krugerdef _getSeparateTestvars(testDict): 135e53dc769SScott Kruger """ 136e53dc769SScott Kruger Given: dictionary that may have 137e53dc769SScott Kruger Return: Variables that cause a test split 138e53dc769SScott Kruger """ 139e53dc769SScott Kruger vals=None 140e53dc769SScott Kruger sepvars=[] 141e53dc769SScott Kruger # Check nsize 142e53dc769SScott Kruger if testDict.has_key('nsize'): 143e53dc769SScott Kruger varset=testDict['nsize'] 144aae9f2d9SScott Kruger if '{{' in varset: 145aae9f2d9SScott Kruger keynm,lvars,ftype=parseLoopArgs(varset) 146aae9f2d9SScott Kruger if ftype=='separate': sepvars.append(keynm) 147e53dc769SScott Kruger 148e53dc769SScott Kruger # Now check args 149e53dc769SScott Kruger if not testDict.has_key('args'): return sepvars 150e53dc769SScott Kruger for varset in re.split('-(?=[a-zA-Z])',testDict['args']): 151e53dc769SScott Kruger if not varset.strip(): continue 152aae9f2d9SScott Kruger if '{{' in varset: 153e53dc769SScott Kruger # Assuming only one for loop per var specification 154aae9f2d9SScott Kruger keynm,lvars,ftype=parseLoopArgs(varset) 155aae9f2d9SScott Kruger if ftype=='separate': sepvars.append(keynm) 156e53dc769SScott Kruger 157e53dc769SScott Kruger return sepvars 158e53dc769SScott Kruger 159*4d82b48cSScott Krugerdef _getNewArgs(args): 160*4d82b48cSScott Kruger """ 161*4d82b48cSScott Kruger Given: String that has args that might have loops in them 162*4d82b48cSScott Kruger Return: All of the arguments/values that do not have 163*4d82b48cSScott Kruger for 'separate output' in for loops 164*4d82b48cSScott Kruger """ 165*4d82b48cSScott Kruger newargs='' 166*4d82b48cSScott Kruger if not args.strip(): return args 167*4d82b48cSScott Kruger for varset in re.split('-(?=[a-zA-Z])',args): 168*4d82b48cSScott Kruger if not varset.strip(): continue 169*4d82b48cSScott Kruger if '{{' not in varset: 170*4d82b48cSScott Kruger if 'separate' not in varset: 171*4d82b48cSScott Kruger newargs+="-"+varset.strip()+" " 172*4d82b48cSScott Kruger 173*4d82b48cSScott Kruger return newargs 174*4d82b48cSScott Kruger 17544776d8cSScott Krugerdef _getVarVals(findvar,testDict): 17644776d8cSScott Kruger """ 17744776d8cSScott Kruger Given: variable that is either nsize or in args 178*4d82b48cSScott Kruger Return: Values to loop over and the other arguments 179*4d82b48cSScott Kruger Note that we keep the other arguments even if they have 180*4d82b48cSScott Kruger for loops to enable stepping through all of the for lops 18144776d8cSScott Kruger """ 182*4d82b48cSScott Kruger save_vals=None 18344776d8cSScott Kruger if findvar=='nsize': 184e53dc769SScott Kruger varset=testDict[findvar] 185*4d82b48cSScott Kruger keynm,save_vals,ftype=parseLoopArgs('nsize '+varset) 18644776d8cSScott Kruger else: 18744776d8cSScott Kruger varlist=[] 18844776d8cSScott Kruger for varset in re.split('-(?=[a-zA-Z])',testDict['args']): 18944776d8cSScott Kruger if not varset.strip(): continue 190*4d82b48cSScott Kruger if '{{' not in varset: continue 191aae9f2d9SScott Kruger keyvar,vals,ftype=parseLoopArgs(varset) 192*4d82b48cSScott Kruger if keyvar==findvar: 193*4d82b48cSScott Kruger save_vals=vals 19444776d8cSScott Kruger 195*4d82b48cSScott Kruger if not save_vals: raise StandardError("Could not find separate_testvar: "+findvar) 196*4d82b48cSScott Kruger return save_vals 19744776d8cSScott Kruger 19844776d8cSScott Krugerdef genTestsSeparateTestvars(intests,indicts): 19944776d8cSScott Kruger """ 200e53dc769SScott Kruger Given: testname, sdict with 'separate_testvars 20144776d8cSScott Kruger Return: testnames,sdicts: List of generated tests 202*4d82b48cSScott Kruger The tricky part here is the {{ ... }separate output} 203*4d82b48cSScott Kruger that can be used multiple times 20444776d8cSScott Kruger """ 20544776d8cSScott Kruger testnames=[]; sdicts=[] 20644776d8cSScott Kruger for i in range(len(intests)): 20744776d8cSScott Kruger testname=intests[i]; sdict=indicts[i]; i+=1 208e53dc769SScott Kruger separate_testvars=_getSeparateTestvars(sdict) 209e53dc769SScott Kruger if len(separate_testvars)>0: 210*4d82b48cSScott Kruger sep_dicts=[sdict.copy()] 211*4d82b48cSScott Kruger if 'args' in sep_dicts[0]: 212*4d82b48cSScott Kruger sep_dicts[0]['args']=_getNewArgs(sdict['args']) 213*4d82b48cSScott Kruger sep_testnames=[testname] 214e53dc769SScott Kruger for kvar in separate_testvars: 215*4d82b48cSScott Kruger kvals=_getVarVals(kvar,sdict) 216*4d82b48cSScott Kruger 217*4d82b48cSScott Kruger # Have to do loop over previous var/val combos as well 218*4d82b48cSScott Kruger # and accumulate as we go 219*4d82b48cSScott Kruger val_testnames=[]; val_dicts=[] 22044776d8cSScott Kruger for val in kvals.split(): 221541979b6SScott Kruger gensuffix="_"+kvar+"-"+val.replace(',','__') 222*4d82b48cSScott Kruger for kvaltestnm in sep_testnames: 223*4d82b48cSScott Kruger val_testnames.append(kvaltestnm+gensuffix) 224*4d82b48cSScott Kruger for kv in sep_dicts: 225*4d82b48cSScott Kruger kvardict=kv.copy() 226*4d82b48cSScott Kruger # If the last var then we have the final version 227*4d82b48cSScott Kruger if 'suffix' in sdict: 228*4d82b48cSScott Kruger kvardict['suffix']+=gensuffix 2290bcc1aabSScott Kruger else: 2300bcc1aabSScott Kruger kvardict['suffix']=gensuffix 23144776d8cSScott Kruger if kvar=='nsize': 23244776d8cSScott Kruger kvardict[kvar]=val 23344776d8cSScott Kruger else: 234*4d82b48cSScott Kruger kvardict['args']+="-"+kvar+" "+val+" " 235*4d82b48cSScott Kruger val_dicts.append(kvardict) 236*4d82b48cSScott Kruger sep_testnames=val_testnames 237*4d82b48cSScott Kruger sep_dicts=val_dicts 238*4d82b48cSScott Kruger testnames+=sep_testnames 239*4d82b48cSScott Kruger sdicts+=sep_dicts 24044776d8cSScott Kruger else: 24144776d8cSScott Kruger testnames.append(testname) 24244776d8cSScott Kruger sdicts.append(sdict) 24344776d8cSScott Kruger return testnames,sdicts 24444776d8cSScott Kruger 24544776d8cSScott Krugerdef genTestsSubtestSuffix(testnames,sdicts): 24644776d8cSScott Kruger """ 24744776d8cSScott Kruger Given: testname, sdict with separate_testvars 24844776d8cSScott Kruger Return: testnames,sdicts: List of generated tests 24944776d8cSScott Kruger """ 25044776d8cSScott Kruger tnms=[]; sdcts=[] 25144776d8cSScott Kruger for i in range(len(testnames)): 25244776d8cSScott Kruger testname=testnames[i] 25344776d8cSScott Kruger rmsubtests=[]; keepSubtests=False 25444776d8cSScott Kruger if sdicts[i].has_key('subtests'): 25544776d8cSScott Kruger for stest in sdicts[i]["subtests"]: 25644776d8cSScott Kruger if sdicts[i][stest].has_key('suffix'): 25744776d8cSScott Kruger rmsubtests.append(stest) 25844776d8cSScott Kruger gensuffix="_"+sdicts[i][stest]['suffix'] 25944776d8cSScott Kruger newtestnm=testname+gensuffix 26044776d8cSScott Kruger tnms.append(newtestnm) 26144776d8cSScott Kruger newsdict=sdicts[i].copy() 26244776d8cSScott Kruger del newsdict['subtests'] 26344776d8cSScott Kruger # Have to hand update 26444776d8cSScott Kruger # Append 26544776d8cSScott Kruger for kup in appendlist: 26644776d8cSScott Kruger if sdicts[i][stest].has_key(kup): 26744776d8cSScott Kruger if sdicts[i].has_key(kup): 26844776d8cSScott Kruger newsdict[kup]=sdicts[i][kup]+" "+sdicts[i][stest][kup] 26944776d8cSScott Kruger else: 27044776d8cSScott Kruger newsdict[kup]=sdicts[i][stest][kup] 27144776d8cSScott Kruger # Promote 27244776d8cSScott Kruger for kup in acceptedkeys: 27344776d8cSScott Kruger if kup in appendlist: continue 27444776d8cSScott Kruger if sdicts[i][stest].has_key(kup): 27544776d8cSScott Kruger newsdict[kup]=sdicts[i][stest][kup] 27644776d8cSScott Kruger # Cleanup 27744776d8cSScott Kruger for st in sdicts[i]["subtests"]: del newsdict[st] 27844776d8cSScott Kruger sdcts.append(newsdict) 27944776d8cSScott Kruger else: 28044776d8cSScott Kruger keepSubtests=True 28144776d8cSScott Kruger else: 28244776d8cSScott Kruger tnms.append(testnames[i]) 28344776d8cSScott Kruger sdcts.append(sdicts[i]) 2840bcc1aabSScott Kruger # If a subtest without a suffix exists, then save it 28544776d8cSScott Kruger if keepSubtests: 28644776d8cSScott Kruger tnms.append(testnames[i]) 2870bcc1aabSScott Kruger newsdict=sdicts[i].copy() 2880bcc1aabSScott Kruger # Prune the tests to prepare for keeping 2890bcc1aabSScott Kruger for rmtest in rmsubtests: 2900bcc1aabSScott Kruger newsdict['subtests'].remove(rmtest) 2910bcc1aabSScott Kruger del newsdict[rmtest] 2920bcc1aabSScott Kruger sdcts.append(newsdict) 29344776d8cSScott Kruger i+=1 29444776d8cSScott Kruger return tnms,sdcts 29544776d8cSScott Kruger 29644776d8cSScott Krugerdef splitTests(testname,sdict): 29744776d8cSScott Kruger """ 29844776d8cSScott Kruger Given: testname and YAML-generated dictionary 29944776d8cSScott Kruger Return: list of names and dictionaries corresponding to each test 30044776d8cSScott Kruger given that the YAML language allows for multiple tests 30144776d8cSScott Kruger """ 30244776d8cSScott Kruger 30344776d8cSScott Kruger # Order: Parent sep_tv, subtests suffix, subtests sep_tv 30444776d8cSScott Kruger testnames,sdicts=genTestsSeparateTestvars([testname],[sdict]) 30544776d8cSScott Kruger testnames,sdicts=genTestsSubtestSuffix(testnames,sdicts) 30644776d8cSScott Kruger testnames,sdicts=genTestsSeparateTestvars(testnames,sdicts) 30744776d8cSScott Kruger 30844776d8cSScott Kruger # Because I am altering the list, I do this in passes. Inelegant 30944776d8cSScott Kruger 31044776d8cSScott Kruger return testnames, sdicts 31144776d8cSScott Kruger 3126cecdbdcSScott Krugerdef parseTest(testStr,srcfile,verbosity): 31329921a8fSScott Kruger """ 31429921a8fSScott Kruger This parses an individual test 31529921a8fSScott Kruger YAML is hierarchial so should use a state machine in the general case, 31653f2a965SBarry Smith but in practice we only support two levels of test: 31729921a8fSScott Kruger """ 31829921a8fSScott Kruger basename=os.path.basename(srcfile) 31929921a8fSScott Kruger # Handle the new at the begininng 32029921a8fSScott Kruger bn=re.sub("new_","",basename) 32129921a8fSScott Kruger # This is the default 32229921a8fSScott Kruger testname="run"+os.path.splitext(bn)[0] 32329921a8fSScott Kruger 32429921a8fSScott Kruger # Tests that have default everything (so empty effectively) 32578659935SScott Kruger if len(testStr)==0: return [testname], [{}] 32629921a8fSScott Kruger 32729921a8fSScott Kruger striptest=_stripIndent(testStr,srcfile) 32829921a8fSScott Kruger 32929921a8fSScott Kruger # go through and parse 33029921a8fSScott Kruger subtestnum=0 33129921a8fSScott Kruger subdict={} 33268a9e459SScott Kruger comments=[] 33368a9e459SScott Kruger indentlevel=0 33468a9e459SScott Kruger for ln in striptest.split("\n"): 335c4b80baaSScott Kruger line=ln.split('#')[0].rstrip() 336cadd188bSScott Kruger if verbosity>2: print(line) 3370bcc1aabSScott Kruger comment=("" if len(ln.split("#"))>0 else " ".join(ln.split("#")[1:]).strip()) 33868a9e459SScott Kruger if comment: comments.append(comment) 33929921a8fSScott Kruger if not line.strip(): continue 34044776d8cSScott Kruger lsplit=line.split(':') 34144776d8cSScott Kruger if len(lsplit)==0: raise Exception("Missing : in line: "+line) 34244776d8cSScott Kruger indentcount=lsplit[0].count(" ") 34344776d8cSScott Kruger var=lsplit[0].strip() 34440ae0433SScott Kruger val=line[line.find(':')+1:].strip() 34544776d8cSScott Kruger if not var in acceptedkeys: raise Exception("Not a defined key: "+var+" from: "+line) 34629921a8fSScott Kruger # Start by seeing if we are in a subtest 34729921a8fSScott Kruger if line.startswith(" "): 348c0658d2aSScott Kruger subdict[subtestname][var]=val 34968a9e459SScott Kruger if not indentlevel: indentlevel=indentcount 350cadd188bSScott Kruger #if indentlevel!=indentcount: print("Error in indentation:", ln) 35129921a8fSScott Kruger # Determine subtest name and make dict 35229921a8fSScott Kruger elif var=="test": 35329921a8fSScott Kruger subtestname="test"+str(subtestnum) 35429921a8fSScott Kruger subdict[subtestname]={} 35529921a8fSScott Kruger if not subdict.has_key("subtests"): subdict["subtests"]=[] 35629921a8fSScott Kruger subdict["subtests"].append(subtestname) 35729921a8fSScott Kruger subtestnum=subtestnum+1 35868a9e459SScott Kruger # The rest are easy 35929921a8fSScott Kruger else: 36044776d8cSScott Kruger # For convenience, it is sometimes convenient to list twice 36144776d8cSScott Kruger if subdict.has_key(var): 36244776d8cSScott Kruger if var in appendlist: 36344776d8cSScott Kruger subdict[var]+=" "+val 36444776d8cSScott Kruger else: 36544776d8cSScott Kruger raise Exception(var+" entered twice: "+line) 36644776d8cSScott Kruger else: 367c0658d2aSScott Kruger subdict[var]=val 36829921a8fSScott Kruger if var=="suffix": 36929921a8fSScott Kruger if len(val)>0: 37029921a8fSScott Kruger testname=testname+"_"+val 37129921a8fSScott Kruger 37268a9e459SScott Kruger if len(comments): subdict['comments']="\n".join(comments).lstrip("\n") 37344776d8cSScott Kruger # A test block can create multiple tests. This does 37444776d8cSScott Kruger # that logic 37544776d8cSScott Kruger testnames,subdicts=splitTests(testname,subdict) 37644776d8cSScott Kruger return testnames,subdicts 37729921a8fSScott Kruger 3786cecdbdcSScott Krugerdef parseTests(testStr,srcfile,fileNums,verbosity): 37929921a8fSScott Kruger """ 38029921a8fSScott Kruger Parse the yaml string describing tests and return 38129921a8fSScott Kruger a dictionary with the info in the form of: 38229921a8fSScott Kruger testDict[test][subtest] 38329921a8fSScott Kruger This is an inelegant parser as we do not wish to 38429921a8fSScott Kruger introduce a new dependency by bringing in pyyaml. 38529921a8fSScott Kruger The advantage is that validation can be done as 38629921a8fSScott Kruger it is parsed (e.g., 'test' is the only top-level node) 38729921a8fSScott Kruger """ 38829921a8fSScott Kruger 38929921a8fSScott Kruger testDict={} 39029921a8fSScott Kruger 39129921a8fSScott Kruger # The first entry should be test: but it might be indented. 39244776d8cSScott Kruger newTestStr=_stripIndent(testStr,srcfile,entireBlock=True,fileNums=fileNums) 393cadd188bSScott Kruger if verbosity>2: print(srcfile) 39429921a8fSScott Kruger 395e4653983SScott Kruger ## Check and see if we have build requirements 396e4653983SScott Kruger addToRunRequirements=None 397aec507c4SScott Kruger if "\nbuild:" in newTestStr: 398aec507c4SScott Kruger testDict['build']={} 399aec507c4SScott Kruger # The file info is already here and need to append 400aec507c4SScott Kruger Part1=newTestStr.split("build:")[1] 401aec507c4SScott Kruger fileInfo=re.split("\ntest(?:set)?:",newTestStr)[0] 402aec507c4SScott Kruger for bkey in buildkeys: 403aec507c4SScott Kruger if bkey+":" in fileInfo: 404aec507c4SScott Kruger testDict['build'][bkey]=fileInfo.split(bkey+":")[1].split("\n")[0].strip() 405aec507c4SScott Kruger #if verbosity>1: bkey+": "+testDict['build'][bkey] 406e4653983SScott Kruger # If a runtime requires are put into build, push them down to all run tests 407e4653983SScott Kruger # At this point, we are working with strings and not lists 408e4653983SScott Kruger if 'requires' in testDict['build']: 409e4653983SScott Kruger if 'datafilespath' in testDict['build']['requires']: 410e4653983SScott Kruger newreqs=re.sub('datafilespath','',testDict['build']['requires']) 411e4653983SScott Kruger testDict['build']['requires']=newreqs.strip() 412e4653983SScott Kruger addToRunRequirements='datafilespath' 413e4653983SScott Kruger 414aec507c4SScott Kruger 41529921a8fSScott Kruger # Now go through each test. First elem in split is blank 416e53dc769SScott Kruger for test in re.split("\ntest(?:set)?:",newTestStr)[1:]: 4176cecdbdcSScott Kruger testnames,subdicts=parseTest(test,srcfile,verbosity) 41844776d8cSScott Kruger for i in range(len(testnames)): 41944776d8cSScott Kruger if testDict.has_key(testnames[i]): 4201acf9037SMatthew G. Knepley raise RuntimeError("Multiple test names specified: "+testnames[i]+" in file: "+srcfile) 421e4653983SScott Kruger # Add in build requirements that need to be moved 422e4653983SScott Kruger if addToRunRequirements: 423e4653983SScott Kruger if 'requires' in subdicts[i]: 424e4653983SScott Kruger subdicts[i]['requires']+=addToRunRequirements 425e4653983SScott Kruger else: 426e4653983SScott Kruger subdicts[i]['requires']=addToRunRequirements 42744776d8cSScott Kruger testDict[testnames[i]]=subdicts[i] 42829921a8fSScott Kruger 42929921a8fSScott Kruger return testDict 43029921a8fSScott Kruger 4316cecdbdcSScott Krugerdef parseTestFile(srcfile,verbosity): 43229921a8fSScott Kruger """ 43329921a8fSScott Kruger Parse single example files and return dictionary of the form: 43429921a8fSScott Kruger testDict[srcfile][test][subtest] 43529921a8fSScott Kruger """ 43629921a8fSScott Kruger debug=False 437cadd188bSScott Kruger basename=os.path.basename(srcfile) 438cadd188bSScott Kruger if basename=='makefile': return {} 439cadd188bSScott Kruger 44029921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 44129921a8fSScott Kruger basedir=os.path.dirname(os.path.realpath(srcfile)) 44229921a8fSScott Kruger os.chdir(basedir) 44329921a8fSScott Kruger 44429921a8fSScott Kruger testDict={} 445cadd188bSScott Kruger sh=open(basename,"r"); fileStr=sh.read(); sh.close() 44629921a8fSScott Kruger 44729921a8fSScott Kruger ## Start with doing the tests 44829921a8fSScott Kruger # 44929921a8fSScott Kruger fsplit=fileStr.split("/*TEST\n")[1:] 45044776d8cSScott Kruger fstart=len(fileStr.split("/*TEST\n")[0].split("\n"))+1 45129921a8fSScott Kruger # Allow for multiple "/*TEST" blocks even though it really should be 4526f029658SMatthew G. Knepley # one 45329921a8fSScott Kruger srcTests=[] 45429921a8fSScott Kruger for t in fsplit: srcTests.append(t.split("TEST*/")[0]) 45529921a8fSScott Kruger testString=" ".join(srcTests) 45644776d8cSScott Kruger flen=len(testString.split("\n")) 45744776d8cSScott Kruger fend=fstart+flen-1 45844776d8cSScott Kruger fileNums=range(fstart,fend) 4596cecdbdcSScott Kruger testDict[basename]=parseTests(testString,srcfile,fileNums,verbosity) 460aec507c4SScott Kruger # Massage dictionary for build requirements 461aec507c4SScott Kruger if 'build' in testDict[basename]: 462aec507c4SScott Kruger testDict[basename].update(testDict[basename]['build']) 463aec507c4SScott Kruger del testDict[basename]['build'] 46429921a8fSScott Kruger 46529921a8fSScott Kruger 46629921a8fSScott Kruger os.chdir(curdir) 46729921a8fSScott Kruger return testDict 46829921a8fSScott Kruger 4696cecdbdcSScott Krugerdef parseTestDir(directory,verbosity): 47029921a8fSScott Kruger """ 47129921a8fSScott Kruger Parse single example files and return dictionary of the form: 47229921a8fSScott Kruger testDict[srcfile][test][subtest] 47329921a8fSScott Kruger """ 47429921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 47529921a8fSScott Kruger basedir=os.path.realpath(directory) 47629921a8fSScott Kruger os.chdir(basedir) 47729921a8fSScott Kruger 47829921a8fSScott Kruger tDict={} 47929921a8fSScott Kruger for test_file in glob.glob("new_ex*.*"): 4806cecdbdcSScott Kruger tDict.update(parseTestFile(test_file,verbosity)) 48129921a8fSScott Kruger 48229921a8fSScott Kruger os.chdir(curdir) 48329921a8fSScott Kruger return tDict 48429921a8fSScott Kruger 48529921a8fSScott Krugerdef printExParseDict(rDict): 48629921a8fSScott Kruger """ 48729921a8fSScott Kruger This is useful for debugging 48829921a8fSScott Kruger """ 48929921a8fSScott Kruger indent=" " 49029921a8fSScott Kruger for sfile in rDict: 491cadd188bSScott Kruger print(sfile) 49244776d8cSScott Kruger sortkeys=rDict[sfile].keys() 49344776d8cSScott Kruger sortkeys.sort() 49444776d8cSScott Kruger for runex in sortkeys: 495cadd188bSScott Kruger print(indent+runex) 49629921a8fSScott Kruger if type(rDict[sfile][runex])==types.StringType: 497cadd188bSScott Kruger print(indent*2+rDict[sfile][runex]) 49829921a8fSScott Kruger else: 49929921a8fSScott Kruger for var in rDict[sfile][runex]: 50044776d8cSScott Kruger if var.startswith("test"): continue 501cadd188bSScott Kruger print(indent*2+var+": "+str(rDict[sfile][runex][var])) 50244776d8cSScott Kruger if rDict[sfile][runex].has_key('subtests'): 50344776d8cSScott Kruger for var in rDict[sfile][runex]['subtests']: 504cadd188bSScott Kruger print(indent*2+var) 50529921a8fSScott Kruger for var2 in rDict[sfile][runex][var]: 506cadd188bSScott Kruger print(indent*3+var2+": "+str(rDict[sfile][runex][var][var2])) 507cadd188bSScott Kruger print("\n") 50829921a8fSScott Kruger return 50929921a8fSScott Kruger 51029921a8fSScott Krugerdef main(directory='',test_file='',verbosity=0): 51129921a8fSScott Kruger 51229921a8fSScott Kruger if directory: 5136cecdbdcSScott Kruger tDict=parseTestDir(directory,verbosity) 51429921a8fSScott Kruger else: 5156cecdbdcSScott Kruger tDict=parseTestFile(test_file,verbosity) 51629921a8fSScott Kruger if verbosity>0: printExParseDict(tDict) 51729921a8fSScott Kruger 51829921a8fSScott Kruger return 51929921a8fSScott Kruger 52029921a8fSScott Krugerif __name__ == '__main__': 52129921a8fSScott Kruger import optparse 52229921a8fSScott Kruger parser = optparse.OptionParser() 52329921a8fSScott Kruger parser.add_option('-d', '--directory', dest='directory', 52429921a8fSScott Kruger default="", help='Directory containing files to parse') 52529921a8fSScott Kruger parser.add_option('-t', '--test_file', dest='test_file', 52629921a8fSScott Kruger default="", help='Test file, e.g., ex1.c, to parse') 52729921a8fSScott Kruger parser.add_option('-v', '--verbosity', dest='verbosity', 52829921a8fSScott Kruger help='Verbosity of output by level: 1, 2, or 3', default=0) 52929921a8fSScott Kruger opts, extra_args = parser.parse_args() 53029921a8fSScott Kruger 53129921a8fSScott Kruger if extra_args: 53229921a8fSScott Kruger import sys 53329921a8fSScott Kruger sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args)) 53429921a8fSScott Kruger exit(1) 53529921a8fSScott Kruger if not opts.test_file and not opts.directory: 536cadd188bSScott Kruger print("test file or directory is required") 53729921a8fSScott Kruger parser.print_usage() 53829921a8fSScott Kruger sys.exit() 53929921a8fSScott Kruger 54029921a8fSScott Kruger # Need verbosity to be an integer 54129921a8fSScott Kruger try: 54229921a8fSScott Kruger verbosity=int(opts.verbosity) 54329921a8fSScott Kruger except: 54429921a8fSScott Kruger raise Exception("Error: Verbosity must be integer") 54529921a8fSScott Kruger 54629921a8fSScott Kruger main(directory=opts.directory,test_file=opts.test_file,verbosity=verbosity) 547