xref: /petsc/config/testparse.py (revision 29921a8f05c7587ede8fed409dd5e8ff5aa91522)
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