1*29921a8fSScott Kruger#!/usr/bin/env python 2*29921a8fSScott Kruger""" 3*29921a8fSScott KrugerParse the test file and return a dictionary. 4*29921a8fSScott Kruger 5*29921a8fSScott KrugerQuick usage:: 6*29921a8fSScott Kruger 7*29921a8fSScott Kruger bin/maint/testparse.py -t src/ksp/ksp/examples/tutorials/ex1.c 8*29921a8fSScott Kruger 9*29921a8fSScott KrugerFrom the command line, it prints out the dictionary. 10*29921a8fSScott KrugerThis is meant to be used by other scripts, but it is 11*29921a8fSScott Krugeruseful to debug individual files. 12*29921a8fSScott Kruger 13*29921a8fSScott Kruger 14*29921a8fSScott Kruger 15*29921a8fSScott KrugerExample language 16*29921a8fSScott Kruger---------------- 17*29921a8fSScott Kruger/*T 18*29921a8fSScott Kruger Concepts: 19*29921a8fSScott Kruger requires: moab 20*29921a8fSScott KrugerT*/ 21*29921a8fSScott Kruger 22*29921a8fSScott Kruger 23*29921a8fSScott Kruger 24*29921a8fSScott Kruger/*TEST 25*29921a8fSScott Kruger 26*29921a8fSScott Kruger test: 27*29921a8fSScott 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 28*29921a8fSScott Kruger output_file: output/ex25_1.out 29*29921a8fSScott Kruger redirect_file: ex25_1.tmp 30*29921a8fSScott Kruger 31*29921a8fSScott Kruger test: 32*29921a8fSScott Kruger suffix: 2 33*29921a8fSScott Kruger nsize: 2 34*29921a8fSScott 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 35*29921a8fSScott Kruger 36*29921a8fSScott KrugerTEST*/ 37*29921a8fSScott Kruger 38*29921a8fSScott Kruger""" 39*29921a8fSScott Kruger 40*29921a8fSScott Krugerimport os, re, glob, types 41*29921a8fSScott Krugerfrom distutils.sysconfig import parse_makefile 42*29921a8fSScott Krugerimport sys 43*29921a8fSScott Krugerimport logging 44*29921a8fSScott Krugersys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) 45*29921a8fSScott Kruger 46*29921a8fSScott Krugerimport inspect 47*29921a8fSScott Krugerthisscriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 48*29921a8fSScott Krugermaintdir=os.path.join(os.path.join(os.path.dirname(thisscriptdir),'bin'),'maint') 49*29921a8fSScott Krugersys.path.insert(0,maintdir) 50*29921a8fSScott Kruger 51*29921a8fSScott Kruger# These are special keys describing build 52*29921a8fSScott Krugerbuildkeys="requires TODO SKIP depends".split() 53*29921a8fSScott Kruger 54*29921a8fSScott Krugerdef _stripIndent(block,srcfile): 55*29921a8fSScott Kruger """ 56*29921a8fSScott Kruger Go through and remove a level of indentation 57*29921a8fSScott Kruger Also strip of trailing whitespace 58*29921a8fSScott Kruger """ 59*29921a8fSScott Kruger # The first entry should be test: but it might be indented. 60*29921a8fSScott Kruger ext=os.path.splitext(srcfile)[1] 61*29921a8fSScott Kruger for line in block.split("\n"): 62*29921a8fSScott Kruger if not line.strip(): continue 63*29921a8fSScott Kruger stripstr=" " if not ext.startswith(".F") else "! " 64*29921a8fSScott Kruger nspace=len(line)-len(line.lstrip(stripstr)) 65*29921a8fSScott Kruger newline=line[nspace:] 66*29921a8fSScott Kruger break 67*29921a8fSScott Kruger 68*29921a8fSScott Kruger # Strip off any indentation for the whole string and any trailing 69*29921a8fSScott Kruger # whitespace for convenience 70*29921a8fSScott Kruger newTestStr="\n" 71*29921a8fSScott Kruger for line in block.split("\n"): 72*29921a8fSScott Kruger if not line.strip(): continue 73*29921a8fSScott Kruger newline=line[nspace:] 74*29921a8fSScott Kruger newTestStr=newTestStr+newline.rstrip()+"\n" 75*29921a8fSScott Kruger 76*29921a8fSScott Kruger return newTestStr 77*29921a8fSScott Kruger 78*29921a8fSScott Krugerdef parseTest(testStr,srcfile): 79*29921a8fSScott Kruger """ 80*29921a8fSScott Kruger This parses an individual test 81*29921a8fSScott Kruger YAML is hierarchial so should use a state machine in the general case, 82*29921a8fSScott Kruger but in practice we only support to levels of test: 83*29921a8fSScott Kruger """ 84*29921a8fSScott Kruger basename=os.path.basename(srcfile) 85*29921a8fSScott Kruger # Handle the new at the begininng 86*29921a8fSScott Kruger bn=re.sub("new_","",basename) 87*29921a8fSScott Kruger # This is the default 88*29921a8fSScott Kruger testname="run"+os.path.splitext(bn)[0] 89*29921a8fSScott Kruger 90*29921a8fSScott Kruger # Tests that have default everything (so empty effectively) 91*29921a8fSScott Kruger if len(testStr)==0: return testname, {} 92*29921a8fSScott Kruger 93*29921a8fSScott Kruger striptest=_stripIndent(testStr,srcfile) 94*29921a8fSScott Kruger 95*29921a8fSScott Kruger # go through and parse 96*29921a8fSScott Kruger subtestnum=0 97*29921a8fSScott Kruger subdict={} 98*29921a8fSScott Kruger for line in striptest.split("\n"): 99*29921a8fSScott Kruger if not line.strip(): continue 100*29921a8fSScott Kruger var=line.split(":")[0].strip() 101*29921a8fSScott Kruger val=line.split(":")[1].strip() 102*29921a8fSScott Kruger # Start by seeing if we are in a subtest 103*29921a8fSScott Kruger if line.startswith(" "): 104*29921a8fSScott Kruger subdict[subtestname][var]=val 105*29921a8fSScott Kruger # Determine subtest name and make dict 106*29921a8fSScott Kruger elif var=="test": 107*29921a8fSScott Kruger subtestname="test"+str(subtestnum) 108*29921a8fSScott Kruger subdict[subtestname]={} 109*29921a8fSScott Kruger if not subdict.has_key("subtests"): subdict["subtests"]=[] 110*29921a8fSScott Kruger subdict["subtests"].append(subtestname) 111*29921a8fSScott Kruger subtestnum=subtestnum+1 112*29921a8fSScott Kruger # The reset are easy 113*29921a8fSScott Kruger else: 114*29921a8fSScott Kruger subdict[var]=val 115*29921a8fSScott Kruger if var=="suffix": 116*29921a8fSScott Kruger if len(val)>0: 117*29921a8fSScott Kruger testname=testname+"_"+val 118*29921a8fSScott Kruger 119*29921a8fSScott Kruger return testname,subdict 120*29921a8fSScott Kruger 121*29921a8fSScott Krugerdef parseTests(testStr,srcfile): 122*29921a8fSScott Kruger """ 123*29921a8fSScott Kruger Parse the yaml string describing tests and return 124*29921a8fSScott Kruger a dictionary with the info in the form of: 125*29921a8fSScott Kruger testDict[test][subtest] 126*29921a8fSScott Kruger This is an inelegant parser as we do not wish to 127*29921a8fSScott Kruger introduce a new dependency by bringing in pyyaml. 128*29921a8fSScott Kruger The advantage is that validation can be done as 129*29921a8fSScott Kruger it is parsed (e.g., 'test' is the only top-level node) 130*29921a8fSScott Kruger """ 131*29921a8fSScott Kruger 132*29921a8fSScott Kruger testDict={} 133*29921a8fSScott Kruger 134*29921a8fSScott Kruger # The first entry should be test: but it might be indented. 135*29921a8fSScott Kruger newTestStr=_stripIndent(testStr,srcfile) 136*29921a8fSScott Kruger 137*29921a8fSScott Kruger # Now go through each test. First elem in split is blank 138*29921a8fSScott Kruger for test in newTestStr.split("\ntest:\n")[1:]: 139*29921a8fSScott Kruger testname,subdict=parseTest(test,srcfile) 140*29921a8fSScott Kruger if testDict.has_key(testname): 141*29921a8fSScott Kruger print "Multiple test names specified: "+testname+" in file: "+srcfile 142*29921a8fSScott Kruger testDict[testname]=subdict 143*29921a8fSScott Kruger 144*29921a8fSScott Kruger return testDict 145*29921a8fSScott Kruger 146*29921a8fSScott Krugerdef parseTestFile(srcfile): 147*29921a8fSScott Kruger """ 148*29921a8fSScott Kruger Parse single example files and return dictionary of the form: 149*29921a8fSScott Kruger testDict[srcfile][test][subtest] 150*29921a8fSScott Kruger """ 151*29921a8fSScott Kruger debug=False 152*29921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 153*29921a8fSScott Kruger basedir=os.path.dirname(os.path.realpath(srcfile)) 154*29921a8fSScott Kruger basename=os.path.basename(srcfile) 155*29921a8fSScott Kruger os.chdir(basedir) 156*29921a8fSScott Kruger 157*29921a8fSScott Kruger testDict={} 158*29921a8fSScott Kruger sh=open(srcfile,"r"); fileStr=sh.read(); sh.close() 159*29921a8fSScott Kruger 160*29921a8fSScott Kruger ## Start with doing the tests 161*29921a8fSScott Kruger # 162*29921a8fSScott Kruger fsplit=fileStr.split("/*TEST\n")[1:] 163*29921a8fSScott Kruger if len(fsplit)==0: 164*29921a8fSScott Kruger if debug: print "No test found in: "+srcfile 165*29921a8fSScott Kruger return {} 166*29921a8fSScott Kruger # Allow for multiple "/*TEST" blocks even though it really should be 167*29921a8fSScott Kruger # on 168*29921a8fSScott Kruger srcTests=[] 169*29921a8fSScott Kruger for t in fsplit: srcTests.append(t.split("TEST*/")[0]) 170*29921a8fSScott Kruger testString=" ".join(srcTests) 171*29921a8fSScott Kruger if len(testString.strip())==0: 172*29921a8fSScott Kruger print "No test found in: "+srcfile 173*29921a8fSScott Kruger return {} 174*29921a8fSScott Kruger testDict[basename]=parseTests(testString,srcfile) 175*29921a8fSScott Kruger 176*29921a8fSScott Kruger ## Check and see if we have build reuqirements 177*29921a8fSScott Kruger # 178*29921a8fSScott Kruger if "/*T\n" in fileStr or "/*T " in fileStr: 179*29921a8fSScott Kruger # The file info is already here and need to append 180*29921a8fSScott Kruger Part1=fileStr.split("T*/")[0] 181*29921a8fSScott Kruger fileInfo=Part1.split("/*T")[1] 182*29921a8fSScott Kruger for bkey in buildkeys: 183*29921a8fSScott Kruger if bkey+":" in fileInfo: 184*29921a8fSScott Kruger testDict[basename][bkey]=fileInfo.split(bkey+":")[1].split("\n")[0].strip() 185*29921a8fSScott Kruger 186*29921a8fSScott Kruger os.chdir(curdir) 187*29921a8fSScott Kruger return testDict 188*29921a8fSScott Kruger 189*29921a8fSScott Krugerdef parseTestDir(directory): 190*29921a8fSScott Kruger """ 191*29921a8fSScott Kruger Parse single example files and return dictionary of the form: 192*29921a8fSScott Kruger testDict[srcfile][test][subtest] 193*29921a8fSScott Kruger """ 194*29921a8fSScott Kruger curdir=os.path.realpath(os.path.curdir) 195*29921a8fSScott Kruger basedir=os.path.realpath(directory) 196*29921a8fSScott Kruger os.chdir(basedir) 197*29921a8fSScott Kruger 198*29921a8fSScott Kruger tDict={} 199*29921a8fSScott Kruger for test_file in glob.glob("new_ex*.*"): 200*29921a8fSScott Kruger tDict.update(parseTestFile(test_file)) 201*29921a8fSScott Kruger 202*29921a8fSScott Kruger os.chdir(curdir) 203*29921a8fSScott Kruger return tDict 204*29921a8fSScott Kruger 205*29921a8fSScott Krugerdef printExParseDict(rDict): 206*29921a8fSScott Kruger """ 207*29921a8fSScott Kruger This is useful for debugging 208*29921a8fSScott Kruger """ 209*29921a8fSScott Kruger indent=" " 210*29921a8fSScott Kruger for sfile in rDict: 211*29921a8fSScott Kruger print "\n\n"+sfile 212*29921a8fSScott Kruger for runex in rDict[sfile]: 213*29921a8fSScott Kruger print indent+runex 214*29921a8fSScott Kruger if type(rDict[sfile][runex])==types.StringType: 215*29921a8fSScott Kruger print indent*2+rDict[sfile][runex] 216*29921a8fSScott Kruger else: 217*29921a8fSScott Kruger for var in rDict[sfile][runex]: 218*29921a8fSScott Kruger if var.startswith("test"): 219*29921a8fSScott Kruger print indent*2+var 220*29921a8fSScott Kruger for var2 in rDict[sfile][runex][var]: 221*29921a8fSScott Kruger print indent*3+var2+": "+str(rDict[sfile][runex][var][var2]) 222*29921a8fSScott Kruger else: 223*29921a8fSScott Kruger print indent*2+var+": "+str(rDict[sfile][runex][var]) 224*29921a8fSScott Kruger return 225*29921a8fSScott Kruger 226*29921a8fSScott Krugerdef main(directory='',test_file='',verbosity=0): 227*29921a8fSScott Kruger 228*29921a8fSScott Kruger if directory: 229*29921a8fSScott Kruger tDict=parseTestDir(directory) 230*29921a8fSScott Kruger else: 231*29921a8fSScott Kruger tDict=parseTestFile(test_file) 232*29921a8fSScott Kruger if verbosity>0: printExParseDict(tDict) 233*29921a8fSScott Kruger 234*29921a8fSScott Kruger return 235*29921a8fSScott Kruger 236*29921a8fSScott Krugerif __name__ == '__main__': 237*29921a8fSScott Kruger import optparse 238*29921a8fSScott Kruger parser = optparse.OptionParser() 239*29921a8fSScott Kruger parser.add_option('-d', '--directory', dest='directory', 240*29921a8fSScott Kruger default="", help='Directory containing files to parse') 241*29921a8fSScott Kruger parser.add_option('-t', '--test_file', dest='test_file', 242*29921a8fSScott Kruger default="", help='Test file, e.g., ex1.c, to parse') 243*29921a8fSScott Kruger parser.add_option('-v', '--verbosity', dest='verbosity', 244*29921a8fSScott Kruger help='Verbosity of output by level: 1, 2, or 3', default=0) 245*29921a8fSScott Kruger opts, extra_args = parser.parse_args() 246*29921a8fSScott Kruger 247*29921a8fSScott Kruger if extra_args: 248*29921a8fSScott Kruger import sys 249*29921a8fSScott Kruger sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args)) 250*29921a8fSScott Kruger exit(1) 251*29921a8fSScott Kruger if not opts.test_file and not opts.directory: 252*29921a8fSScott Kruger print "test file or directory is required" 253*29921a8fSScott Kruger parser.print_usage() 254*29921a8fSScott Kruger sys.exit() 255*29921a8fSScott Kruger 256*29921a8fSScott Kruger # Need verbosity to be an integer 257*29921a8fSScott Kruger try: 258*29921a8fSScott Kruger verbosity=int(opts.verbosity) 259*29921a8fSScott Kruger except: 260*29921a8fSScott Kruger raise Exception("Error: Verbosity must be integer") 261*29921a8fSScott Kruger 262*29921a8fSScott Kruger main(directory=opts.directory,test_file=opts.test_file,verbosity=verbosity) 263