129921a8fSScott Kruger#!/usr/bin/env python 229921a8fSScott Kruger""" 329921a8fSScott KrugerParse the test file and return a dictionary. 429921a8fSScott Kruger 529921a8fSScott KrugerQuick usage:: 629921a8fSScott Kruger 7c3a89c15SBarry Smith lib/petsc/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""" 375b6bfdb9SJed Brownfrom __future__ import print_function 3829921a8fSScott Kruger 3929921a8fSScott Krugerimport os, re, glob, types 4029921a8fSScott Krugerimport sys 4129921a8fSScott Krugerimport logging 4229921a8fSScott Krugersys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) 4329921a8fSScott Kruger 4429921a8fSScott Krugerimport inspect 4529921a8fSScott Krugerthisscriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 4629921a8fSScott Krugermaintdir=os.path.join(os.path.join(os.path.dirname(thisscriptdir),'bin'),'maint') 4729921a8fSScott Krugersys.path.insert(0,maintdir) 4829921a8fSScott Kruger 4929921a8fSScott Kruger# These are special keys describing build 5029921a8fSScott Krugerbuildkeys="requires TODO SKIP depends".split() 5129921a8fSScott Kruger 520a091e3eSScott Krugeracceptedkeys="test nsize requires command suffix args filter filter_output localrunfiles comments TODO SKIP output_file timeoutfactor".split() 5344776d8cSScott Krugerappendlist="args requires comments".split() 5468a9e459SScott Kruger 5568a9e459SScott Krugerimport re 5668a9e459SScott Kruger 5744776d8cSScott Krugerdef _stripIndent(block,srcfile,entireBlock=False,fileNums=[]): 5829921a8fSScott Kruger """ 5929921a8fSScott Kruger Go through and remove a level of indentation 6029921a8fSScott Kruger Also strip of trailing whitespace 6129921a8fSScott Kruger """ 6229921a8fSScott Kruger # The first entry should be test: but it might be indented. 6329921a8fSScott Kruger ext=os.path.splitext(srcfile)[1] 6444776d8cSScott Kruger stripstr=" " 6566db876fSScott Kruger if len(fileNums)>0: lineNum=fileNums[0]-1 668ccd5183SScott Kruger for lline in block.split("\n"): 6766db876fSScott Kruger if len(fileNums)>0: lineNum+=1 688ccd5183SScott Kruger line=lline[1:] if lline.startswith("!") else lline 6929921a8fSScott Kruger if not line.strip(): continue 70c4b80baaSScott Kruger if line.strip().startswith('#'): continue 7144776d8cSScott Kruger if entireBlock: 7244776d8cSScott Kruger var=line.split(":")[0].strip() 73aec507c4SScott Kruger if not var in ['test','testset','build']: 7466db876fSScott Kruger raise Exception("Formatting error: Cannot find test in file: "+srcfile+" at line: "+str(lineNum)+"\n") 7529921a8fSScott Kruger nspace=len(line)-len(line.lstrip(stripstr)) 7629921a8fSScott Kruger newline=line[nspace:] 7729921a8fSScott Kruger break 7829921a8fSScott Kruger 7929921a8fSScott Kruger # Strip off any indentation for the whole string and any trailing 8029921a8fSScott Kruger # whitespace for convenience 8129921a8fSScott Kruger newTestStr="\n" 8244776d8cSScott Kruger if len(fileNums)>0: lineNum=fileNums[0]-1 8344776d8cSScott Kruger firstPass=True 848ccd5183SScott Kruger for lline in block.split("\n"): 8544776d8cSScott Kruger if len(fileNums)>0: lineNum+=1 868ccd5183SScott Kruger line=lline[1:] if lline.startswith("!") else lline 8729921a8fSScott Kruger if not line.strip(): continue 88c4b80baaSScott Kruger if line.strip().startswith('#'): 89c4b80baaSScott Kruger newTestStr+=line+'\n' 90c4b80baaSScott Kruger else: 9129921a8fSScott Kruger newline=line[nspace:] 92c4b80baaSScott Kruger newTestStr+=newline.rstrip()+"\n" 9344776d8cSScott Kruger # Do some basic indentation checks 9444776d8cSScott Kruger if entireBlock: 9544776d8cSScott Kruger # Don't need to check comment lines 9644776d8cSScott Kruger if line.strip().startswith('#'): continue 9744776d8cSScott Kruger if not newline.startswith(" "): 9844776d8cSScott Kruger var=newline.split(":")[0].strip() 99aec507c4SScott Kruger if not var in ['test','testset','build']: 10044776d8cSScott Kruger err="Formatting error in file "+srcfile+" at line: " +line+"\n" 10144776d8cSScott Kruger if len(fileNums)>0: 10244776d8cSScott Kruger raise Exception(err+"Check indentation at line number: "+str(lineNum)) 10344776d8cSScott Kruger else: 10444776d8cSScott Kruger raise Exception(err) 10544776d8cSScott Kruger else: 10644776d8cSScott Kruger var=line.split(":")[0].strip() 107aec507c4SScott Kruger if var in ['test','testset','build']: 10844776d8cSScott Kruger subnspace=len(line)-len(line.lstrip(stripstr)) 10944776d8cSScott Kruger if firstPass: 11044776d8cSScott Kruger firstsubnspace=subnspace 11144776d8cSScott Kruger firstPass=False 11244776d8cSScott Kruger else: 11344776d8cSScott Kruger if firstsubnspace!=subnspace: 11444776d8cSScott Kruger err="Formatting subtest error in file "+srcfile+" at line: " +line+"\n" 11544776d8cSScott Kruger if len(fileNums)>0: 11644776d8cSScott Kruger raise Exception(err+"Check indentation at line number: "+str(lineNum)) 11744776d8cSScott Kruger else: 11844776d8cSScott Kruger raise Exception(err) 11944776d8cSScott Kruger 12043c6e11bSMatthew G. Knepley # Allow line continuation character '\' 12143c6e11bSMatthew G. Knepley return newTestStr.replace('\\\n', ' ') 12229921a8fSScott Kruger 123aae9f2d9SScott Krugerdef parseLoopArgs(varset): 12444776d8cSScott Kruger """ 125aae9f2d9SScott Kruger Given: String containing loop variables 126aae9f2d9SScott Kruger Return: tuple containing separate/shared and string of loop vars 12744776d8cSScott Kruger """ 128a449fbaeSJed Brown keynm=varset.split("{{")[0].strip().lstrip('-') 129aae9f2d9SScott Kruger if not keynm.strip(): keynm='nsize' 130aae9f2d9SScott Kruger lvars=varset.split('{{')[1].split('}')[0] 131aae9f2d9SScott Kruger suffx=varset.split('{{')[1].split('}')[1] 132aae9f2d9SScott Kruger ftype='separate' if suffx.startswith('separate') else 'shared' 133aae9f2d9SScott Kruger return keynm,lvars,ftype 13444776d8cSScott Kruger 135e53dc769SScott Krugerdef _getSeparateTestvars(testDict): 136e53dc769SScott Kruger """ 137e53dc769SScott Kruger Given: dictionary that may have 138e53dc769SScott Kruger Return: Variables that cause a test split 139e53dc769SScott Kruger """ 140e53dc769SScott Kruger vals=None 141e53dc769SScott Kruger sepvars=[] 142e53dc769SScott Kruger # Check nsize 1435b6bfdb9SJed Brown if 'nsize' in testDict: 144e53dc769SScott Kruger varset=testDict['nsize'] 145aae9f2d9SScott Kruger if '{{' in varset: 146aae9f2d9SScott Kruger keynm,lvars,ftype=parseLoopArgs(varset) 147aae9f2d9SScott Kruger if ftype=='separate': sepvars.append(keynm) 148e53dc769SScott Kruger 149e53dc769SScott Kruger # Now check args 1505b6bfdb9SJed Brown if 'args' not in testDict: return sepvars 151*d87d9516SStefano Zampini for varset in re.split('(^|\W)-(?=[a-zA-Z])',testDict['args']): 152e53dc769SScott Kruger if not varset.strip(): continue 153aae9f2d9SScott Kruger if '{{' in varset: 154e53dc769SScott Kruger # Assuming only one for loop per var specification 155aae9f2d9SScott Kruger keynm,lvars,ftype=parseLoopArgs(varset) 156aae9f2d9SScott Kruger if ftype=='separate': sepvars.append(keynm) 157e53dc769SScott Kruger 158e53dc769SScott Kruger return sepvars 159e53dc769SScott Kruger 1604d82b48cSScott Krugerdef _getNewArgs(args): 1614d82b48cSScott Kruger """ 1624d82b48cSScott Kruger Given: String that has args that might have loops in them 1634d82b48cSScott Kruger Return: All of the arguments/values that do not have 1644d82b48cSScott Kruger for 'separate output' in for loops 1654d82b48cSScott Kruger """ 1664d82b48cSScott Kruger newargs='' 1674d82b48cSScott Kruger if not args.strip(): return args 168*d87d9516SStefano Zampini for varset in re.split('(^|\W)-(?=[a-zA-Z])',args): 1694d82b48cSScott Kruger if not varset.strip(): continue 1704d82b48cSScott Kruger if '{{' not in varset: 1714d82b48cSScott Kruger if 'separate' not in varset: 1724d82b48cSScott Kruger newargs+="-"+varset.strip()+" " 1734d82b48cSScott Kruger 1744d82b48cSScott Kruger return newargs 1754d82b48cSScott Kruger 17644776d8cSScott Krugerdef _getVarVals(findvar,testDict): 17744776d8cSScott Kruger """ 17844776d8cSScott Kruger Given: variable that is either nsize or in args 1794d82b48cSScott Kruger Return: Values to loop over and the other arguments 1804d82b48cSScott Kruger Note that we keep the other arguments even if they have 1814d82b48cSScott Kruger for loops to enable stepping through all of the for lops 18244776d8cSScott Kruger """ 1834d82b48cSScott Kruger save_vals=None 18444776d8cSScott Kruger if findvar=='nsize': 185e53dc769SScott Kruger varset=testDict[findvar] 1864d82b48cSScott Kruger keynm,save_vals,ftype=parseLoopArgs('nsize '+varset) 18744776d8cSScott Kruger else: 18844776d8cSScott Kruger varlist=[] 18944776d8cSScott Kruger for varset in re.split('-(?=[a-zA-Z])',testDict['args']): 19044776d8cSScott Kruger if not varset.strip(): continue 1914d82b48cSScott Kruger if '{{' not in varset: continue 192aae9f2d9SScott Kruger keyvar,vals,ftype=parseLoopArgs(varset) 1934d82b48cSScott Kruger if keyvar==findvar: 1944d82b48cSScott Kruger save_vals=vals 19544776d8cSScott Kruger 1965b6bfdb9SJed Brown if not save_vals: raise Exception("Could not find separate_testvar: "+findvar) 1974d82b48cSScott Kruger return save_vals 19844776d8cSScott Kruger 1994f8a0bffSScott Krugerdef genTestsSeparateTestvars(intests,indicts,final=False): 20044776d8cSScott Kruger """ 201e53dc769SScott Kruger Given: testname, sdict with 'separate_testvars 20244776d8cSScott Kruger Return: testnames,sdicts: List of generated tests 2034d82b48cSScott Kruger The tricky part here is the {{ ... }separate output} 2044d82b48cSScott Kruger that can be used multiple times 20544776d8cSScott Kruger """ 20644776d8cSScott Kruger testnames=[]; sdicts=[] 20744776d8cSScott Kruger for i in range(len(intests)): 20844776d8cSScott Kruger testname=intests[i]; sdict=indicts[i]; i+=1 209e53dc769SScott Kruger separate_testvars=_getSeparateTestvars(sdict) 210e53dc769SScott Kruger if len(separate_testvars)>0: 2114d82b48cSScott Kruger sep_dicts=[sdict.copy()] 2124d82b48cSScott Kruger if 'args' in sep_dicts[0]: 2134d82b48cSScott Kruger sep_dicts[0]['args']=_getNewArgs(sdict['args']) 2144d82b48cSScott Kruger sep_testnames=[testname] 215e53dc769SScott Kruger for kvar in separate_testvars: 2164d82b48cSScott Kruger kvals=_getVarVals(kvar,sdict) 2174d82b48cSScott Kruger 2184d82b48cSScott Kruger # Have to do loop over previous var/val combos as well 2194d82b48cSScott Kruger # and accumulate as we go 2204d82b48cSScott Kruger val_testnames=[]; val_dicts=[] 22144776d8cSScott Kruger for val in kvals.split(): 222541979b6SScott Kruger gensuffix="_"+kvar+"-"+val.replace(',','__') 2234d82b48cSScott Kruger for kvaltestnm in sep_testnames: 2244d82b48cSScott Kruger val_testnames.append(kvaltestnm+gensuffix) 2254d82b48cSScott Kruger for kv in sep_dicts: 2264d82b48cSScott Kruger kvardict=kv.copy() 2274d82b48cSScott Kruger # If the last var then we have the final version 2284d82b48cSScott Kruger if 'suffix' in sdict: 2294d82b48cSScott Kruger kvardict['suffix']+=gensuffix 2300bcc1aabSScott Kruger else: 2310bcc1aabSScott Kruger kvardict['suffix']=gensuffix 23244776d8cSScott Kruger if kvar=='nsize': 23344776d8cSScott Kruger kvardict[kvar]=val 23444776d8cSScott Kruger else: 2354d82b48cSScott Kruger kvardict['args']+="-"+kvar+" "+val+" " 2364d82b48cSScott Kruger val_dicts.append(kvardict) 2374d82b48cSScott Kruger sep_testnames=val_testnames 2384d82b48cSScott Kruger sep_dicts=val_dicts 2394d82b48cSScott Kruger testnames+=sep_testnames 2404d82b48cSScott Kruger sdicts+=sep_dicts 24144776d8cSScott Kruger else: 2424f8a0bffSScott Kruger # These are plain vanilla tests (no subtests, no loops) that 2434f8a0bffSScott Kruger # do not have a suffix. This makes the targets match up with 2444f8a0bffSScott Kruger # the output file (testname_1.out) 2454f8a0bffSScott Kruger if final: 2464f8a0bffSScott Kruger if '_' not in testname: testname+='_1' 24744776d8cSScott Kruger testnames.append(testname) 24844776d8cSScott Kruger sdicts.append(sdict) 24944776d8cSScott Kruger return testnames,sdicts 25044776d8cSScott Kruger 25144776d8cSScott Krugerdef genTestsSubtestSuffix(testnames,sdicts): 25244776d8cSScott Kruger """ 25344776d8cSScott Kruger Given: testname, sdict with separate_testvars 25444776d8cSScott Kruger Return: testnames,sdicts: List of generated tests 25544776d8cSScott Kruger """ 25644776d8cSScott Kruger tnms=[]; sdcts=[] 25744776d8cSScott Kruger for i in range(len(testnames)): 25844776d8cSScott Kruger testname=testnames[i] 25944776d8cSScott Kruger rmsubtests=[]; keepSubtests=False 2605b6bfdb9SJed Brown if 'subtests' in sdicts[i]: 26144776d8cSScott Kruger for stest in sdicts[i]["subtests"]: 2625b6bfdb9SJed Brown if 'suffix' in sdicts[i][stest]: 26344776d8cSScott Kruger rmsubtests.append(stest) 26444776d8cSScott Kruger gensuffix="_"+sdicts[i][stest]['suffix'] 26544776d8cSScott Kruger newtestnm=testname+gensuffix 26644776d8cSScott Kruger tnms.append(newtestnm) 26744776d8cSScott Kruger newsdict=sdicts[i].copy() 26844776d8cSScott Kruger del newsdict['subtests'] 26944776d8cSScott Kruger # Have to hand update 27044776d8cSScott Kruger # Append 27144776d8cSScott Kruger for kup in appendlist: 2725b6bfdb9SJed Brown if kup in sdicts[i][stest]: 2735b6bfdb9SJed Brown if kup in sdicts[i]: 27444776d8cSScott Kruger newsdict[kup]=sdicts[i][kup]+" "+sdicts[i][stest][kup] 27544776d8cSScott Kruger else: 27644776d8cSScott Kruger newsdict[kup]=sdicts[i][stest][kup] 27744776d8cSScott Kruger # Promote 27844776d8cSScott Kruger for kup in acceptedkeys: 27944776d8cSScott Kruger if kup in appendlist: continue 2805b6bfdb9SJed Brown if kup in sdicts[i][stest]: 28144776d8cSScott Kruger newsdict[kup]=sdicts[i][stest][kup] 28244776d8cSScott Kruger # Cleanup 28344776d8cSScott Kruger for st in sdicts[i]["subtests"]: del newsdict[st] 28444776d8cSScott Kruger sdcts.append(newsdict) 28544776d8cSScott Kruger else: 28644776d8cSScott Kruger keepSubtests=True 28744776d8cSScott Kruger else: 28844776d8cSScott Kruger tnms.append(testnames[i]) 28944776d8cSScott Kruger sdcts.append(sdicts[i]) 2900bcc1aabSScott Kruger # If a subtest without a suffix exists, then save it 29144776d8cSScott Kruger if keepSubtests: 29244776d8cSScott Kruger tnms.append(testnames[i]) 2930bcc1aabSScott Kruger newsdict=sdicts[i].copy() 2940bcc1aabSScott Kruger # Prune the tests to prepare for keeping 2950bcc1aabSScott Kruger for rmtest in rmsubtests: 2960bcc1aabSScott Kruger newsdict['subtests'].remove(rmtest) 2970bcc1aabSScott Kruger del newsdict[rmtest] 2980bcc1aabSScott Kruger sdcts.append(newsdict) 29944776d8cSScott Kruger i+=1 30044776d8cSScott Kruger return tnms,sdcts 30144776d8cSScott Kruger 30244776d8cSScott Krugerdef splitTests(testname,sdict): 30344776d8cSScott Kruger """ 30444776d8cSScott Kruger Given: testname and YAML-generated dictionary 30544776d8cSScott Kruger Return: list of names and dictionaries corresponding to each test 30644776d8cSScott Kruger given that the YAML language allows for multiple tests 30744776d8cSScott Kruger """ 30844776d8cSScott Kruger 30944776d8cSScott Kruger # Order: Parent sep_tv, subtests suffix, subtests sep_tv 31044776d8cSScott Kruger testnames,sdicts=genTestsSeparateTestvars([testname],[sdict]) 31144776d8cSScott Kruger testnames,sdicts=genTestsSubtestSuffix(testnames,sdicts) 3124f8a0bffSScott Kruger testnames,sdicts=genTestsSeparateTestvars(testnames,sdicts,final=True) 31344776d8cSScott Kruger 31444776d8cSScott Kruger # Because I am altering the list, I do this in passes. Inelegant 31544776d8cSScott Kruger 31644776d8cSScott Kruger return testnames, sdicts 31744776d8cSScott Kruger 3186cecdbdcSScott Krugerdef parseTest(testStr,srcfile,verbosity): 31929921a8fSScott Kruger """ 32029921a8fSScott Kruger This parses an individual test 32129921a8fSScott Kruger YAML is hierarchial so should use a state machine in the general case, 32253f2a965SBarry Smith but in practice we only support two levels of test: 32329921a8fSScott Kruger """ 32429921a8fSScott Kruger basename=os.path.basename(srcfile) 32529921a8fSScott Kruger # Handle the new at the begininng 32629921a8fSScott Kruger bn=re.sub("new_","",basename) 32729921a8fSScott Kruger # This is the default 32829921a8fSScott Kruger testname="run"+os.path.splitext(bn)[0] 32929921a8fSScott Kruger 33029921a8fSScott Kruger # Tests that have default everything (so empty effectively) 33178659935SScott Kruger if len(testStr)==0: return [testname], [{}] 33229921a8fSScott Kruger 33329921a8fSScott Kruger striptest=_stripIndent(testStr,srcfile) 33429921a8fSScott Kruger 33529921a8fSScott Kruger # go through and parse 33629921a8fSScott Kruger subtestnum=0 33729921a8fSScott Kruger subdict={} 33868a9e459SScott Kruger comments=[] 33968a9e459SScott Kruger indentlevel=0 34068a9e459SScott Kruger for ln in striptest.split("\n"): 341c4b80baaSScott Kruger line=ln.split('#')[0].rstrip() 342cadd188bSScott Kruger if verbosity>2: print(line) 3430bcc1aabSScott Kruger comment=("" if len(ln.split("#"))>0 else " ".join(ln.split("#")[1:]).strip()) 34468a9e459SScott Kruger if comment: comments.append(comment) 34529921a8fSScott Kruger if not line.strip(): continue 34644776d8cSScott Kruger lsplit=line.split(':') 34744776d8cSScott Kruger if len(lsplit)==0: raise Exception("Missing : in line: "+line) 34844776d8cSScott Kruger indentcount=lsplit[0].count(" ") 34944776d8cSScott Kruger var=lsplit[0].strip() 35040ae0433SScott Kruger val=line[line.find(':')+1:].strip() 35144776d8cSScott Kruger if not var in acceptedkeys: raise Exception("Not a defined key: "+var+" from: "+line) 35229921a8fSScott Kruger # Start by seeing if we are in a subtest 35329921a8fSScott Kruger if line.startswith(" "): 354ecc1beb5SScott Kruger if var in subdict[subtestname]: 355ecc1beb5SScott Kruger subdict[subtestname][var]+=" "+val 356ecc1beb5SScott Kruger else: 357c0658d2aSScott Kruger subdict[subtestname][var]=val 35868a9e459SScott Kruger if not indentlevel: indentlevel=indentcount 359cadd188bSScott Kruger #if indentlevel!=indentcount: print("Error in indentation:", ln) 36029921a8fSScott Kruger # Determine subtest name and make dict 36129921a8fSScott Kruger elif var=="test": 36229921a8fSScott Kruger subtestname="test"+str(subtestnum) 36329921a8fSScott Kruger subdict[subtestname]={} 3645b6bfdb9SJed Brown if "subtests" not in subdict: subdict["subtests"]=[] 36529921a8fSScott Kruger subdict["subtests"].append(subtestname) 36629921a8fSScott Kruger subtestnum=subtestnum+1 36768a9e459SScott Kruger # The rest are easy 36829921a8fSScott Kruger else: 36944776d8cSScott Kruger # For convenience, it is sometimes convenient to list twice 3705b6bfdb9SJed Brown if var in subdict: 37144776d8cSScott Kruger if var in appendlist: 37244776d8cSScott Kruger subdict[var]+=" "+val 37344776d8cSScott Kruger else: 37444776d8cSScott Kruger raise Exception(var+" entered twice: "+line) 37544776d8cSScott Kruger else: 376c0658d2aSScott Kruger subdict[var]=val 37729921a8fSScott Kruger if var=="suffix": 37829921a8fSScott Kruger if len(val)>0: 3794f8a0bffSScott Kruger testname+="_"+val 38029921a8fSScott Kruger 38168a9e459SScott Kruger if len(comments): subdict['comments']="\n".join(comments).lstrip("\n") 3824f8a0bffSScott Kruger 3834f8a0bffSScott Kruger # A test block can create multiple tests. This does that logic 38444776d8cSScott Kruger testnames,subdicts=splitTests(testname,subdict) 38544776d8cSScott Kruger return testnames,subdicts 38629921a8fSScott Kruger 3876cecdbdcSScott Krugerdef parseTests(testStr,srcfile,fileNums,verbosity): 38829921a8fSScott Kruger """ 38929921a8fSScott Kruger Parse the yaml string describing tests and return 39029921a8fSScott Kruger a dictionary with the info in the form of: 39129921a8fSScott Kruger testDict[test][subtest] 39229921a8fSScott Kruger This is an inelegant parser as we do not wish to 39329921a8fSScott Kruger introduce a new dependency by bringing in pyyaml. 39429921a8fSScott Kruger The advantage is that validation can be done as 39529921a8fSScott Kruger it is parsed (e.g., 'test' is the only top-level node) 39629921a8fSScott Kruger """ 39729921a8fSScott Kruger 39829921a8fSScott Kruger testDict={} 39929921a8fSScott Kruger 40029921a8fSScott Kruger # The first entry should be test: but it might be indented. 40144776d8cSScott Kruger newTestStr=_stripIndent(testStr,srcfile,entireBlock=True,fileNums=fileNums) 402cadd188bSScott Kruger if verbosity>2: print(srcfile) 40329921a8fSScott Kruger 404e4653983SScott Kruger ## Check and see if we have build requirements 405e4653983SScott Kruger addToRunRequirements=None 406aec507c4SScott Kruger if "\nbuild:" in newTestStr: 407aec507c4SScott Kruger testDict['build']={} 408aec507c4SScott Kruger # The file info is already here and need to append 409aec507c4SScott Kruger Part1=newTestStr.split("build:")[1] 410aec507c4SScott Kruger fileInfo=re.split("\ntest(?:set)?:",newTestStr)[0] 411aec507c4SScott Kruger for bkey in buildkeys: 412aec507c4SScott Kruger if bkey+":" in fileInfo: 413aec507c4SScott Kruger testDict['build'][bkey]=fileInfo.split(bkey+":")[1].split("\n")[0].strip() 414aec507c4SScott Kruger #if verbosity>1: bkey+": "+testDict['build'][bkey] 415e4653983SScott Kruger # If a runtime requires are put into build, push them down to all run tests 416e4653983SScott Kruger # At this point, we are working with strings and not lists 417e4653983SScott Kruger if 'requires' in testDict['build']: 418e4653983SScott Kruger if 'datafilespath' in testDict['build']['requires']: 419e4653983SScott Kruger newreqs=re.sub('datafilespath','',testDict['build']['requires']) 420e4653983SScott Kruger testDict['build']['requires']=newreqs.strip() 421e4653983SScott Kruger addToRunRequirements='datafilespath' 422e4653983SScott Kruger 423aec507c4SScott Kruger 42429921a8fSScott Kruger # Now go through each test. First elem in split is blank 425e53dc769SScott Kruger for test in re.split("\ntest(?:set)?:",newTestStr)[1:]: 4266cecdbdcSScott Kruger testnames,subdicts=parseTest(test,srcfile,verbosity) 42744776d8cSScott Kruger for i in range(len(testnames)): 4285b6bfdb9SJed Brown if testnames[i] in testDict: 4291acf9037SMatthew G. Knepley raise RuntimeError("Multiple test names specified: "+testnames[i]+" in file: "+srcfile) 430e4653983SScott Kruger # Add in build requirements that need to be moved 431e4653983SScott Kruger if addToRunRequirements: 432e4653983SScott Kruger if 'requires' in subdicts[i]: 433e4653983SScott Kruger subdicts[i]['requires']+=addToRunRequirements 434e4653983SScott Kruger else: 435e4653983SScott Kruger subdicts[i]['requires']=addToRunRequirements 43644776d8cSScott Kruger testDict[testnames[i]]=subdicts[i] 43729921a8fSScott Kruger 43829921a8fSScott Kruger return testDict 43929921a8fSScott Kruger 4406cecdbdcSScott Krugerdef parseTestFile(srcfile,verbosity): 44129921a8fSScott Kruger """ 44229921a8fSScott Kruger Parse single example files and return dictionary of the form: 44329921a8fSScott Kruger testDict[srcfile][test][subtest] 44429921a8fSScott Kruger """ 44529921a8fSScott Kruger debug=False 446cadd188bSScott Kruger basename=os.path.basename(srcfile) 447cadd188bSScott Kruger if basename=='makefile': return {} 448cadd188bSScott Kruger 44929921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 45029921a8fSScott Kruger basedir=os.path.dirname(os.path.realpath(srcfile)) 45129921a8fSScott Kruger os.chdir(basedir) 45229921a8fSScott Kruger 45329921a8fSScott Kruger testDict={} 454cadd188bSScott Kruger sh=open(basename,"r"); fileStr=sh.read(); sh.close() 45529921a8fSScott Kruger 45629921a8fSScott Kruger ## Start with doing the tests 45729921a8fSScott Kruger # 45829921a8fSScott Kruger fsplit=fileStr.split("/*TEST\n")[1:] 45944776d8cSScott Kruger fstart=len(fileStr.split("/*TEST\n")[0].split("\n"))+1 46029921a8fSScott Kruger # Allow for multiple "/*TEST" blocks even though it really should be 4616f029658SMatthew G. Knepley # one 46229921a8fSScott Kruger srcTests=[] 46329921a8fSScott Kruger for t in fsplit: srcTests.append(t.split("TEST*/")[0]) 46429921a8fSScott Kruger testString=" ".join(srcTests) 46544776d8cSScott Kruger flen=len(testString.split("\n")) 46644776d8cSScott Kruger fend=fstart+flen-1 46744776d8cSScott Kruger fileNums=range(fstart,fend) 4686cecdbdcSScott Kruger testDict[basename]=parseTests(testString,srcfile,fileNums,verbosity) 469aec507c4SScott Kruger # Massage dictionary for build requirements 470aec507c4SScott Kruger if 'build' in testDict[basename]: 471aec507c4SScott Kruger testDict[basename].update(testDict[basename]['build']) 472aec507c4SScott Kruger del testDict[basename]['build'] 47329921a8fSScott Kruger 47429921a8fSScott Kruger 47529921a8fSScott Kruger os.chdir(curdir) 47629921a8fSScott Kruger return testDict 47729921a8fSScott Kruger 4786cecdbdcSScott Krugerdef parseTestDir(directory,verbosity): 47929921a8fSScott Kruger """ 48029921a8fSScott Kruger Parse single example files and return dictionary of the form: 48129921a8fSScott Kruger testDict[srcfile][test][subtest] 48229921a8fSScott Kruger """ 48329921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 48429921a8fSScott Kruger basedir=os.path.realpath(directory) 48529921a8fSScott Kruger os.chdir(basedir) 48629921a8fSScott Kruger 48729921a8fSScott Kruger tDict={} 48809a6cbfcSBernhard M. Wiedemann for test_file in sorted(glob.glob("new_ex*.*")): 4896cecdbdcSScott Kruger tDict.update(parseTestFile(test_file,verbosity)) 49029921a8fSScott Kruger 49129921a8fSScott Kruger os.chdir(curdir) 49229921a8fSScott Kruger return tDict 49329921a8fSScott Kruger 49429921a8fSScott Krugerdef printExParseDict(rDict): 49529921a8fSScott Kruger """ 49629921a8fSScott Kruger This is useful for debugging 49729921a8fSScott Kruger """ 49829921a8fSScott Kruger indent=" " 49929921a8fSScott Kruger for sfile in rDict: 500cadd188bSScott Kruger print(sfile) 501c3d83d22SScott Kruger sortkeys=list(rDict[sfile].keys()) 50244776d8cSScott Kruger sortkeys.sort() 50344776d8cSScott Kruger for runex in sortkeys: 504cadd188bSScott Kruger print(indent+runex) 5055b6bfdb9SJed Brown if type(rDict[sfile][runex])==bytes: 506cadd188bSScott Kruger print(indent*2+rDict[sfile][runex]) 50729921a8fSScott Kruger else: 50829921a8fSScott Kruger for var in rDict[sfile][runex]: 50944776d8cSScott Kruger if var.startswith("test"): continue 510cadd188bSScott Kruger print(indent*2+var+": "+str(rDict[sfile][runex][var])) 5115b6bfdb9SJed Brown if 'subtests' in rDict[sfile][runex]: 51244776d8cSScott Kruger for var in rDict[sfile][runex]['subtests']: 513cadd188bSScott Kruger print(indent*2+var) 51429921a8fSScott Kruger for var2 in rDict[sfile][runex][var]: 515cadd188bSScott Kruger print(indent*3+var2+": "+str(rDict[sfile][runex][var][var2])) 516cadd188bSScott Kruger print("\n") 51729921a8fSScott Kruger return 51829921a8fSScott Kruger 51929921a8fSScott Krugerdef main(directory='',test_file='',verbosity=0): 52029921a8fSScott Kruger 52129921a8fSScott Kruger if directory: 5226cecdbdcSScott Kruger tDict=parseTestDir(directory,verbosity) 52329921a8fSScott Kruger else: 5246cecdbdcSScott Kruger tDict=parseTestFile(test_file,verbosity) 52529921a8fSScott Kruger if verbosity>0: printExParseDict(tDict) 52629921a8fSScott Kruger 52729921a8fSScott Kruger return 52829921a8fSScott Kruger 52929921a8fSScott Krugerif __name__ == '__main__': 53029921a8fSScott Kruger import optparse 53129921a8fSScott Kruger parser = optparse.OptionParser() 53229921a8fSScott Kruger parser.add_option('-d', '--directory', dest='directory', 53329921a8fSScott Kruger default="", help='Directory containing files to parse') 53429921a8fSScott Kruger parser.add_option('-t', '--test_file', dest='test_file', 53529921a8fSScott Kruger default="", help='Test file, e.g., ex1.c, to parse') 53629921a8fSScott Kruger parser.add_option('-v', '--verbosity', dest='verbosity', 53729921a8fSScott Kruger help='Verbosity of output by level: 1, 2, or 3', default=0) 53829921a8fSScott Kruger opts, extra_args = parser.parse_args() 53929921a8fSScott Kruger 54029921a8fSScott Kruger if extra_args: 54129921a8fSScott Kruger import sys 54229921a8fSScott Kruger sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args)) 54329921a8fSScott Kruger exit(1) 54429921a8fSScott Kruger if not opts.test_file and not opts.directory: 545cadd188bSScott Kruger print("test file or directory is required") 54629921a8fSScott Kruger parser.print_usage() 54729921a8fSScott Kruger sys.exit() 54829921a8fSScott Kruger 54929921a8fSScott Kruger # Need verbosity to be an integer 55029921a8fSScott Kruger try: 55129921a8fSScott Kruger verbosity=int(opts.verbosity) 55229921a8fSScott Kruger except: 55329921a8fSScott Kruger raise Exception("Error: Verbosity must be integer") 55429921a8fSScott Kruger 55529921a8fSScott Kruger main(directory=opts.directory,test_file=opts.test_file,verbosity=verbosity) 556