xref: /petsc/config/testparse.py (revision 4d82b48c19d7ae0cc74d3fb2fe6300ce426af820)
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 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"""
3729921a8fSScott Kruger
3829921a8fSScott Krugerimport os, re, glob, types
3929921a8fSScott Krugerimport sys
4029921a8fSScott Krugerimport logging
4129921a8fSScott Krugersys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
4229921a8fSScott Kruger
4329921a8fSScott Krugerimport inspect
4429921a8fSScott Krugerthisscriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
4529921a8fSScott Krugermaintdir=os.path.join(os.path.join(os.path.dirname(thisscriptdir),'bin'),'maint')
4629921a8fSScott Krugersys.path.insert(0,maintdir)
4729921a8fSScott Kruger
4829921a8fSScott Kruger# These are special keys describing build
4929921a8fSScott Krugerbuildkeys="requires TODO SKIP depends".split()
5029921a8fSScott Kruger
510a091e3eSScott Krugeracceptedkeys="test nsize requires command suffix args filter filter_output localrunfiles comments TODO SKIP output_file timeoutfactor".split()
5244776d8cSScott Krugerappendlist="args requires comments".split()
5368a9e459SScott Kruger
5468a9e459SScott Krugerimport re
5568a9e459SScott Kruger
5644776d8cSScott Krugerdef _stripIndent(block,srcfile,entireBlock=False,fileNums=[]):
5729921a8fSScott Kruger  """
5829921a8fSScott Kruger  Go through and remove a level of indentation
5929921a8fSScott Kruger  Also strip of trailing whitespace
6029921a8fSScott Kruger  """
6129921a8fSScott Kruger  # The first entry should be test: but it might be indented.
6229921a8fSScott Kruger  ext=os.path.splitext(srcfile)[1]
6344776d8cSScott Kruger  stripstr=" "
6466db876fSScott Kruger  if len(fileNums)>0: lineNum=fileNums[0]-1
658ccd5183SScott Kruger  for lline in block.split("\n"):
6666db876fSScott Kruger    if len(fileNums)>0: lineNum+=1
678ccd5183SScott Kruger    line=lline[1:] if lline.startswith("!") else lline
6829921a8fSScott Kruger    if not line.strip(): continue
69c4b80baaSScott Kruger    if line.strip().startswith('#'): continue
7044776d8cSScott Kruger    if entireBlock:
7144776d8cSScott Kruger      var=line.split(":")[0].strip()
72aec507c4SScott Kruger      if not var in ['test','testset','build']:
7366db876fSScott Kruger        raise Exception("Formatting error: Cannot find test in file: "+srcfile+" at line: "+str(lineNum)+"\n")
7429921a8fSScott Kruger    nspace=len(line)-len(line.lstrip(stripstr))
7529921a8fSScott Kruger    newline=line[nspace:]
7629921a8fSScott Kruger    break
7729921a8fSScott Kruger
7829921a8fSScott Kruger  # Strip off any indentation for the whole string and any trailing
7929921a8fSScott Kruger  # whitespace for convenience
8029921a8fSScott Kruger  newTestStr="\n"
8144776d8cSScott Kruger  if len(fileNums)>0: lineNum=fileNums[0]-1
8244776d8cSScott Kruger  firstPass=True
838ccd5183SScott Kruger  for lline in block.split("\n"):
8444776d8cSScott Kruger    if len(fileNums)>0: lineNum+=1
858ccd5183SScott Kruger    line=lline[1:] if lline.startswith("!") else lline
8629921a8fSScott Kruger    if not line.strip(): continue
87c4b80baaSScott Kruger    if line.strip().startswith('#'):
88c4b80baaSScott Kruger      newTestStr+=line+'\n'
89c4b80baaSScott Kruger    else:
9029921a8fSScott Kruger      newline=line[nspace:]
91c4b80baaSScott Kruger      newTestStr+=newline.rstrip()+"\n"
9244776d8cSScott Kruger    # Do some basic indentation checks
9344776d8cSScott Kruger    if entireBlock:
9444776d8cSScott Kruger      # Don't need to check comment lines
9544776d8cSScott Kruger      if line.strip().startswith('#'): continue
9644776d8cSScott Kruger      if not newline.startswith(" "):
9744776d8cSScott Kruger        var=newline.split(":")[0].strip()
98aec507c4SScott Kruger        if not var in ['test','testset','build']:
9944776d8cSScott Kruger          err="Formatting error in file "+srcfile+" at line: " +line+"\n"
10044776d8cSScott Kruger          if len(fileNums)>0:
10144776d8cSScott Kruger            raise Exception(err+"Check indentation at line number: "+str(lineNum))
10244776d8cSScott Kruger          else:
10344776d8cSScott Kruger            raise Exception(err)
10444776d8cSScott Kruger      else:
10544776d8cSScott Kruger        var=line.split(":")[0].strip()
106aec507c4SScott Kruger        if var in ['test','testset','build']:
10744776d8cSScott Kruger          subnspace=len(line)-len(line.lstrip(stripstr))
10844776d8cSScott Kruger          if firstPass:
10944776d8cSScott Kruger            firstsubnspace=subnspace
11044776d8cSScott Kruger            firstPass=False
11144776d8cSScott Kruger          else:
11244776d8cSScott Kruger            if firstsubnspace!=subnspace:
11344776d8cSScott Kruger              err="Formatting subtest error in file "+srcfile+" at line: " +line+"\n"
11444776d8cSScott Kruger              if len(fileNums)>0:
11544776d8cSScott Kruger                raise Exception(err+"Check indentation at line number: "+str(lineNum))
11644776d8cSScott Kruger              else:
11744776d8cSScott Kruger                raise Exception(err)
11844776d8cSScott Kruger
11929921a8fSScott Kruger
12029921a8fSScott Kruger  return newTestStr
12129921a8fSScott Kruger
122aae9f2d9SScott Krugerdef parseLoopArgs(varset):
12344776d8cSScott Kruger  """
124aae9f2d9SScott Kruger  Given:   String containing loop variables
125aae9f2d9SScott Kruger  Return: tuple containing separate/shared and string of loop vars
12644776d8cSScott Kruger  """
127aae9f2d9SScott Kruger  keynm=varset.split("{{")[0].strip()
128aae9f2d9SScott Kruger  if not keynm.strip(): keynm='nsize'
129aae9f2d9SScott Kruger  lvars=varset.split('{{')[1].split('}')[0]
130aae9f2d9SScott Kruger  suffx=varset.split('{{')[1].split('}')[1]
131aae9f2d9SScott Kruger  ftype='separate' if suffx.startswith('separate') else 'shared'
132aae9f2d9SScott Kruger  return keynm,lvars,ftype
13344776d8cSScott Kruger
134e53dc769SScott Krugerdef _getSeparateTestvars(testDict):
135e53dc769SScott Kruger  """
136e53dc769SScott Kruger  Given: dictionary that may have
137e53dc769SScott Kruger  Return:  Variables that cause a test split
138e53dc769SScott Kruger  """
139e53dc769SScott Kruger  vals=None
140e53dc769SScott Kruger  sepvars=[]
141e53dc769SScott Kruger  # Check nsize
142e53dc769SScott Kruger  if testDict.has_key('nsize'):
143e53dc769SScott Kruger    varset=testDict['nsize']
144aae9f2d9SScott Kruger    if '{{' in varset:
145aae9f2d9SScott Kruger      keynm,lvars,ftype=parseLoopArgs(varset)
146aae9f2d9SScott Kruger      if ftype=='separate': sepvars.append(keynm)
147e53dc769SScott Kruger
148e53dc769SScott Kruger  # Now check args
149e53dc769SScott Kruger  if not testDict.has_key('args'): return sepvars
150e53dc769SScott Kruger  for varset in re.split('-(?=[a-zA-Z])',testDict['args']):
151e53dc769SScott Kruger    if not varset.strip(): continue
152aae9f2d9SScott Kruger    if '{{' in varset:
153e53dc769SScott Kruger      # Assuming only one for loop per var specification
154aae9f2d9SScott Kruger      keynm,lvars,ftype=parseLoopArgs(varset)
155aae9f2d9SScott Kruger      if ftype=='separate': sepvars.append(keynm)
156e53dc769SScott Kruger
157e53dc769SScott Kruger  return sepvars
158e53dc769SScott Kruger
159*4d82b48cSScott Krugerdef _getNewArgs(args):
160*4d82b48cSScott Kruger  """
161*4d82b48cSScott Kruger  Given: String that has args that might have loops in them
162*4d82b48cSScott Kruger  Return:  All of the arguments/values that do not have
163*4d82b48cSScott Kruger             for 'separate output' in for loops
164*4d82b48cSScott Kruger  """
165*4d82b48cSScott Kruger  newargs=''
166*4d82b48cSScott Kruger  if not args.strip(): return args
167*4d82b48cSScott Kruger  for varset in re.split('-(?=[a-zA-Z])',args):
168*4d82b48cSScott Kruger    if not varset.strip(): continue
169*4d82b48cSScott Kruger    if '{{' not in varset:
170*4d82b48cSScott Kruger      if 'separate' not in varset:
171*4d82b48cSScott Kruger        newargs+="-"+varset.strip()+" "
172*4d82b48cSScott Kruger
173*4d82b48cSScott Kruger  return newargs
174*4d82b48cSScott Kruger
17544776d8cSScott Krugerdef _getVarVals(findvar,testDict):
17644776d8cSScott Kruger  """
17744776d8cSScott Kruger  Given: variable that is either nsize or in args
178*4d82b48cSScott Kruger  Return:  Values to loop over and the other arguments
179*4d82b48cSScott Kruger    Note that we keep the other arguments even if they have
180*4d82b48cSScott Kruger    for loops to enable stepping through all of the for lops
18144776d8cSScott Kruger  """
182*4d82b48cSScott Kruger  save_vals=None
18344776d8cSScott Kruger  if findvar=='nsize':
184e53dc769SScott Kruger    varset=testDict[findvar]
185*4d82b48cSScott Kruger    keynm,save_vals,ftype=parseLoopArgs('nsize '+varset)
18644776d8cSScott Kruger  else:
18744776d8cSScott Kruger    varlist=[]
18844776d8cSScott Kruger    for varset in re.split('-(?=[a-zA-Z])',testDict['args']):
18944776d8cSScott Kruger      if not varset.strip(): continue
190*4d82b48cSScott Kruger      if '{{' not in varset: continue
191aae9f2d9SScott Kruger      keyvar,vals,ftype=parseLoopArgs(varset)
192*4d82b48cSScott Kruger      if keyvar==findvar:
193*4d82b48cSScott Kruger        save_vals=vals
19444776d8cSScott Kruger
195*4d82b48cSScott Kruger  if not save_vals: raise StandardError("Could not find separate_testvar: "+findvar)
196*4d82b48cSScott Kruger  return save_vals
19744776d8cSScott Kruger
19844776d8cSScott Krugerdef genTestsSeparateTestvars(intests,indicts):
19944776d8cSScott Kruger  """
200e53dc769SScott Kruger  Given: testname, sdict with 'separate_testvars
20144776d8cSScott Kruger  Return: testnames,sdicts: List of generated tests
202*4d82b48cSScott Kruger    The tricky part here is the {{ ... }separate output}
203*4d82b48cSScott Kruger    that can be used multiple times
20444776d8cSScott Kruger  """
20544776d8cSScott Kruger  testnames=[]; sdicts=[]
20644776d8cSScott Kruger  for i in range(len(intests)):
20744776d8cSScott Kruger    testname=intests[i]; sdict=indicts[i]; i+=1
208e53dc769SScott Kruger    separate_testvars=_getSeparateTestvars(sdict)
209e53dc769SScott Kruger    if len(separate_testvars)>0:
210*4d82b48cSScott Kruger      sep_dicts=[sdict.copy()]
211*4d82b48cSScott Kruger      if 'args' in sep_dicts[0]:
212*4d82b48cSScott Kruger        sep_dicts[0]['args']=_getNewArgs(sdict['args'])
213*4d82b48cSScott Kruger      sep_testnames=[testname]
214e53dc769SScott Kruger      for kvar in separate_testvars:
215*4d82b48cSScott Kruger        kvals=_getVarVals(kvar,sdict)
216*4d82b48cSScott Kruger
217*4d82b48cSScott Kruger        # Have to do loop over previous var/val combos as well
218*4d82b48cSScott Kruger        # and accumulate as we go
219*4d82b48cSScott Kruger        val_testnames=[]; val_dicts=[]
22044776d8cSScott Kruger        for val in kvals.split():
221541979b6SScott Kruger          gensuffix="_"+kvar+"-"+val.replace(',','__')
222*4d82b48cSScott Kruger          for kvaltestnm in sep_testnames:
223*4d82b48cSScott Kruger            val_testnames.append(kvaltestnm+gensuffix)
224*4d82b48cSScott Kruger          for kv in sep_dicts:
225*4d82b48cSScott Kruger            kvardict=kv.copy()
226*4d82b48cSScott Kruger            # If the last var then we have the final version
227*4d82b48cSScott Kruger            if 'suffix' in sdict:
228*4d82b48cSScott Kruger              kvardict['suffix']+=gensuffix
2290bcc1aabSScott Kruger            else:
2300bcc1aabSScott Kruger              kvardict['suffix']=gensuffix
23144776d8cSScott Kruger            if kvar=='nsize':
23244776d8cSScott Kruger              kvardict[kvar]=val
23344776d8cSScott Kruger            else:
234*4d82b48cSScott Kruger              kvardict['args']+="-"+kvar+" "+val+" "
235*4d82b48cSScott Kruger            val_dicts.append(kvardict)
236*4d82b48cSScott Kruger        sep_testnames=val_testnames
237*4d82b48cSScott Kruger        sep_dicts=val_dicts
238*4d82b48cSScott Kruger      testnames+=sep_testnames
239*4d82b48cSScott Kruger      sdicts+=sep_dicts
24044776d8cSScott Kruger    else:
24144776d8cSScott Kruger      testnames.append(testname)
24244776d8cSScott Kruger      sdicts.append(sdict)
24344776d8cSScott Kruger  return testnames,sdicts
24444776d8cSScott Kruger
24544776d8cSScott Krugerdef genTestsSubtestSuffix(testnames,sdicts):
24644776d8cSScott Kruger  """
24744776d8cSScott Kruger  Given: testname, sdict with separate_testvars
24844776d8cSScott Kruger  Return: testnames,sdicts: List of generated tests
24944776d8cSScott Kruger  """
25044776d8cSScott Kruger  tnms=[]; sdcts=[]
25144776d8cSScott Kruger  for i in range(len(testnames)):
25244776d8cSScott Kruger    testname=testnames[i]
25344776d8cSScott Kruger    rmsubtests=[]; keepSubtests=False
25444776d8cSScott Kruger    if sdicts[i].has_key('subtests'):
25544776d8cSScott Kruger      for stest in sdicts[i]["subtests"]:
25644776d8cSScott Kruger        if sdicts[i][stest].has_key('suffix'):
25744776d8cSScott Kruger          rmsubtests.append(stest)
25844776d8cSScott Kruger          gensuffix="_"+sdicts[i][stest]['suffix']
25944776d8cSScott Kruger          newtestnm=testname+gensuffix
26044776d8cSScott Kruger          tnms.append(newtestnm)
26144776d8cSScott Kruger          newsdict=sdicts[i].copy()
26244776d8cSScott Kruger          del newsdict['subtests']
26344776d8cSScott Kruger          # Have to hand update
26444776d8cSScott Kruger          # Append
26544776d8cSScott Kruger          for kup in appendlist:
26644776d8cSScott Kruger            if sdicts[i][stest].has_key(kup):
26744776d8cSScott Kruger              if sdicts[i].has_key(kup):
26844776d8cSScott Kruger                newsdict[kup]=sdicts[i][kup]+" "+sdicts[i][stest][kup]
26944776d8cSScott Kruger              else:
27044776d8cSScott Kruger                newsdict[kup]=sdicts[i][stest][kup]
27144776d8cSScott Kruger          # Promote
27244776d8cSScott Kruger          for kup in acceptedkeys:
27344776d8cSScott Kruger            if kup in appendlist: continue
27444776d8cSScott Kruger            if sdicts[i][stest].has_key(kup):
27544776d8cSScott Kruger              newsdict[kup]=sdicts[i][stest][kup]
27644776d8cSScott Kruger          # Cleanup
27744776d8cSScott Kruger          for st in sdicts[i]["subtests"]: del newsdict[st]
27844776d8cSScott Kruger          sdcts.append(newsdict)
27944776d8cSScott Kruger        else:
28044776d8cSScott Kruger          keepSubtests=True
28144776d8cSScott Kruger    else:
28244776d8cSScott Kruger      tnms.append(testnames[i])
28344776d8cSScott Kruger      sdcts.append(sdicts[i])
2840bcc1aabSScott Kruger    # If a subtest without a suffix exists, then save it
28544776d8cSScott Kruger    if keepSubtests:
28644776d8cSScott Kruger      tnms.append(testnames[i])
2870bcc1aabSScott Kruger      newsdict=sdicts[i].copy()
2880bcc1aabSScott Kruger      # Prune the tests to prepare for keeping
2890bcc1aabSScott Kruger      for rmtest in rmsubtests:
2900bcc1aabSScott Kruger        newsdict['subtests'].remove(rmtest)
2910bcc1aabSScott Kruger        del newsdict[rmtest]
2920bcc1aabSScott Kruger      sdcts.append(newsdict)
29344776d8cSScott Kruger    i+=1
29444776d8cSScott Kruger  return tnms,sdcts
29544776d8cSScott Kruger
29644776d8cSScott Krugerdef splitTests(testname,sdict):
29744776d8cSScott Kruger  """
29844776d8cSScott Kruger  Given: testname and YAML-generated dictionary
29944776d8cSScott Kruger  Return: list of names and dictionaries corresponding to each test
30044776d8cSScott Kruger          given that the YAML language allows for multiple tests
30144776d8cSScott Kruger  """
30244776d8cSScott Kruger
30344776d8cSScott Kruger  # Order: Parent sep_tv, subtests suffix, subtests sep_tv
30444776d8cSScott Kruger  testnames,sdicts=genTestsSeparateTestvars([testname],[sdict])
30544776d8cSScott Kruger  testnames,sdicts=genTestsSubtestSuffix(testnames,sdicts)
30644776d8cSScott Kruger  testnames,sdicts=genTestsSeparateTestvars(testnames,sdicts)
30744776d8cSScott Kruger
30844776d8cSScott Kruger  # Because I am altering the list, I do this in passes.  Inelegant
30944776d8cSScott Kruger
31044776d8cSScott Kruger  return testnames, sdicts
31144776d8cSScott Kruger
3126cecdbdcSScott Krugerdef parseTest(testStr,srcfile,verbosity):
31329921a8fSScott Kruger  """
31429921a8fSScott Kruger  This parses an individual test
31529921a8fSScott Kruger  YAML is hierarchial so should use a state machine in the general case,
31653f2a965SBarry Smith  but in practice we only support two levels of test:
31729921a8fSScott Kruger  """
31829921a8fSScott Kruger  basename=os.path.basename(srcfile)
31929921a8fSScott Kruger  # Handle the new at the begininng
32029921a8fSScott Kruger  bn=re.sub("new_","",basename)
32129921a8fSScott Kruger  # This is the default
32229921a8fSScott Kruger  testname="run"+os.path.splitext(bn)[0]
32329921a8fSScott Kruger
32429921a8fSScott Kruger  # Tests that have default everything (so empty effectively)
32578659935SScott Kruger  if len(testStr)==0: return [testname], [{}]
32629921a8fSScott Kruger
32729921a8fSScott Kruger  striptest=_stripIndent(testStr,srcfile)
32829921a8fSScott Kruger
32929921a8fSScott Kruger  # go through and parse
33029921a8fSScott Kruger  subtestnum=0
33129921a8fSScott Kruger  subdict={}
33268a9e459SScott Kruger  comments=[]
33368a9e459SScott Kruger  indentlevel=0
33468a9e459SScott Kruger  for ln in striptest.split("\n"):
335c4b80baaSScott Kruger    line=ln.split('#')[0].rstrip()
336cadd188bSScott Kruger    if verbosity>2: print(line)
3370bcc1aabSScott Kruger    comment=("" if len(ln.split("#"))>0 else " ".join(ln.split("#")[1:]).strip())
33868a9e459SScott Kruger    if comment: comments.append(comment)
33929921a8fSScott Kruger    if not line.strip(): continue
34044776d8cSScott Kruger    lsplit=line.split(':')
34144776d8cSScott Kruger    if len(lsplit)==0: raise Exception("Missing : in line: "+line)
34244776d8cSScott Kruger    indentcount=lsplit[0].count(" ")
34344776d8cSScott Kruger    var=lsplit[0].strip()
34440ae0433SScott Kruger    val=line[line.find(':')+1:].strip()
34544776d8cSScott Kruger    if not var in acceptedkeys: raise Exception("Not a defined key: "+var+" from:  "+line)
34629921a8fSScott Kruger    # Start by seeing if we are in a subtest
34729921a8fSScott Kruger    if line.startswith(" "):
348c0658d2aSScott Kruger      subdict[subtestname][var]=val
34968a9e459SScott Kruger      if not indentlevel: indentlevel=indentcount
350cadd188bSScott Kruger      #if indentlevel!=indentcount: print("Error in indentation:", ln)
35129921a8fSScott Kruger    # Determine subtest name and make dict
35229921a8fSScott Kruger    elif var=="test":
35329921a8fSScott Kruger      subtestname="test"+str(subtestnum)
35429921a8fSScott Kruger      subdict[subtestname]={}
35529921a8fSScott Kruger      if not subdict.has_key("subtests"): subdict["subtests"]=[]
35629921a8fSScott Kruger      subdict["subtests"].append(subtestname)
35729921a8fSScott Kruger      subtestnum=subtestnum+1
35868a9e459SScott Kruger    # The rest are easy
35929921a8fSScott Kruger    else:
36044776d8cSScott Kruger      # For convenience, it is sometimes convenient to list twice
36144776d8cSScott Kruger      if subdict.has_key(var):
36244776d8cSScott Kruger        if var in appendlist:
36344776d8cSScott Kruger          subdict[var]+=" "+val
36444776d8cSScott Kruger        else:
36544776d8cSScott Kruger          raise Exception(var+" entered twice: "+line)
36644776d8cSScott Kruger      else:
367c0658d2aSScott Kruger        subdict[var]=val
36829921a8fSScott Kruger      if var=="suffix":
36929921a8fSScott Kruger        if len(val)>0:
37029921a8fSScott Kruger          testname=testname+"_"+val
37129921a8fSScott Kruger
37268a9e459SScott Kruger  if len(comments): subdict['comments']="\n".join(comments).lstrip("\n")
37344776d8cSScott Kruger  # A test block can create multiple tests.  This does
37444776d8cSScott Kruger  # that logic
37544776d8cSScott Kruger  testnames,subdicts=splitTests(testname,subdict)
37644776d8cSScott Kruger  return testnames,subdicts
37729921a8fSScott Kruger
3786cecdbdcSScott Krugerdef parseTests(testStr,srcfile,fileNums,verbosity):
37929921a8fSScott Kruger  """
38029921a8fSScott Kruger  Parse the yaml string describing tests and return
38129921a8fSScott Kruger  a dictionary with the info in the form of:
38229921a8fSScott Kruger    testDict[test][subtest]
38329921a8fSScott Kruger  This is an inelegant parser as we do not wish to
38429921a8fSScott Kruger  introduce a new dependency by bringing in pyyaml.
38529921a8fSScott Kruger  The advantage is that validation can be done as
38629921a8fSScott Kruger  it is parsed (e.g., 'test' is the only top-level node)
38729921a8fSScott Kruger  """
38829921a8fSScott Kruger
38929921a8fSScott Kruger  testDict={}
39029921a8fSScott Kruger
39129921a8fSScott Kruger  # The first entry should be test: but it might be indented.
39244776d8cSScott Kruger  newTestStr=_stripIndent(testStr,srcfile,entireBlock=True,fileNums=fileNums)
393cadd188bSScott Kruger  if verbosity>2: print(srcfile)
39429921a8fSScott Kruger
395e4653983SScott Kruger  ## Check and see if we have build requirements
396e4653983SScott Kruger  addToRunRequirements=None
397aec507c4SScott Kruger  if "\nbuild:" in newTestStr:
398aec507c4SScott Kruger    testDict['build']={}
399aec507c4SScott Kruger    # The file info is already here and need to append
400aec507c4SScott Kruger    Part1=newTestStr.split("build:")[1]
401aec507c4SScott Kruger    fileInfo=re.split("\ntest(?:set)?:",newTestStr)[0]
402aec507c4SScott Kruger    for bkey in buildkeys:
403aec507c4SScott Kruger      if bkey+":" in fileInfo:
404aec507c4SScott Kruger        testDict['build'][bkey]=fileInfo.split(bkey+":")[1].split("\n")[0].strip()
405aec507c4SScott Kruger        #if verbosity>1: bkey+": "+testDict['build'][bkey]
406e4653983SScott Kruger      # If a runtime requires are put into build, push them down to all run tests
407e4653983SScott Kruger      # At this point, we are working with strings and not lists
408e4653983SScott Kruger      if 'requires' in testDict['build']:
409e4653983SScott Kruger         if 'datafilespath' in testDict['build']['requires']:
410e4653983SScott Kruger             newreqs=re.sub('datafilespath','',testDict['build']['requires'])
411e4653983SScott Kruger             testDict['build']['requires']=newreqs.strip()
412e4653983SScott Kruger             addToRunRequirements='datafilespath'
413e4653983SScott Kruger
414aec507c4SScott Kruger
41529921a8fSScott Kruger  # Now go through each test.  First elem in split is blank
416e53dc769SScott Kruger  for test in re.split("\ntest(?:set)?:",newTestStr)[1:]:
4176cecdbdcSScott Kruger    testnames,subdicts=parseTest(test,srcfile,verbosity)
41844776d8cSScott Kruger    for i in range(len(testnames)):
41944776d8cSScott Kruger      if testDict.has_key(testnames[i]):
4201acf9037SMatthew G. Knepley        raise RuntimeError("Multiple test names specified: "+testnames[i]+" in file: "+srcfile)
421e4653983SScott Kruger      # Add in build requirements that need to be moved
422e4653983SScott Kruger      if addToRunRequirements:
423e4653983SScott Kruger          if 'requires' in subdicts[i]:
424e4653983SScott Kruger              subdicts[i]['requires']+=addToRunRequirements
425e4653983SScott Kruger          else:
426e4653983SScott Kruger              subdicts[i]['requires']=addToRunRequirements
42744776d8cSScott Kruger      testDict[testnames[i]]=subdicts[i]
42829921a8fSScott Kruger
42929921a8fSScott Kruger  return testDict
43029921a8fSScott Kruger
4316cecdbdcSScott Krugerdef parseTestFile(srcfile,verbosity):
43229921a8fSScott Kruger  """
43329921a8fSScott Kruger  Parse single example files and return dictionary of the form:
43429921a8fSScott Kruger    testDict[srcfile][test][subtest]
43529921a8fSScott Kruger  """
43629921a8fSScott Kruger  debug=False
437cadd188bSScott Kruger  basename=os.path.basename(srcfile)
438cadd188bSScott Kruger  if basename=='makefile': return {}
439cadd188bSScott Kruger
44029921a8fSScott Kruger  curdir=os.path.realpath(os.path.curdir)
44129921a8fSScott Kruger  basedir=os.path.dirname(os.path.realpath(srcfile))
44229921a8fSScott Kruger  os.chdir(basedir)
44329921a8fSScott Kruger
44429921a8fSScott Kruger  testDict={}
445cadd188bSScott Kruger  sh=open(basename,"r"); fileStr=sh.read(); sh.close()
44629921a8fSScott Kruger
44729921a8fSScott Kruger  ## Start with doing the tests
44829921a8fSScott Kruger  #
44929921a8fSScott Kruger  fsplit=fileStr.split("/*TEST\n")[1:]
45044776d8cSScott Kruger  fstart=len(fileStr.split("/*TEST\n")[0].split("\n"))+1
45129921a8fSScott Kruger  # Allow for multiple "/*TEST" blocks even though it really should be
4526f029658SMatthew G. Knepley  # one
45329921a8fSScott Kruger  srcTests=[]
45429921a8fSScott Kruger  for t in fsplit: srcTests.append(t.split("TEST*/")[0])
45529921a8fSScott Kruger  testString=" ".join(srcTests)
45644776d8cSScott Kruger  flen=len(testString.split("\n"))
45744776d8cSScott Kruger  fend=fstart+flen-1
45844776d8cSScott Kruger  fileNums=range(fstart,fend)
4596cecdbdcSScott Kruger  testDict[basename]=parseTests(testString,srcfile,fileNums,verbosity)
460aec507c4SScott Kruger  # Massage dictionary for build requirements
461aec507c4SScott Kruger  if 'build' in testDict[basename]:
462aec507c4SScott Kruger    testDict[basename].update(testDict[basename]['build'])
463aec507c4SScott Kruger    del testDict[basename]['build']
46429921a8fSScott Kruger
46529921a8fSScott Kruger
46629921a8fSScott Kruger  os.chdir(curdir)
46729921a8fSScott Kruger  return testDict
46829921a8fSScott Kruger
4696cecdbdcSScott Krugerdef parseTestDir(directory,verbosity):
47029921a8fSScott Kruger  """
47129921a8fSScott Kruger  Parse single example files and return dictionary of the form:
47229921a8fSScott Kruger    testDict[srcfile][test][subtest]
47329921a8fSScott Kruger  """
47429921a8fSScott Kruger  curdir=os.path.realpath(os.path.curdir)
47529921a8fSScott Kruger  basedir=os.path.realpath(directory)
47629921a8fSScott Kruger  os.chdir(basedir)
47729921a8fSScott Kruger
47829921a8fSScott Kruger  tDict={}
47929921a8fSScott Kruger  for test_file in glob.glob("new_ex*.*"):
4806cecdbdcSScott Kruger    tDict.update(parseTestFile(test_file,verbosity))
48129921a8fSScott Kruger
48229921a8fSScott Kruger  os.chdir(curdir)
48329921a8fSScott Kruger  return tDict
48429921a8fSScott Kruger
48529921a8fSScott Krugerdef printExParseDict(rDict):
48629921a8fSScott Kruger  """
48729921a8fSScott Kruger  This is useful for debugging
48829921a8fSScott Kruger  """
48929921a8fSScott Kruger  indent="   "
49029921a8fSScott Kruger  for sfile in rDict:
491cadd188bSScott Kruger    print(sfile)
49244776d8cSScott Kruger    sortkeys=rDict[sfile].keys()
49344776d8cSScott Kruger    sortkeys.sort()
49444776d8cSScott Kruger    for runex in sortkeys:
495cadd188bSScott Kruger      print(indent+runex)
49629921a8fSScott Kruger      if type(rDict[sfile][runex])==types.StringType:
497cadd188bSScott Kruger        print(indent*2+rDict[sfile][runex])
49829921a8fSScott Kruger      else:
49929921a8fSScott Kruger        for var in rDict[sfile][runex]:
50044776d8cSScott Kruger          if var.startswith("test"): continue
501cadd188bSScott Kruger          print(indent*2+var+": "+str(rDict[sfile][runex][var]))
50244776d8cSScott Kruger        if rDict[sfile][runex].has_key('subtests'):
50344776d8cSScott Kruger          for var in rDict[sfile][runex]['subtests']:
504cadd188bSScott Kruger            print(indent*2+var)
50529921a8fSScott Kruger            for var2 in rDict[sfile][runex][var]:
506cadd188bSScott Kruger              print(indent*3+var2+": "+str(rDict[sfile][runex][var][var2]))
507cadd188bSScott Kruger      print("\n")
50829921a8fSScott Kruger  return
50929921a8fSScott Kruger
51029921a8fSScott Krugerdef main(directory='',test_file='',verbosity=0):
51129921a8fSScott Kruger
51229921a8fSScott Kruger    if directory:
5136cecdbdcSScott Kruger      tDict=parseTestDir(directory,verbosity)
51429921a8fSScott Kruger    else:
5156cecdbdcSScott Kruger      tDict=parseTestFile(test_file,verbosity)
51629921a8fSScott Kruger    if verbosity>0: printExParseDict(tDict)
51729921a8fSScott Kruger
51829921a8fSScott Kruger    return
51929921a8fSScott Kruger
52029921a8fSScott Krugerif __name__ == '__main__':
52129921a8fSScott Kruger    import optparse
52229921a8fSScott Kruger    parser = optparse.OptionParser()
52329921a8fSScott Kruger    parser.add_option('-d', '--directory', dest='directory',
52429921a8fSScott Kruger                      default="", help='Directory containing files to parse')
52529921a8fSScott Kruger    parser.add_option('-t', '--test_file', dest='test_file',
52629921a8fSScott Kruger                      default="", help='Test file, e.g., ex1.c, to parse')
52729921a8fSScott Kruger    parser.add_option('-v', '--verbosity', dest='verbosity',
52829921a8fSScott Kruger                      help='Verbosity of output by level: 1, 2, or 3', default=0)
52929921a8fSScott Kruger    opts, extra_args = parser.parse_args()
53029921a8fSScott Kruger
53129921a8fSScott Kruger    if extra_args:
53229921a8fSScott Kruger        import sys
53329921a8fSScott Kruger        sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args))
53429921a8fSScott Kruger        exit(1)
53529921a8fSScott Kruger    if not opts.test_file and not opts.directory:
536cadd188bSScott Kruger      print("test file or directory is required")
53729921a8fSScott Kruger      parser.print_usage()
53829921a8fSScott Kruger      sys.exit()
53929921a8fSScott Kruger
54029921a8fSScott Kruger    # Need verbosity to be an integer
54129921a8fSScott Kruger    try:
54229921a8fSScott Kruger      verbosity=int(opts.verbosity)
54329921a8fSScott Kruger    except:
54429921a8fSScott Kruger      raise Exception("Error: Verbosity must be integer")
54529921a8fSScott Kruger
54629921a8fSScott Kruger    main(directory=opts.directory,test_file=opts.test_file,verbosity=verbosity)
547