Gaudi Framework, version v23r4

Home   Generated: Mon Sep 17 2012
Classes | Functions | Variables

GaudiTest Namespace Reference

Classes

class  TemporaryEnvironment
 Utility Classes. More...
class  TempDir
class  TempFile
class  CMT
class  BasicOutputValidator
 Output Validation Classes. More...
class  FilePreprocessor
class  FilePreprocessorSequence
class  LineSkipper
class  BlockSkipper
class  RegexpReplacer
class  LineSorter
 Special preprocessor sorting the list of strings (whitespace separated) that follow a signature on a single line. More...
class  ReferenceFileValidator
class  GaudiFilterExecutable
class  GaudiExeTest
class  HTMLResultStream

Functions

def which
 Locates an executable in the executables path ($PATH) and returns the full path to it.
def rationalizepath
def findReferenceBlock
def countErrorLines
def _parseTTreeSummary
def findTTreeSummaries
def cmpTreesDicts
def getCmpFailingValues
def parseHistosSummary
def findHistosSummaries

Variables

string __author__ = 'Marco Clemencic CERN/PH-LBC'
 File: GaudiTest.py Author: Marco Clemencic CERN/PH-LBC.
tuple maskPointers = RegexpReplacer("0x[0-9a-fA-F]{4,16}","0x########")
tuple normalizeDate
tuple normalizeEOL = FilePreprocessor()
tuple skipEmptyLines = FilePreprocessor()
 normalizeExamples = maskPointers+normalizeDate
tuple h_count_re = re.compile(r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)")

Function Documentation

def GaudiTest::_parseTTreeSummary (   lines,
  pos 
) [private]
Parse the TTree summary table in lines, starting from pos.
Returns a tuple with the dictionary with the digested informations and the
position of the first line after the summary.

Definition at line 631 of file GaudiTest.py.

00632                                   :
00633     """
00634     Parse the TTree summary table in lines, starting from pos.
00635     Returns a tuple with the dictionary with the digested informations and the
00636     position of the first line after the summary.
00637     """
00638     result = {}
00639     i = pos + 1 # first line is a sequence of '*'
00640     count = len(lines)
00641 
00642     splitcols = lambda l: [ f.strip() for f in l.strip("*\n").split(':',2) ]
00643     def parseblock(ll):
00644         r = {}
00645         cols = splitcols(ll[0])
00646         r["Name"], r["Title"] = cols[1:]
00647 
00648         cols = splitcols(ll[1])
00649         r["Entries"] = int(cols[1])
00650 
00651         sizes = cols[2].split()
00652         r["Total size"] = int(sizes[2])
00653         if sizes[-1] == "memory":
00654             r["File size"] = 0
00655         else:
00656             r["File size"] = int(sizes[-1])
00657 
00658         cols = splitcols(ll[2])
00659         sizes = cols[2].split()
00660         if cols[0] == "Baskets":
00661             r["Baskets"] = int(cols[1])
00662             r["Basket size"] = int(sizes[2])
00663         r["Compression"] = float(sizes[-1])
00664         return r
00665 
00666     if i < (count - 3) and lines[i].startswith("*Tree"):
00667         result = parseblock(lines[i:i+3])
00668         result["Branches"] = {}
00669         i += 4
00670         while i < (count - 3) and lines[i].startswith("*Br"):
00671             if i < (count - 2) and lines[i].startswith("*Branch "):
00672                 # skip branch header
00673                 i += 3
00674                 continue
00675             branch = parseblock(lines[i:i+3])
00676             result["Branches"][branch["Name"]] = branch
00677             i += 4
00678 
00679     return (result, i)

def GaudiTest::cmpTreesDicts (   reference,
  to_check,
  ignore = None 
)
Check that all the keys in reference are in to_check too, with the same value.
If the value is a dict, the function is called recursively. to_check can
contain more keys than reference, that will not be tested.
The function returns at the first difference found.

Definition at line 701 of file GaudiTest.py.

00702                                                      :
00703     """
00704     Check that all the keys in reference are in to_check too, with the same value.
00705     If the value is a dict, the function is called recursively. to_check can
00706     contain more keys than reference, that will not be tested.
00707     The function returns at the first difference found.
00708     """
00709     fail_keys = []
00710     # filter the keys in the reference dictionary
00711     if ignore:
00712         ignore_re = re.compile(ignore)
00713         keys = [ key for key in reference if not ignore_re.match(key) ]
00714     else:
00715         keys = reference.keys()
00716     # loop over the keys (not ignored) in the reference dictionary
00717     for k in keys:
00718         if k in to_check: # the key must be in the dictionary to_check
00719             if (type(reference[k]) is dict) and (type(to_check[k]) is dict):
00720                 # if both reference and to_check values are dictionaries, recurse
00721                 failed = fail_keys = cmpTreesDicts(reference[k], to_check[k], ignore)
00722             else:
00723                 # compare the two values
00724                 failed = to_check[k] != reference[k]
00725         else: # handle missing keys in the dictionary to check (i.e. failure)
00726             to_check[k] = None
00727             failed = True
00728         if failed:
00729             fail_keys.insert(0, k)
00730             break # exit from the loop at the first failure
00731     return fail_keys # return the list of keys bringing to the different values

def GaudiTest::countErrorLines (   expected = {'ERROR':0,
  FATAL 
)

Definition at line 595 of file GaudiTest.py.

00595                                {'ERROR':0, 'FATAL':0}, **kwargs):
00596     """
00597     Count the number of messages with required severity (by default ERROR and FATAL)
00598     and check if their numbers match the expected ones (0 by default).
00599     The dictionary "expected" can be used to tune the number of errors and fatals
00600     allowed, or to limit the number of expected warnings etc.
00601     """
00602     stdout = kwargs["stdout"]
00603     result = kwargs["result"]
00604     causes = kwargs["causes"]
00605 
00606     # prepare the dictionary to record the extracted lines
00607     errors = {}
00608     for sev in expected:
00609         errors[sev] = []
00610 
00611     outlines = stdout.splitlines()
00612     from math import log10
00613     fmt = "%%%dd - %%s" % (int(log10(len(outlines))+1))
00614 
00615     linecount = 0
00616     for l in outlines:
00617         linecount += 1
00618         words = l.split()
00619         if len(words) >= 2 and words[1] in errors:
00620             errors[words[1]].append(fmt%(linecount,l.rstrip()))
00621 
00622     for e in errors:
00623         if len(errors[e]) != expected[e]:
00624             causes.append('%s(%d)'%(e,len(errors[e])))
00625             result["GaudiTest.lines.%s"%e] = result.Quote('\n'.join(errors[e]))
00626             result["GaudiTest.lines.%s.expected#"%e] = result.Quote(str(expected[e]))
00627 
00628     return causes
00629 
00630 
def GaudiTest::findHistosSummaries (   stdout )
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 808 of file GaudiTest.py.

00809                                :
00810     """
00811     Scan stdout to find ROOT TTree summaries and digest them.
00812     """
00813     outlines = stdout.splitlines()
00814     nlines = len(outlines) - 1
00815     summaries = {}
00816     global h_count_re
00817 
00818     pos = 0
00819     while pos < nlines:
00820         summ = {}
00821         # find first line of block:
00822         match = h_count_re.search(outlines[pos])
00823         while pos < nlines and not match:
00824             pos += 1
00825             match = h_count_re.search(outlines[pos])
00826         if match:
00827             summ, pos = parseHistosSummary(outlines, pos)
00828         summaries.update(summ)
00829     return summaries

def GaudiTest::findReferenceBlock (   reference,
  stdout,
  result,
  causes,
  signature_offset = 0,
  signature = None,
  id = None 
)
Given a block of text, tries to find it in the output.
The block had to be identified by a signature line. By default, the first
line is used as signature, or the line pointed to by signature_offset. If
signature_offset points outside the block, a signature line can be passed as
signature argument. Note: if 'signature' is None (the default), a negative
signature_offset is interpreted as index in a list (e.g. -1 means the last
line), otherwise the it is interpreted as the number of lines before the
first one of the block the signature must appear.
The parameter 'id' allow to distinguish between different calls to this
function in the same validation code.

Definition at line 548 of file GaudiTest.py.

00550                                  :
00551     """
00552     Given a block of text, tries to find it in the output.
00553     The block had to be identified by a signature line. By default, the first
00554     line is used as signature, or the line pointed to by signature_offset. If
00555     signature_offset points outside the block, a signature line can be passed as
00556     signature argument. Note: if 'signature' is None (the default), a negative
00557     signature_offset is interpreted as index in a list (e.g. -1 means the last
00558     line), otherwise the it is interpreted as the number of lines before the
00559     first one of the block the signature must appear.
00560     The parameter 'id' allow to distinguish between different calls to this
00561     function in the same validation code.
00562     """
00563     # split reference file, sanitize EOLs and remove empty lines
00564     reflines = filter(None,map(lambda s: s.rstrip(), reference.splitlines()))
00565     if not reflines:
00566         raise RuntimeError("Empty (or null) reference")
00567     # the same on standard output
00568     outlines = filter(None,map(lambda s: s.rstrip(), stdout.splitlines()))
00569 
00570     res_field = "GaudiTest.RefBlock"
00571     if id:
00572         res_field += "_%s" % id
00573 
00574     if signature is None:
00575         if signature_offset < 0:
00576             signature_offset = len(reference)+signature_offset
00577         signature = reflines[signature_offset]
00578     # find the reference block in the output file
00579     try:
00580         pos = outlines.index(signature)
00581         outlines = outlines[pos-signature_offset:pos+len(reflines)-signature_offset]
00582         if reflines != outlines:
00583             msg = "standard output"
00584             # I do not want 2 messages in causes if teh function is called twice
00585             if not msg in causes:
00586                 causes.append(msg)
00587             result[res_field + ".observed"] = result.Quote("\n".join(outlines))
00588     except ValueError:
00589         causes.append("missing signature")
00590     result[res_field + ".signature"] = result.Quote(signature)
00591     if len(reflines) > 1 or signature != reflines[0]:
00592         result[res_field + ".expected"] = result.Quote("\n".join(reflines))
00593 
00594     return causes

def GaudiTest::findTTreeSummaries (   stdout )
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 680 of file GaudiTest.py.

00681                               :
00682     """
00683     Scan stdout to find ROOT TTree summaries and digest them.
00684     """
00685     stars = re.compile(r"^\*+$")
00686     outlines = stdout.splitlines()
00687     nlines = len(outlines)
00688     trees = {}
00689 
00690     i = 0
00691     while i < nlines: #loop over the output
00692         # look for
00693         while i < nlines and not stars.match(outlines[i]):
00694             i += 1
00695         if i < nlines:
00696             tree, i = _parseTTreeSummary(outlines, i)
00697             if tree:
00698                 trees[tree["Name"]] = tree
00699 
00700     return trees

def GaudiTest::getCmpFailingValues (   reference,
  to_check,
  fail_path 
)

Definition at line 732 of file GaudiTest.py.

00733                                                        :
00734     c = to_check
00735     r = reference
00736     for k in fail_path:
00737         c = c.get(k,None)
00738         r = r.get(k,None)
00739         if c is None or r is None:
00740             break # one of the dictionaries is not deep enough
00741     return (fail_path, r, c)
00742 
# signature of the print-out of the histograms
def GaudiTest::parseHistosSummary (   lines,
  pos 
)
Extract the histograms infos from the lines starting at pos.
Returns the position of the first line after the summary block.

Definition at line 745 of file GaudiTest.py.

00746                                   :
00747     """
00748     Extract the histograms infos from the lines starting at pos.
00749     Returns the position of the first line after the summary block.
00750     """
00751     global h_count_re
00752     h_table_head = re.compile(r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"')
00753     h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
00754 
00755     nlines = len(lines)
00756 
00757     # decode header
00758     m = h_count_re.search(lines[pos])
00759     name = m.group(1).strip()
00760     total = int(m.group(2))
00761     header = {}
00762     for k, v in [ x.split("=") for x in  m.group(3).split() ]:
00763         header[k] = int(v)
00764     pos += 1
00765     header["Total"] = total
00766 
00767     summ = {}
00768     while pos < nlines:
00769         m = h_table_head.search(lines[pos])
00770         if m:
00771             t, d = m.groups(1) # type and directory
00772             t = t.replace(" profile", "Prof")
00773             pos += 1
00774             if pos < nlines:
00775                 l = lines[pos]
00776             else:
00777                 l = ""
00778             cont = {}
00779             if l.startswith(" | ID"):
00780                 # table format
00781                 titles = [ x.strip() for x in l.split("|")][1:]
00782                 pos += 1
00783                 while pos < nlines and lines[pos].startswith(" |"):
00784                     l = lines[pos]
00785                     values = [ x.strip() for x in l.split("|")][1:]
00786                     hcont = {}
00787                     for i in range(len(titles)):
00788                         hcont[titles[i]] = values[i]
00789                     cont[hcont["ID"]] = hcont
00790                     pos += 1
00791             elif l.startswith(" ID="):
00792                 while pos < nlines and lines[pos].startswith(" ID="):
00793                     values = [ x.strip() for x in  h_short_summ.search(lines[pos]).groups() ]
00794                     cont[values[0]] = values
00795                     pos += 1
00796             else: # not interpreted
00797                 raise RuntimeError("Cannot understand line %d: '%s'" % (pos, l))
00798             if not d in summ:
00799                 summ[d] = {}
00800             summ[d][t] = cont
00801             summ[d]["header"] = header
00802         else:
00803             break
00804     if not summ:
00805         # If the full table is not present, we use only the header
00806         summ[name] = {"header": header}
00807     return summ, pos

def GaudiTest::rationalizepath (   p )

Definition at line 283 of file GaudiTest.py.

00284                       :
00285     p = os.path.normpath(os.path.expandvars(p))
00286     if os.path.exists(p):
00287         p = os.path.realpath(p)
00288     return p

def GaudiTest::which (   executable )

Locates an executable in the executables path ($PATH) and returns the full path to it.

If the executable cannot be found, None is returned

Locates an executable in the executables path ($PATH) and returns the full
path to it.  An application is looked for with or without the '.exe' suffix.
If the executable cannot be found, None is returned

Definition at line 263 of file GaudiTest.py.

00264                      :
00265     """
00266     Locates an executable in the executables path ($PATH) and returns the full
00267     path to it.  An application is looked for with or without the '.exe' suffix.
00268     If the executable cannot be found, None is returned
00269     """
00270     if os.path.isabs(executable):
00271         if not os.path.exists(executable):
00272             if executable.endswith('.exe'):
00273                 if os.path.exists(executable[:-4]):
00274                     return executable[:-4]
00275         return executable
00276     for d in os.environ.get("PATH").split(os.pathsep):
00277         fullpath = os.path.join(d, executable)
00278         if os.path.exists(fullpath):
00279             return fullpath
00280     if executable.endswith('.exe'):
00281         return which(executable[:-4])
00282     return None


Variable Documentation

string GaudiTest::__author__ = 'Marco Clemencic CERN/PH-LBC'

File: GaudiTest.py Author: Marco Clemencic CERN/PH-LBC.

Definition at line 5 of file GaudiTest.py.

tuple GaudiTest::h_count_re = re.compile(r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)")

Definition at line 743 of file GaudiTest.py.

tuple GaudiTest::maskPointers = RegexpReplacer("0x[0-9a-fA-F]{4,16}","0x########")

Definition at line 418 of file GaudiTest.py.

Initial value:
00001 RegexpReplacer("[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9] [0-9]{4}[-/][01][0-9][-/][0-3][0-9] *(CES?T)?",
00002                                "00:00:00 1970-01-01")

Definition at line 419 of file GaudiTest.py.

Definition at line 421 of file GaudiTest.py.

Definition at line 444 of file GaudiTest.py.

Definition at line 424 of file GaudiTest.py.

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated at Mon Sep 17 2012 13:49:58 for Gaudi Framework, version v23r4 by Doxygen version 1.7.2 written by Dimitri van Heesch, © 1997-2004