129921a8fSScott Kruger#!/usr/bin/env python 229921a8fSScott Kruger""" 329921a8fSScott KrugerParse the test file and return a dictionary. 429921a8fSScott Kruger 529921a8fSScott KrugerQuick usage:: 629921a8fSScott Kruger 7c4762a1bSJed Brown lib/petsc/bin/maint/testparse.py -t src/ksp/ksp/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 575e361860SScott Krugerdef getDefaultOutputFileRoot(testname): 585e361860SScott Kruger """ 595e361860SScott Kruger Given testname, give DefaultRoot and DefaultOutputFilename 605e361860SScott Kruger e.g., runex1 gives ex1_1, output/ex1_1.out 615e361860SScott Kruger """ 625e361860SScott Kruger defroot=(re.sub("run","",testname) if testname.startswith("run") else testname) 635e361860SScott Kruger if not "_" in defroot: defroot=defroot+"_1" 645e361860SScott Kruger return defroot 655e361860SScott Kruger 6644776d8cSScott Krugerdef _stripIndent(block,srcfile,entireBlock=False,fileNums=[]): 6729921a8fSScott Kruger """ 6829921a8fSScott Kruger Go through and remove a level of indentation 6929921a8fSScott Kruger Also strip of trailing whitespace 7029921a8fSScott Kruger """ 7129921a8fSScott Kruger # The first entry should be test: but it might be indented. 7229921a8fSScott Kruger ext=os.path.splitext(srcfile)[1] 7344776d8cSScott Kruger stripstr=" " 7466db876fSScott Kruger if len(fileNums)>0: lineNum=fileNums[0]-1 758ccd5183SScott Kruger for lline in block.split("\n"): 7666db876fSScott Kruger if len(fileNums)>0: lineNum+=1 778ccd5183SScott Kruger line=lline[1:] if lline.startswith("!") else lline 7829921a8fSScott Kruger if not line.strip(): continue 79c4b80baaSScott Kruger if line.strip().startswith('#'): continue 8044776d8cSScott Kruger if entireBlock: 8144776d8cSScott Kruger var=line.split(":")[0].strip() 82aec507c4SScott Kruger if not var in ['test','testset','build']: 8366db876fSScott Kruger raise Exception("Formatting error: Cannot find test in file: "+srcfile+" at line: "+str(lineNum)+"\n") 8429921a8fSScott Kruger nspace=len(line)-len(line.lstrip(stripstr)) 8529921a8fSScott Kruger newline=line[nspace:] 8629921a8fSScott Kruger break 8729921a8fSScott Kruger 8829921a8fSScott Kruger # Strip off any indentation for the whole string and any trailing 8929921a8fSScott Kruger # whitespace for convenience 9029921a8fSScott Kruger newTestStr="\n" 9144776d8cSScott Kruger if len(fileNums)>0: lineNum=fileNums[0]-1 9244776d8cSScott Kruger firstPass=True 938ccd5183SScott Kruger for lline in block.split("\n"): 9444776d8cSScott Kruger if len(fileNums)>0: lineNum+=1 958ccd5183SScott Kruger line=lline[1:] if lline.startswith("!") else lline 9629921a8fSScott Kruger if not line.strip(): continue 97c4b80baaSScott Kruger if line.strip().startswith('#'): 98c4b80baaSScott Kruger newTestStr+=line+'\n' 99c4b80baaSScott Kruger else: 10029921a8fSScott Kruger newline=line[nspace:] 101c4b80baaSScott Kruger newTestStr+=newline.rstrip()+"\n" 10244776d8cSScott Kruger # Do some basic indentation checks 10344776d8cSScott Kruger if entireBlock: 10444776d8cSScott Kruger # Don't need to check comment lines 10544776d8cSScott Kruger if line.strip().startswith('#'): continue 10644776d8cSScott Kruger if not newline.startswith(" "): 10744776d8cSScott Kruger var=newline.split(":")[0].strip() 108aec507c4SScott Kruger if not var in ['test','testset','build']: 10944776d8cSScott Kruger err="Formatting error in file "+srcfile+" at line: " +line+"\n" 11044776d8cSScott Kruger if len(fileNums)>0: 11144776d8cSScott Kruger raise Exception(err+"Check indentation at line number: "+str(lineNum)) 11244776d8cSScott Kruger else: 11344776d8cSScott Kruger raise Exception(err) 11444776d8cSScott Kruger else: 11544776d8cSScott Kruger var=line.split(":")[0].strip() 116aec507c4SScott Kruger if var in ['test','testset','build']: 11744776d8cSScott Kruger subnspace=len(line)-len(line.lstrip(stripstr)) 11844776d8cSScott Kruger if firstPass: 11944776d8cSScott Kruger firstsubnspace=subnspace 12044776d8cSScott Kruger firstPass=False 12144776d8cSScott Kruger else: 12244776d8cSScott Kruger if firstsubnspace!=subnspace: 12344776d8cSScott Kruger err="Formatting subtest error in file "+srcfile+" at line: " +line+"\n" 12444776d8cSScott Kruger if len(fileNums)>0: 12544776d8cSScott Kruger raise Exception(err+"Check indentation at line number: "+str(lineNum)) 12644776d8cSScott Kruger else: 12744776d8cSScott Kruger raise Exception(err) 12844776d8cSScott Kruger 12943c6e11bSMatthew G. Knepley # Allow line continuation character '\' 13043c6e11bSMatthew G. Knepley return newTestStr.replace('\\\n', ' ') 13129921a8fSScott Kruger 132aae9f2d9SScott Krugerdef parseLoopArgs(varset): 13344776d8cSScott Kruger """ 134aae9f2d9SScott Kruger Given: String containing loop variables 135aae9f2d9SScott Kruger Return: tuple containing separate/shared and string of loop vars 13644776d8cSScott Kruger """ 137a449fbaeSJed Brown keynm=varset.split("{{")[0].strip().lstrip('-') 138aae9f2d9SScott Kruger if not keynm.strip(): keynm='nsize' 139aae9f2d9SScott Kruger lvars=varset.split('{{')[1].split('}')[0] 140aae9f2d9SScott Kruger suffx=varset.split('{{')[1].split('}')[1] 141aae9f2d9SScott Kruger ftype='separate' if suffx.startswith('separate') else 'shared' 142aae9f2d9SScott Kruger return keynm,lvars,ftype 14344776d8cSScott Kruger 144e53dc769SScott Krugerdef _getSeparateTestvars(testDict): 145e53dc769SScott Kruger """ 146e53dc769SScott Kruger Given: dictionary that may have 147e53dc769SScott Kruger Return: Variables that cause a test split 148e53dc769SScott Kruger """ 149e53dc769SScott Kruger vals=None 150e53dc769SScott Kruger sepvars=[] 151e53dc769SScott Kruger # Check nsize 1525b6bfdb9SJed Brown if 'nsize' in testDict: 153e53dc769SScott Kruger varset=testDict['nsize'] 154aae9f2d9SScott Kruger if '{{' in varset: 155aae9f2d9SScott Kruger keynm,lvars,ftype=parseLoopArgs(varset) 156aae9f2d9SScott Kruger if ftype=='separate': sepvars.append(keynm) 157e53dc769SScott Kruger 158e53dc769SScott Kruger # Now check args 1595b6bfdb9SJed Brown if 'args' not in testDict: return sepvars 160d87d9516SStefano Zampini for varset in re.split('(^|\W)-(?=[a-zA-Z])',testDict['args']): 161e53dc769SScott Kruger if not varset.strip(): continue 162aae9f2d9SScott Kruger if '{{' in varset: 163e53dc769SScott Kruger # Assuming only one for loop per var specification 164aae9f2d9SScott Kruger keynm,lvars,ftype=parseLoopArgs(varset) 165aae9f2d9SScott Kruger if ftype=='separate': sepvars.append(keynm) 166e53dc769SScott Kruger 167e53dc769SScott Kruger return sepvars 168e53dc769SScott Kruger 1694d82b48cSScott Krugerdef _getNewArgs(args): 1704d82b48cSScott Kruger """ 1714d82b48cSScott Kruger Given: String that has args that might have loops in them 1724d82b48cSScott Kruger Return: All of the arguments/values that do not have 1734d82b48cSScott Kruger for 'separate output' in for loops 1744d82b48cSScott Kruger """ 1754d82b48cSScott Kruger newargs='' 1764d82b48cSScott Kruger if not args.strip(): return args 177d87d9516SStefano Zampini for varset in re.split('(^|\W)-(?=[a-zA-Z])',args): 1784d82b48cSScott Kruger if not varset.strip(): continue 17914f228deSScott Kruger if '{{' in varset and 'separate' in varset: continue 1804d82b48cSScott Kruger newargs+="-"+varset.strip()+" " 1814d82b48cSScott Kruger 1824d82b48cSScott Kruger return newargs 1834d82b48cSScott Kruger 18444776d8cSScott Krugerdef _getVarVals(findvar,testDict): 18544776d8cSScott Kruger """ 18644776d8cSScott Kruger Given: variable that is either nsize or in args 1874d82b48cSScott Kruger Return: Values to loop over and the other arguments 1884d82b48cSScott Kruger Note that we keep the other arguments even if they have 1894d82b48cSScott Kruger for loops to enable stepping through all of the for lops 19044776d8cSScott Kruger """ 1914d82b48cSScott Kruger save_vals=None 19244776d8cSScott Kruger if findvar=='nsize': 193e53dc769SScott Kruger varset=testDict[findvar] 1944d82b48cSScott Kruger keynm,save_vals,ftype=parseLoopArgs('nsize '+varset) 19544776d8cSScott Kruger else: 19644776d8cSScott Kruger varlist=[] 19744776d8cSScott Kruger for varset in re.split('-(?=[a-zA-Z])',testDict['args']): 19844776d8cSScott Kruger if not varset.strip(): continue 1994d82b48cSScott Kruger if '{{' not in varset: continue 200aae9f2d9SScott Kruger keyvar,vals,ftype=parseLoopArgs(varset) 2014d82b48cSScott Kruger if keyvar==findvar: 2024d82b48cSScott Kruger save_vals=vals 20344776d8cSScott Kruger 2045b6bfdb9SJed Brown if not save_vals: raise Exception("Could not find separate_testvar: "+findvar) 2054d82b48cSScott Kruger return save_vals 20644776d8cSScott Kruger 2074f8a0bffSScott Krugerdef genTestsSeparateTestvars(intests,indicts,final=False): 20844776d8cSScott Kruger """ 209e53dc769SScott Kruger Given: testname, sdict with 'separate_testvars 21044776d8cSScott Kruger Return: testnames,sdicts: List of generated tests 2114d82b48cSScott Kruger The tricky part here is the {{ ... }separate output} 2124d82b48cSScott Kruger that can be used multiple times 21344776d8cSScott Kruger """ 21444776d8cSScott Kruger testnames=[]; sdicts=[] 21544776d8cSScott Kruger for i in range(len(intests)): 21644776d8cSScott Kruger testname=intests[i]; sdict=indicts[i]; i+=1 217e53dc769SScott Kruger separate_testvars=_getSeparateTestvars(sdict) 218e53dc769SScott Kruger if len(separate_testvars)>0: 2194d82b48cSScott Kruger sep_dicts=[sdict.copy()] 2204d82b48cSScott Kruger if 'args' in sep_dicts[0]: 2214d82b48cSScott Kruger sep_dicts[0]['args']=_getNewArgs(sdict['args']) 2224d82b48cSScott Kruger sep_testnames=[testname] 223e53dc769SScott Kruger for kvar in separate_testvars: 2244d82b48cSScott Kruger kvals=_getVarVals(kvar,sdict) 2254d82b48cSScott Kruger 2264d82b48cSScott Kruger # Have to do loop over previous var/val combos as well 2274d82b48cSScott Kruger # and accumulate as we go 2284d82b48cSScott Kruger val_testnames=[]; val_dicts=[] 22944776d8cSScott Kruger for val in kvals.split(): 230541979b6SScott Kruger gensuffix="_"+kvar+"-"+val.replace(',','__') 2314d82b48cSScott Kruger for kvaltestnm in sep_testnames: 2324d82b48cSScott Kruger val_testnames.append(kvaltestnm+gensuffix) 2334d82b48cSScott Kruger for kv in sep_dicts: 2344d82b48cSScott Kruger kvardict=kv.copy() 2354d82b48cSScott Kruger # If the last var then we have the final version 2364d82b48cSScott Kruger if 'suffix' in sdict: 2374d82b48cSScott Kruger kvardict['suffix']+=gensuffix 2380bcc1aabSScott Kruger else: 2390bcc1aabSScott Kruger kvardict['suffix']=gensuffix 24044776d8cSScott Kruger if kvar=='nsize': 24144776d8cSScott Kruger kvardict[kvar]=val 24244776d8cSScott Kruger else: 2434d82b48cSScott Kruger kvardict['args']+="-"+kvar+" "+val+" " 2444d82b48cSScott Kruger val_dicts.append(kvardict) 2454d82b48cSScott Kruger sep_testnames=val_testnames 2464d82b48cSScott Kruger sep_dicts=val_dicts 2474d82b48cSScott Kruger testnames+=sep_testnames 2484d82b48cSScott Kruger sdicts+=sep_dicts 24944776d8cSScott Kruger else: 2504f8a0bffSScott Kruger # These are plain vanilla tests (no subtests, no loops) that 2514f8a0bffSScott Kruger # do not have a suffix. This makes the targets match up with 2524f8a0bffSScott Kruger # the output file (testname_1.out) 2534f8a0bffSScott Kruger if final: 2544f8a0bffSScott Kruger if '_' not in testname: testname+='_1' 25544776d8cSScott Kruger testnames.append(testname) 25644776d8cSScott Kruger sdicts.append(sdict) 25744776d8cSScott Kruger return testnames,sdicts 25844776d8cSScott Kruger 25944776d8cSScott Krugerdef genTestsSubtestSuffix(testnames,sdicts): 26044776d8cSScott Kruger """ 26144776d8cSScott Kruger Given: testname, sdict with separate_testvars 26244776d8cSScott Kruger Return: testnames,sdicts: List of generated tests 26344776d8cSScott Kruger """ 26444776d8cSScott Kruger tnms=[]; sdcts=[] 26544776d8cSScott Kruger for i in range(len(testnames)): 26644776d8cSScott Kruger testname=testnames[i] 26744776d8cSScott Kruger rmsubtests=[]; keepSubtests=False 2685b6bfdb9SJed Brown if 'subtests' in sdicts[i]: 26944776d8cSScott Kruger for stest in sdicts[i]["subtests"]: 2705b6bfdb9SJed Brown if 'suffix' in sdicts[i][stest]: 27144776d8cSScott Kruger rmsubtests.append(stest) 27244776d8cSScott Kruger gensuffix="_"+sdicts[i][stest]['suffix'] 27344776d8cSScott Kruger newtestnm=testname+gensuffix 27444776d8cSScott Kruger tnms.append(newtestnm) 27544776d8cSScott Kruger newsdict=sdicts[i].copy() 27644776d8cSScott Kruger del newsdict['subtests'] 27744776d8cSScott Kruger # Have to hand update 27844776d8cSScott Kruger # Append 27944776d8cSScott Kruger for kup in appendlist: 2805b6bfdb9SJed Brown if kup in sdicts[i][stest]: 2815b6bfdb9SJed Brown if kup in sdicts[i]: 28244776d8cSScott Kruger newsdict[kup]=sdicts[i][kup]+" "+sdicts[i][stest][kup] 28344776d8cSScott Kruger else: 28444776d8cSScott Kruger newsdict[kup]=sdicts[i][stest][kup] 28544776d8cSScott Kruger # Promote 28644776d8cSScott Kruger for kup in acceptedkeys: 28744776d8cSScott Kruger if kup in appendlist: continue 2885b6bfdb9SJed Brown if kup in sdicts[i][stest]: 28944776d8cSScott Kruger newsdict[kup]=sdicts[i][stest][kup] 29044776d8cSScott Kruger # Cleanup 29144776d8cSScott Kruger for st in sdicts[i]["subtests"]: del newsdict[st] 29244776d8cSScott Kruger sdcts.append(newsdict) 29344776d8cSScott Kruger else: 29444776d8cSScott Kruger keepSubtests=True 29544776d8cSScott Kruger else: 29644776d8cSScott Kruger tnms.append(testnames[i]) 29744776d8cSScott Kruger sdcts.append(sdicts[i]) 2980bcc1aabSScott Kruger # If a subtest without a suffix exists, then save it 29944776d8cSScott Kruger if keepSubtests: 30044776d8cSScott Kruger tnms.append(testnames[i]) 3010bcc1aabSScott Kruger newsdict=sdicts[i].copy() 3020bcc1aabSScott Kruger # Prune the tests to prepare for keeping 3030bcc1aabSScott Kruger for rmtest in rmsubtests: 3040bcc1aabSScott Kruger newsdict['subtests'].remove(rmtest) 3050bcc1aabSScott Kruger del newsdict[rmtest] 3060bcc1aabSScott Kruger sdcts.append(newsdict) 30744776d8cSScott Kruger i+=1 30844776d8cSScott Kruger return tnms,sdcts 30944776d8cSScott Kruger 31044776d8cSScott Krugerdef splitTests(testname,sdict): 31144776d8cSScott Kruger """ 3122be3497aSPatrick Sanan Given: testname and dictionary generated from the YAML-like definition 31344776d8cSScott Kruger Return: list of names and dictionaries corresponding to each test 3142be3497aSPatrick Sanan given that the YAML-like language allows for multiple tests 31544776d8cSScott Kruger """ 31644776d8cSScott Kruger 31744776d8cSScott Kruger # Order: Parent sep_tv, subtests suffix, subtests sep_tv 31844776d8cSScott Kruger testnames,sdicts=genTestsSeparateTestvars([testname],[sdict]) 31944776d8cSScott Kruger testnames,sdicts=genTestsSubtestSuffix(testnames,sdicts) 3204f8a0bffSScott Kruger testnames,sdicts=genTestsSeparateTestvars(testnames,sdicts,final=True) 32144776d8cSScott Kruger 32244776d8cSScott Kruger # Because I am altering the list, I do this in passes. Inelegant 32344776d8cSScott Kruger 32444776d8cSScott Kruger return testnames, sdicts 32544776d8cSScott Kruger 326*080f0011SToby Isaac 327*080f0011SToby Isaacdef testSplit(striptest): 328*080f0011SToby Isaac """ 329*080f0011SToby Isaac Split up a test into lines, but use a shell parser to detect when newlines are within quotation marks 330*080f0011SToby Isaac and keep those together 331*080f0011SToby Isaac """ 332*080f0011SToby Isaac import shlex 333*080f0011SToby Isaac 334*080f0011SToby Isaac sl = shlex.shlex() 335*080f0011SToby Isaac sl.whitespace_split = True # only split at whitespace 336*080f0011SToby Isaac sl.commenters = '' 337*080f0011SToby Isaac sl.push_source(striptest) 338*080f0011SToby Isaac last_pos = sl.instream.tell() 339*080f0011SToby Isaac try: 340*080f0011SToby Isaac last_token = sl.read_token() 341*080f0011SToby Isaac except ValueError: 342*080f0011SToby Isaac print(striptest) 343*080f0011SToby Isaac raise ValueError 344*080f0011SToby Isaac last_line = '' 345*080f0011SToby Isaac while last_token != '': 346*080f0011SToby Isaac new_pos = sl.instream.tell() 347*080f0011SToby Isaac block = striptest[last_pos:new_pos] 348*080f0011SToby Isaac token_start = block.find(last_token) 349*080f0011SToby Isaac leading = block[0:token_start] 350*080f0011SToby Isaac trailing = block[(token_start + len(last_token)):] 351*080f0011SToby Isaac leading_split = leading.split('\n') 352*080f0011SToby Isaac if len(leading_split) > 1: 353*080f0011SToby Isaac yield last_line 354*080f0011SToby Isaac last_line = '' 355*080f0011SToby Isaac last_line += leading_split[-1] 356*080f0011SToby Isaac last_line += last_token 357*080f0011SToby Isaac trailing_split = trailing.split('\n') 358*080f0011SToby Isaac last_line += trailing_split[0] 359*080f0011SToby Isaac if len(trailing_split) > 1: 360*080f0011SToby Isaac yield last_line 361*080f0011SToby Isaac last_line = '' 362*080f0011SToby Isaac last_pos = new_pos 363*080f0011SToby Isaac try: 364*080f0011SToby Isaac last_token = sl.read_token() 365*080f0011SToby Isaac except ValueError: 366*080f0011SToby Isaac print(striptest) 367*080f0011SToby Isaac raise ValueError 368*080f0011SToby Isaac yield last_line 369*080f0011SToby Isaac 370*080f0011SToby Isaac 3716cecdbdcSScott Krugerdef parseTest(testStr,srcfile,verbosity): 37229921a8fSScott Kruger """ 37329921a8fSScott Kruger This parses an individual test 3742be3497aSPatrick Sanan Our YAML-like language is hierarchial so should use a state machine in the general case, 37553f2a965SBarry Smith but in practice we only support two levels of test: 37629921a8fSScott Kruger """ 37729921a8fSScott Kruger basename=os.path.basename(srcfile) 37829921a8fSScott Kruger # Handle the new at the begininng 37929921a8fSScott Kruger bn=re.sub("new_","",basename) 38029921a8fSScott Kruger # This is the default 38129921a8fSScott Kruger testname="run"+os.path.splitext(bn)[0] 38229921a8fSScott Kruger 38329921a8fSScott Kruger # Tests that have default everything (so empty effectively) 38478659935SScott Kruger if len(testStr)==0: return [testname], [{}] 38529921a8fSScott Kruger 38629921a8fSScott Kruger striptest=_stripIndent(testStr,srcfile) 38729921a8fSScott Kruger 38829921a8fSScott Kruger # go through and parse 38929921a8fSScott Kruger subtestnum=0 39029921a8fSScott Kruger subdict={} 39168a9e459SScott Kruger comments=[] 39268a9e459SScott Kruger indentlevel=0 393*080f0011SToby Isaac for ln in testSplit(striptest): 394c4b80baaSScott Kruger line=ln.split('#')[0].rstrip() 395cadd188bSScott Kruger if verbosity>2: print(line) 3960bcc1aabSScott Kruger comment=("" if len(ln.split("#"))>0 else " ".join(ln.split("#")[1:]).strip()) 39768a9e459SScott Kruger if comment: comments.append(comment) 39829921a8fSScott Kruger if not line.strip(): continue 39944776d8cSScott Kruger lsplit=line.split(':') 40044776d8cSScott Kruger if len(lsplit)==0: raise Exception("Missing : in line: "+line) 40144776d8cSScott Kruger indentcount=lsplit[0].count(" ") 40244776d8cSScott Kruger var=lsplit[0].strip() 40340ae0433SScott Kruger val=line[line.find(':')+1:].strip() 40444776d8cSScott Kruger if not var in acceptedkeys: raise Exception("Not a defined key: "+var+" from: "+line) 40529921a8fSScott Kruger # Start by seeing if we are in a subtest 40629921a8fSScott Kruger if line.startswith(" "): 407ecc1beb5SScott Kruger if var in subdict[subtestname]: 408ecc1beb5SScott Kruger subdict[subtestname][var]+=" "+val 409ecc1beb5SScott Kruger else: 410c0658d2aSScott Kruger subdict[subtestname][var]=val 41168a9e459SScott Kruger if not indentlevel: indentlevel=indentcount 412cadd188bSScott Kruger #if indentlevel!=indentcount: print("Error in indentation:", ln) 41329921a8fSScott Kruger # Determine subtest name and make dict 41429921a8fSScott Kruger elif var=="test": 41529921a8fSScott Kruger subtestname="test"+str(subtestnum) 41629921a8fSScott Kruger subdict[subtestname]={} 4175b6bfdb9SJed Brown if "subtests" not in subdict: subdict["subtests"]=[] 41829921a8fSScott Kruger subdict["subtests"].append(subtestname) 41929921a8fSScott Kruger subtestnum=subtestnum+1 42068a9e459SScott Kruger # The rest are easy 42129921a8fSScott Kruger else: 42244776d8cSScott Kruger # For convenience, it is sometimes convenient to list twice 4235b6bfdb9SJed Brown if var in subdict: 42444776d8cSScott Kruger if var in appendlist: 42544776d8cSScott Kruger subdict[var]+=" "+val 42644776d8cSScott Kruger else: 42744776d8cSScott Kruger raise Exception(var+" entered twice: "+line) 42844776d8cSScott Kruger else: 429c0658d2aSScott Kruger subdict[var]=val 43029921a8fSScott Kruger if var=="suffix": 43129921a8fSScott Kruger if len(val)>0: 4324f8a0bffSScott Kruger testname+="_"+val 43329921a8fSScott Kruger 43468a9e459SScott Kruger if len(comments): subdict['comments']="\n".join(comments).lstrip("\n") 4354f8a0bffSScott Kruger 4364f8a0bffSScott Kruger # A test block can create multiple tests. This does that logic 43744776d8cSScott Kruger testnames,subdicts=splitTests(testname,subdict) 43844776d8cSScott Kruger return testnames,subdicts 43929921a8fSScott Kruger 4406cecdbdcSScott Krugerdef parseTests(testStr,srcfile,fileNums,verbosity): 44129921a8fSScott Kruger """ 4422be3497aSPatrick Sanan Parse the YAML-like string describing tests and return 44329921a8fSScott Kruger a dictionary with the info in the form of: 44429921a8fSScott Kruger testDict[test][subtest] 44529921a8fSScott Kruger """ 44629921a8fSScott Kruger 44729921a8fSScott Kruger testDict={} 44829921a8fSScott Kruger 44929921a8fSScott Kruger # The first entry should be test: but it might be indented. 45044776d8cSScott Kruger newTestStr=_stripIndent(testStr,srcfile,entireBlock=True,fileNums=fileNums) 451cadd188bSScott Kruger if verbosity>2: print(srcfile) 45229921a8fSScott Kruger 453e4653983SScott Kruger ## Check and see if we have build requirements 454e4653983SScott Kruger addToRunRequirements=None 455aec507c4SScott Kruger if "\nbuild:" in newTestStr: 456aec507c4SScott Kruger testDict['build']={} 457aec507c4SScott Kruger # The file info is already here and need to append 458aec507c4SScott Kruger Part1=newTestStr.split("build:")[1] 459aec507c4SScott Kruger fileInfo=re.split("\ntest(?:set)?:",newTestStr)[0] 460aec507c4SScott Kruger for bkey in buildkeys: 461aec507c4SScott Kruger if bkey+":" in fileInfo: 462aec507c4SScott Kruger testDict['build'][bkey]=fileInfo.split(bkey+":")[1].split("\n")[0].strip() 463aec507c4SScott Kruger #if verbosity>1: bkey+": "+testDict['build'][bkey] 464e4653983SScott Kruger # If a runtime requires are put into build, push them down to all run tests 465e4653983SScott Kruger # At this point, we are working with strings and not lists 466e4653983SScott Kruger if 'requires' in testDict['build']: 467e4653983SScott Kruger if 'datafilespath' in testDict['build']['requires']: 468e4653983SScott Kruger newreqs=re.sub('datafilespath','',testDict['build']['requires']) 469e4653983SScott Kruger testDict['build']['requires']=newreqs.strip() 470e4653983SScott Kruger addToRunRequirements='datafilespath' 471e4653983SScott Kruger 472aec507c4SScott Kruger 47329921a8fSScott Kruger # Now go through each test. First elem in split is blank 474e53dc769SScott Kruger for test in re.split("\ntest(?:set)?:",newTestStr)[1:]: 4756cecdbdcSScott Kruger testnames,subdicts=parseTest(test,srcfile,verbosity) 47644776d8cSScott Kruger for i in range(len(testnames)): 4775b6bfdb9SJed Brown if testnames[i] in testDict: 4781acf9037SMatthew G. Knepley raise RuntimeError("Multiple test names specified: "+testnames[i]+" in file: "+srcfile) 479e4653983SScott Kruger # Add in build requirements that need to be moved 480e4653983SScott Kruger if addToRunRequirements: 481e4653983SScott Kruger if 'requires' in subdicts[i]: 482e4653983SScott Kruger subdicts[i]['requires']+=addToRunRequirements 483e4653983SScott Kruger else: 484e4653983SScott Kruger subdicts[i]['requires']=addToRunRequirements 48544776d8cSScott Kruger testDict[testnames[i]]=subdicts[i] 48629921a8fSScott Kruger 48729921a8fSScott Kruger return testDict 48829921a8fSScott Kruger 4896cecdbdcSScott Krugerdef parseTestFile(srcfile,verbosity): 49029921a8fSScott Kruger """ 49129921a8fSScott Kruger Parse single example files and return dictionary of the form: 49229921a8fSScott Kruger testDict[srcfile][test][subtest] 49329921a8fSScott Kruger """ 49429921a8fSScott Kruger debug=False 495cadd188bSScott Kruger basename=os.path.basename(srcfile) 496cadd188bSScott Kruger if basename=='makefile': return {} 497cadd188bSScott Kruger 49829921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 49929921a8fSScott Kruger basedir=os.path.dirname(os.path.realpath(srcfile)) 50029921a8fSScott Kruger os.chdir(basedir) 50129921a8fSScott Kruger 50229921a8fSScott Kruger testDict={} 503cadd188bSScott Kruger sh=open(basename,"r"); fileStr=sh.read(); sh.close() 50429921a8fSScott Kruger 50529921a8fSScott Kruger ## Start with doing the tests 50629921a8fSScott Kruger # 50729921a8fSScott Kruger fsplit=fileStr.split("/*TEST\n")[1:] 50844776d8cSScott Kruger fstart=len(fileStr.split("/*TEST\n")[0].split("\n"))+1 50929921a8fSScott Kruger # Allow for multiple "/*TEST" blocks even though it really should be 5106f029658SMatthew G. Knepley # one 51129921a8fSScott Kruger srcTests=[] 51229921a8fSScott Kruger for t in fsplit: srcTests.append(t.split("TEST*/")[0]) 51329921a8fSScott Kruger testString=" ".join(srcTests) 51444776d8cSScott Kruger flen=len(testString.split("\n")) 51544776d8cSScott Kruger fend=fstart+flen-1 51644776d8cSScott Kruger fileNums=range(fstart,fend) 5176cecdbdcSScott Kruger testDict[basename]=parseTests(testString,srcfile,fileNums,verbosity) 518aec507c4SScott Kruger # Massage dictionary for build requirements 519aec507c4SScott Kruger if 'build' in testDict[basename]: 520aec507c4SScott Kruger testDict[basename].update(testDict[basename]['build']) 521aec507c4SScott Kruger del testDict[basename]['build'] 52229921a8fSScott Kruger 52329921a8fSScott Kruger 52429921a8fSScott Kruger os.chdir(curdir) 52529921a8fSScott Kruger return testDict 52629921a8fSScott Kruger 5276cecdbdcSScott Krugerdef parseTestDir(directory,verbosity): 52829921a8fSScott Kruger """ 52929921a8fSScott Kruger Parse single example files and return dictionary of the form: 53029921a8fSScott Kruger testDict[srcfile][test][subtest] 53129921a8fSScott Kruger """ 53229921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 53329921a8fSScott Kruger basedir=os.path.realpath(directory) 53429921a8fSScott Kruger os.chdir(basedir) 53529921a8fSScott Kruger 53629921a8fSScott Kruger tDict={} 53709a6cbfcSBernhard M. Wiedemann for test_file in sorted(glob.glob("new_ex*.*")): 5386cecdbdcSScott Kruger tDict.update(parseTestFile(test_file,verbosity)) 53929921a8fSScott Kruger 54029921a8fSScott Kruger os.chdir(curdir) 54129921a8fSScott Kruger return tDict 54229921a8fSScott Kruger 54329921a8fSScott Krugerdef printExParseDict(rDict): 54429921a8fSScott Kruger """ 54529921a8fSScott Kruger This is useful for debugging 54629921a8fSScott Kruger """ 54729921a8fSScott Kruger indent=" " 54829921a8fSScott Kruger for sfile in rDict: 549cadd188bSScott Kruger print(sfile) 550c3d83d22SScott Kruger sortkeys=list(rDict[sfile].keys()) 55144776d8cSScott Kruger sortkeys.sort() 55244776d8cSScott Kruger for runex in sortkeys: 55369fa9ab3SScott Kruger if runex == 'requires': 55469fa9ab3SScott Kruger print(indent+runex+':'+str(rDict[sfile][runex])) 55569fa9ab3SScott Kruger continue 556cadd188bSScott Kruger print(indent+runex) 5575b6bfdb9SJed Brown if type(rDict[sfile][runex])==bytes: 558cadd188bSScott Kruger print(indent*2+rDict[sfile][runex]) 55929921a8fSScott Kruger else: 56029921a8fSScott Kruger for var in rDict[sfile][runex]: 56144776d8cSScott Kruger if var.startswith("test"): continue 562cadd188bSScott Kruger print(indent*2+var+": "+str(rDict[sfile][runex][var])) 5635b6bfdb9SJed Brown if 'subtests' in rDict[sfile][runex]: 56444776d8cSScott Kruger for var in rDict[sfile][runex]['subtests']: 565cadd188bSScott Kruger print(indent*2+var) 56629921a8fSScott Kruger for var2 in rDict[sfile][runex][var]: 567cadd188bSScott Kruger print(indent*3+var2+": "+str(rDict[sfile][runex][var][var2])) 568cadd188bSScott Kruger print("\n") 56929921a8fSScott Kruger return 57029921a8fSScott Kruger 57129921a8fSScott Krugerdef main(directory='',test_file='',verbosity=0): 57229921a8fSScott Kruger 57329921a8fSScott Kruger if directory: 5746cecdbdcSScott Kruger tDict=parseTestDir(directory,verbosity) 57529921a8fSScott Kruger else: 5766cecdbdcSScott Kruger tDict=parseTestFile(test_file,verbosity) 57729921a8fSScott Kruger if verbosity>0: printExParseDict(tDict) 57829921a8fSScott Kruger 57929921a8fSScott Kruger return 58029921a8fSScott Kruger 58129921a8fSScott Krugerif __name__ == '__main__': 58229921a8fSScott Kruger import optparse 58329921a8fSScott Kruger parser = optparse.OptionParser() 58429921a8fSScott Kruger parser.add_option('-d', '--directory', dest='directory', 58529921a8fSScott Kruger default="", help='Directory containing files to parse') 58629921a8fSScott Kruger parser.add_option('-t', '--test_file', dest='test_file', 58729921a8fSScott Kruger default="", help='Test file, e.g., ex1.c, to parse') 58829921a8fSScott Kruger parser.add_option('-v', '--verbosity', dest='verbosity', 58929921a8fSScott Kruger help='Verbosity of output by level: 1, 2, or 3', default=0) 59029921a8fSScott Kruger opts, extra_args = parser.parse_args() 59129921a8fSScott Kruger 59229921a8fSScott Kruger if extra_args: 59329921a8fSScott Kruger import sys 59429921a8fSScott Kruger sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args)) 59529921a8fSScott Kruger exit(1) 59629921a8fSScott Kruger if not opts.test_file and not opts.directory: 597cadd188bSScott Kruger print("test file or directory is required") 59829921a8fSScott Kruger parser.print_usage() 59929921a8fSScott Kruger sys.exit() 60029921a8fSScott Kruger 60129921a8fSScott Kruger # Need verbosity to be an integer 60229921a8fSScott Kruger try: 60329921a8fSScott Kruger verbosity=int(opts.verbosity) 60429921a8fSScott Kruger except: 60529921a8fSScott Kruger raise Exception("Error: Verbosity must be integer") 60629921a8fSScott Kruger 60729921a8fSScott Kruger main(directory=opts.directory,test_file=opts.test_file,verbosity=verbosity) 608