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 Kruger 1429921a8fSScott Kruger 1529921a8fSScott KrugerExample language 1629921a8fSScott Kruger---------------- 1729921a8fSScott Kruger/*T 1829921a8fSScott Kruger Concepts: 1929921a8fSScott Kruger requires: moab 2029921a8fSScott KrugerT*/ 2129921a8fSScott Kruger 2229921a8fSScott Kruger 2329921a8fSScott Kruger 2429921a8fSScott Kruger/*TEST 2529921a8fSScott Kruger 2629921a8fSScott Kruger test: 2729921a8fSScott 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 2829921a8fSScott Kruger output_file: output/ex25_1.out 2929921a8fSScott Kruger 3029921a8fSScott Kruger test: 3129921a8fSScott Kruger suffix: 2 3229921a8fSScott Kruger nsize: 2 3329921a8fSScott 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 3429921a8fSScott Kruger 3529921a8fSScott KrugerTEST*/ 3629921a8fSScott Kruger 3729921a8fSScott Kruger""" 3829921a8fSScott Kruger 3929921a8fSScott Krugerimport os, re, glob, types 4029921a8fSScott Krugerfrom distutils.sysconfig import parse_makefile 4129921a8fSScott Krugerimport sys 4229921a8fSScott Krugerimport logging 4329921a8fSScott Krugersys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) 4429921a8fSScott Kruger 4529921a8fSScott Krugerimport inspect 4629921a8fSScott Krugerthisscriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 4729921a8fSScott Krugermaintdir=os.path.join(os.path.join(os.path.dirname(thisscriptdir),'bin'),'maint') 4829921a8fSScott Krugersys.path.insert(0,maintdir) 4929921a8fSScott Kruger 5029921a8fSScott Kruger# These are special keys describing build 5129921a8fSScott Krugerbuildkeys="requires TODO SKIP depends".split() 5229921a8fSScott Kruger 5368a9e459SScott Kruger 5468a9e459SScott Krugerimport re 5568a9e459SScott Krugerdef extractForVars(argStr): 5668a9e459SScott Kruger """ 5768a9e459SScott Kruger Given: 'args: -bs {{1 2 3 4 5}} -pc_type {{cholesky sor}} -ksp_monitor' 5868a9e459SScott Kruger Return: 5968a9e459SScott Kruger args: -ksp_monitor 6068a9e459SScott Kruger forlist="bs pc_type" # Don't assume OrderedDict 6168a9e459SScott Kruger forargs[bs]['val']="1 2 3 4 5" 6268a9e459SScott Kruger forargs[pc_type]['val']="cholesky sor" 6368a9e459SScott Kruger """ 6468a9e459SScott Kruger loopstr=argStr 6568a9e459SScott Kruger forargs={} 6668a9e459SScott Kruger forlist=[] 6768a9e459SScott Kruger newargs="" 6868a9e459SScott Kruger for varset in re.split('-(?=[a-zA-Z])',loopstr): 6968a9e459SScott Kruger if not varset.strip(): continue 7068a9e459SScott Kruger if len(re.findall('{{(.*?)}}',varset))>0: 7168a9e459SScott Kruger # Assuming only one for loop per var specification 7268a9e459SScott Kruger forvar=varset.split("{{")[0].strip() 7368a9e459SScott Kruger forlist.append(forvar) 7468a9e459SScott Kruger forargs[forvar]={} 7568a9e459SScott Kruger forargs[forvar]['val']=re.findall('{{(.*?)}}',varset)[0] 7668a9e459SScott Kruger else: 7768a9e459SScott Kruger newargs=newargs+"-"+varset+" " 7868a9e459SScott Kruger 7968a9e459SScott Kruger return forlist,forargs,newargs.strip() 8068a9e459SScott Kruger 8168a9e459SScott Kruger 8229921a8fSScott Krugerdef _stripIndent(block,srcfile): 8329921a8fSScott Kruger """ 8429921a8fSScott Kruger Go through and remove a level of indentation 8529921a8fSScott Kruger Also strip of trailing whitespace 8629921a8fSScott Kruger """ 8729921a8fSScott Kruger # The first entry should be test: but it might be indented. 8829921a8fSScott Kruger ext=os.path.splitext(srcfile)[1] 898ccd5183SScott Kruger for lline in block.split("\n"): 908ccd5183SScott Kruger line=lline[1:] if lline.startswith("!") else lline 91ca789a6bSScott Kruger line=line.split('#')[0] 9229921a8fSScott Kruger if not line.strip(): continue 938ccd5183SScott Kruger stripstr=" " 9429921a8fSScott Kruger nspace=len(line)-len(line.lstrip(stripstr)) 9529921a8fSScott Kruger newline=line[nspace:] 9629921a8fSScott Kruger break 9729921a8fSScott Kruger 9829921a8fSScott Kruger # Strip off any indentation for the whole string and any trailing 9929921a8fSScott Kruger # whitespace for convenience 10029921a8fSScott Kruger newTestStr="\n" 1018ccd5183SScott Kruger for lline in block.split("\n"): 1028ccd5183SScott Kruger line=lline[1:] if lline.startswith("!") else lline 10329921a8fSScott Kruger if not line.strip(): continue 10429921a8fSScott Kruger newline=line[nspace:] 10529921a8fSScott Kruger newTestStr=newTestStr+newline.rstrip()+"\n" 10629921a8fSScott Kruger 10729921a8fSScott Kruger return newTestStr 10829921a8fSScott Kruger 10929921a8fSScott Krugerdef parseTest(testStr,srcfile): 11029921a8fSScott Kruger """ 11129921a8fSScott Kruger This parses an individual test 11229921a8fSScott Kruger YAML is hierarchial so should use a state machine in the general case, 11353f2a965SBarry Smith but in practice we only support two levels of test: 11429921a8fSScott Kruger """ 11529921a8fSScott Kruger basename=os.path.basename(srcfile) 11629921a8fSScott Kruger # Handle the new at the begininng 11729921a8fSScott Kruger bn=re.sub("new_","",basename) 11829921a8fSScott Kruger # This is the default 11929921a8fSScott Kruger testname="run"+os.path.splitext(bn)[0] 12029921a8fSScott Kruger 12129921a8fSScott Kruger # Tests that have default everything (so empty effectively) 12229921a8fSScott Kruger if len(testStr)==0: return testname, {} 12329921a8fSScott Kruger 12429921a8fSScott Kruger striptest=_stripIndent(testStr,srcfile) 12529921a8fSScott Kruger 12629921a8fSScott Kruger # go through and parse 12729921a8fSScott Kruger subtestnum=0 12829921a8fSScott Kruger subdict={} 12968a9e459SScott Kruger comments=[] 13068a9e459SScott Kruger indentlevel=0 13168a9e459SScott Kruger for ln in striptest.split("\n"): 13268a9e459SScott Kruger line=ln.split('#')[0] 13368a9e459SScott Kruger comment=("" if len(ln.split("#"))==1 else " ".join(ln.split("#")[1:]).strip()) 13468a9e459SScott Kruger if comment: comments.append(comment) 13529921a8fSScott Kruger if not line.strip(): continue 13668a9e459SScott Kruger indentcount=line.split(":")[0].count(" ") 13729921a8fSScott Kruger var=line.split(":")[0].strip() 13829921a8fSScott Kruger val=line.split(":")[1].strip() 13929921a8fSScott Kruger # Start by seeing if we are in a subtest 14029921a8fSScott Kruger if line.startswith(" "): 141*c0658d2aSScott Kruger subdict[subtestname][var]=val 14268a9e459SScott Kruger if not indentlevel: indentlevel=indentcount 14368a9e459SScott Kruger #if indentlevel!=indentcount: print "Error in indentation:", ln 14429921a8fSScott Kruger # Determine subtest name and make dict 14529921a8fSScott Kruger elif var=="test": 14629921a8fSScott Kruger subtestname="test"+str(subtestnum) 14729921a8fSScott Kruger subdict[subtestname]={} 14829921a8fSScott Kruger if not subdict.has_key("subtests"): subdict["subtests"]=[] 14929921a8fSScott Kruger subdict["subtests"].append(subtestname) 15029921a8fSScott Kruger subtestnum=subtestnum+1 15168a9e459SScott Kruger # The rest are easy 15229921a8fSScott Kruger else: 153*c0658d2aSScott Kruger subdict[var]=val 15429921a8fSScott Kruger if var=="suffix": 15529921a8fSScott Kruger if len(val)>0: 15629921a8fSScott Kruger testname=testname+"_"+val 15729921a8fSScott Kruger 15868a9e459SScott Kruger if len(comments): subdict['comments']="\n".join(comments).lstrip("\n") 15929921a8fSScott Kruger return testname,subdict 16029921a8fSScott Kruger 16129921a8fSScott Krugerdef parseTests(testStr,srcfile): 16229921a8fSScott Kruger """ 16329921a8fSScott Kruger Parse the yaml string describing tests and return 16429921a8fSScott Kruger a dictionary with the info in the form of: 16529921a8fSScott Kruger testDict[test][subtest] 16629921a8fSScott Kruger This is an inelegant parser as we do not wish to 16729921a8fSScott Kruger introduce a new dependency by bringing in pyyaml. 16829921a8fSScott Kruger The advantage is that validation can be done as 16929921a8fSScott Kruger it is parsed (e.g., 'test' is the only top-level node) 17029921a8fSScott Kruger """ 17129921a8fSScott Kruger 17229921a8fSScott Kruger testDict={} 17329921a8fSScott Kruger 17429921a8fSScott Kruger # The first entry should be test: but it might be indented. 17529921a8fSScott Kruger newTestStr=_stripIndent(testStr,srcfile) 17629921a8fSScott Kruger 17729921a8fSScott Kruger # Now go through each test. First elem in split is blank 17829921a8fSScott Kruger for test in newTestStr.split("\ntest:\n")[1:]: 17929921a8fSScott Kruger testname,subdict=parseTest(test,srcfile) 18029921a8fSScott Kruger if testDict.has_key(testname): 18129921a8fSScott Kruger print "Multiple test names specified: "+testname+" in file: "+srcfile 18229921a8fSScott Kruger testDict[testname]=subdict 18329921a8fSScott Kruger 18429921a8fSScott Kruger return testDict 18529921a8fSScott Kruger 18629921a8fSScott Krugerdef parseTestFile(srcfile): 18729921a8fSScott Kruger """ 18829921a8fSScott Kruger Parse single example files and return dictionary of the form: 18929921a8fSScott Kruger testDict[srcfile][test][subtest] 19029921a8fSScott Kruger """ 19129921a8fSScott Kruger debug=False 19229921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 19329921a8fSScott Kruger basedir=os.path.dirname(os.path.realpath(srcfile)) 19429921a8fSScott Kruger basename=os.path.basename(srcfile) 19529921a8fSScott Kruger os.chdir(basedir) 19629921a8fSScott Kruger 19729921a8fSScott Kruger testDict={} 19829921a8fSScott Kruger sh=open(srcfile,"r"); fileStr=sh.read(); sh.close() 19929921a8fSScott Kruger 20029921a8fSScott Kruger ## Start with doing the tests 20129921a8fSScott Kruger # 20229921a8fSScott Kruger fsplit=fileStr.split("/*TEST\n")[1:] 20329921a8fSScott Kruger if len(fsplit)==0: 20429921a8fSScott Kruger if debug: print "No test found in: "+srcfile 20529921a8fSScott Kruger return {} 20629921a8fSScott Kruger # Allow for multiple "/*TEST" blocks even though it really should be 20729921a8fSScott Kruger # on 20829921a8fSScott Kruger srcTests=[] 20929921a8fSScott Kruger for t in fsplit: srcTests.append(t.split("TEST*/")[0]) 21029921a8fSScott Kruger testString=" ".join(srcTests) 21129921a8fSScott Kruger if len(testString.strip())==0: 21229921a8fSScott Kruger print "No test found in: "+srcfile 21329921a8fSScott Kruger return {} 21429921a8fSScott Kruger testDict[basename]=parseTests(testString,srcfile) 21529921a8fSScott Kruger 21629921a8fSScott Kruger ## Check and see if we have build reuqirements 21729921a8fSScott Kruger # 21829921a8fSScott Kruger if "/*T\n" in fileStr or "/*T " in fileStr: 21929921a8fSScott Kruger # The file info is already here and need to append 22029921a8fSScott Kruger Part1=fileStr.split("T*/")[0] 22129921a8fSScott Kruger fileInfo=Part1.split("/*T")[1] 22229921a8fSScott Kruger for bkey in buildkeys: 22329921a8fSScott Kruger if bkey+":" in fileInfo: 22429921a8fSScott Kruger testDict[basename][bkey]=fileInfo.split(bkey+":")[1].split("\n")[0].strip() 22529921a8fSScott Kruger 22629921a8fSScott Kruger os.chdir(curdir) 22729921a8fSScott Kruger return testDict 22829921a8fSScott Kruger 22929921a8fSScott Krugerdef parseTestDir(directory): 23029921a8fSScott Kruger """ 23129921a8fSScott Kruger Parse single example files and return dictionary of the form: 23229921a8fSScott Kruger testDict[srcfile][test][subtest] 23329921a8fSScott Kruger """ 23429921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 23529921a8fSScott Kruger basedir=os.path.realpath(directory) 23629921a8fSScott Kruger os.chdir(basedir) 23729921a8fSScott Kruger 23829921a8fSScott Kruger tDict={} 23929921a8fSScott Kruger for test_file in glob.glob("new_ex*.*"): 24029921a8fSScott Kruger tDict.update(parseTestFile(test_file)) 24129921a8fSScott Kruger 24229921a8fSScott Kruger os.chdir(curdir) 24329921a8fSScott Kruger return tDict 24429921a8fSScott Kruger 24529921a8fSScott Krugerdef printExParseDict(rDict): 24629921a8fSScott Kruger """ 24729921a8fSScott Kruger This is useful for debugging 24829921a8fSScott Kruger """ 24929921a8fSScott Kruger indent=" " 25029921a8fSScott Kruger for sfile in rDict: 25129921a8fSScott Kruger print "\n\n"+sfile 25229921a8fSScott Kruger for runex in rDict[sfile]: 25329921a8fSScott Kruger print indent+runex 25429921a8fSScott Kruger if type(rDict[sfile][runex])==types.StringType: 25529921a8fSScott Kruger print indent*2+rDict[sfile][runex] 25629921a8fSScott Kruger else: 25729921a8fSScott Kruger for var in rDict[sfile][runex]: 25829921a8fSScott Kruger if var.startswith("test"): 25929921a8fSScott Kruger print indent*2+var 26029921a8fSScott Kruger for var2 in rDict[sfile][runex][var]: 26129921a8fSScott Kruger print indent*3+var2+": "+str(rDict[sfile][runex][var][var2]) 26229921a8fSScott Kruger else: 26329921a8fSScott Kruger print indent*2+var+": "+str(rDict[sfile][runex][var]) 26429921a8fSScott Kruger return 26529921a8fSScott Kruger 26629921a8fSScott Krugerdef main(directory='',test_file='',verbosity=0): 26729921a8fSScott Kruger 26829921a8fSScott Kruger if directory: 26929921a8fSScott Kruger tDict=parseTestDir(directory) 27029921a8fSScott Kruger else: 27129921a8fSScott Kruger tDict=parseTestFile(test_file) 27229921a8fSScott Kruger if verbosity>0: printExParseDict(tDict) 27329921a8fSScott Kruger 27429921a8fSScott Kruger return 27529921a8fSScott Kruger 27629921a8fSScott Krugerif __name__ == '__main__': 27729921a8fSScott Kruger import optparse 27829921a8fSScott Kruger parser = optparse.OptionParser() 27929921a8fSScott Kruger parser.add_option('-d', '--directory', dest='directory', 28029921a8fSScott Kruger default="", help='Directory containing files to parse') 28129921a8fSScott Kruger parser.add_option('-t', '--test_file', dest='test_file', 28229921a8fSScott Kruger default="", help='Test file, e.g., ex1.c, to parse') 28329921a8fSScott Kruger parser.add_option('-v', '--verbosity', dest='verbosity', 28429921a8fSScott Kruger help='Verbosity of output by level: 1, 2, or 3', default=0) 28529921a8fSScott Kruger opts, extra_args = parser.parse_args() 28629921a8fSScott Kruger 28729921a8fSScott Kruger if extra_args: 28829921a8fSScott Kruger import sys 28929921a8fSScott Kruger sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args)) 29029921a8fSScott Kruger exit(1) 29129921a8fSScott Kruger if not opts.test_file and not opts.directory: 29229921a8fSScott Kruger print "test file or directory is required" 29329921a8fSScott Kruger parser.print_usage() 29429921a8fSScott Kruger sys.exit() 29529921a8fSScott Kruger 29629921a8fSScott Kruger # Need verbosity to be an integer 29729921a8fSScott Kruger try: 29829921a8fSScott Kruger verbosity=int(opts.verbosity) 29929921a8fSScott Kruger except: 30029921a8fSScott Kruger raise Exception("Error: Verbosity must be integer") 30129921a8fSScott Kruger 30229921a8fSScott Kruger main(directory=opts.directory,test_file=opts.test_file,verbosity=verbosity) 303