| # Gaudi Framework, version v21r7p1 |

Home |  | Generated: 15 Feb 2010

# GaudiTest Namespace Reference

## Classes
class | TemporaryEnvironment
class | TempDir
class | TempFile
class | CMT
class | BasicOutputValidator
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
## 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'
string | __version__ = "$Revision: 1.52 $"
string | __tag__ = "$Name: $"
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 613 of file GaudiTest.py.

    00613                                   :
    00614     """
    00615     Parse the TTree summary table in lines, starting from pos.
    00616     Returns a tuple with the dictionary with the digested informations and the
    00617     position of the first line after the summary.
    00618     """
    00619     result = {}
    00620     i = pos + 1 # first line is a sequence of '*'
    00621     count = len(lines)
    00622
    00623     splitcols = lambda l: [ f.strip() for f in l.strip("*\n").split(':',2) ]
    00624     def parseblock(ll):
    00625         r = {}
    00626         cols = splitcols(ll[0])
    00627         r["Name"], r["Title"] = cols[1:]
    00628
    00629         cols = splitcols(ll[1])
    00630         r["Entries"] = int(cols[1])
    00631
    00632         sizes = cols[2].split()
    00633         r["Total size"] = int(sizes[2])
    00634         if sizes[-1] == "memory":
    00635             r["File size"] = 0
    00636         else:
    00637             r["File size"] = int(sizes[-1])
    00638
    00639         cols = splitcols(ll[2])
    00640         sizes = cols[2].split()
    00641         if cols[0] == "Baskets":
    00642             r["Baskets"] = int(cols[1])
    00643             r["Basket size"] = int(sizes[2])
    00644         r["Compression"] = float(sizes[-1])
    00645         return r
    00646
    00647     if i < (count - 3) and lines[i].startswith("*Tree"):
    00648         result = parseblock(lines[i:i+3])
    00649         result["Branches"] = {}
    00650         i += 4
    00651         while i < (count - 3) and lines[i].startswith("*Br"):
    00652             branch = parseblock(lines[i:i+3])
    00653             result["Branches"][branch["Name"]] = branch
    00654             i += 4
    00655
    00656     return (result, i)
    00657
    def findTTreeSummaries(stdout):

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 679 of file GaudiTest.py.

    00679                                                      :
    00680     """
    00681     Check that all the keys in reference are in to_check too, with the same value.
    00682     If the value is a dict, the function is called recursively. to_check can
    00683     contain more keys than reference, that will not be tested.
    00684     The function returns at the first difference found.
    00685     """
    00686     fail_keys = []
    00687     # filter the keys in the reference dictionary
    00688     if ignore:
    00689         ignore_re = re.compile(ignore)
    00690         keys = [ key for key in reference if not ignore_re.match(key) ]
    00691     else:
    00692         keys = reference.keys()
    00693     # loop over the keys (not ignored) in the reference dictionary
    00694     for k in keys:
    00695         if k in to_check: # the key must be in the dictionary to_check
    00696             if (type(reference[k]) is dict) and (type(to_check[k]) is dict):
    00697                 # if both reference and to_check values are dictionaries, recurse
    00698                 failed = fail_keys = cmpTreesDicts(reference[k], to_check[k], ignore)
    00699             else:
    00700                 # compare the two values
    00701                 failed = to_check[k] != reference[k]
    00702         else: # handle missing keys in the dictionary to check (i.e. failure)
    00703             to_check[k] = None
    00704             failed = True
    00705         if failed:
    00706             fail_keys.insert(0, k)
    00707             break # exit from the loop at the first failure
    00708     return fail_keys # return the list of keys bringing to the different values
    00709
    def getCmpFailingValues(reference, to_check, fail_path):

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

Definition at line 577 of file GaudiTest.py.

    00577                                {'ERROR':0, 'FATAL':0}, **kwargs):
    00578     """
    00579     Count the number of messages with required severity (by default ERROR and FATAL)
    00580     and check if their numbers match the expected ones (0 by default).
    00581     The dictionary "expected" can be used to tune the number of errors and fatals
    00582     allowed, or to limit the number of expected warnings etc.
    00583     """
    00584     stdout = kwargs["stdout"]
    00585     result = kwargs["result"]
    00586     causes = kwargs["causes"]
    00587
    00588     # prepare the dictionary to record the extracted lines
    00589     errors = {}
    00590     for sev in expected:
    00591         errors[sev] = []
    00592
    00593     outlines = stdout.splitlines()
    00594     from math import log10
    00595     fmt = "%%%dd - %%s" % (int(log10(len(outlines))+1))
    00596
    00597     linecount = 0
    00598     for l in outlines:
    00599         linecount += 1
    00600         words = l.split()
    00601         if len(words) >= 2 and words[1] in errors:
    00602             errors[words[1]].append(fmt%(linecount,l.rstrip()))
    00603
    00604     for e in errors:
    00605         if len(errors[e]) != expected[e]:
    00606             causes.append('%s(%d)'%(e,len(errors[e])))
    00607             result["GaudiTest.lines.%s"%e] = result.Quote('\n'.join(errors[e]))
    00608             result["GaudiTest.lines.%s.expected#"%e] = result.Quote(str(expected[e]))
    00609
    00610     return causes
    00611
    00612

def GaudiTest::findHistosSummaries | ( |  | stdout | ) |

    Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 786 of file GaudiTest.py.

    00786                                :
    00787     """
    00788     Scan stdout to find ROOT TTree summaries and digest them.
    00789     """
    00790     outlines = stdout.splitlines()
    00791     nlines = len(outlines) - 1
    00792     summaries = {}
    00793     global h_count_re
    00794
    00795     pos = 0
    00796     while pos < nlines:
    00797         summ = {}
    00798         # find first line of block:
    00799         match = h_count_re.search(outlines[pos])
    00800         while pos < nlines and not match:
    00801             pos += 1
    00802             match = h_count_re.search(outlines[pos])
    00803         if match:
    00804             summ, pos = parseHistosSummary(outlines, pos)
    00805         summaries.update(summ)
    00806     return summaries
    00807
    class GaudiFilterExecutable(qm.executable.Filter):

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 530 of file GaudiTest.py.

    00531                                  :
    00532     """
    00533     Given a block of text, tries to find it in the output.
    00534     The block had to be identified by a signature line. By default, the first
    00535     line is used as signature, or the line pointed to by signature_offset. If
    00536     signature_offset points outside the block, a signature line can be passed as
    00537     signature argument. Note: if 'signature' is None (the default), a negative
    00538     signature_offset is interpreted as index in a list (e.g. -1 means the last
    00539     line), otherwise the it is interpreted as the number of lines before the
    00540     first one of the block the signature must appear.
    00541     The parameter 'id' allow to distinguish between different calls to this
    00542     function in the same validation code.
    00543     """
    00544     # split reference file, sanitize EOLs and remove empty lines
    00545     reflines = filter(None,map(lambda s: s.rstrip(), reference.splitlines()))
    00546     if not reflines:
    00547         raise RuntimeError("Empty (or null) reference")
    00548     # the same on standard output
    00549     outlines = filter(None,map(lambda s: s.rstrip(), stdout.splitlines()))
    00550
    00551     res_field = "GaudiTest.RefBlock"
    00552     if id:
    00553         res_field += "_%s" % id
    00554
    00555     if signature is None:
    00556         if signature_offset < 0:
    00557             signature_offset = len(reference)+signature_offset
    00558         signature = reflines[signature_offset]
    00559     # find the reference block in the output file
    00560     try:
    00561         pos = outlines.index(signature)
    00562         outlines = outlines[pos-signature_offset:pos+len(reflines)-signature_offset]
    00563         if reflines != outlines:
    00564             msg = "standard output"
    00565             # I do not want 2 messages in causes if teh function is called twice
    00566             if not msg in causes:
    00567                 causes.append(msg)
    00568             result[res_field + ".observed"] = result.Quote("\n".join(outlines))
    00569     except ValueError:
    00570         causes.append("missing signature")
    00571     result[res_field + ".signature"] = result.Quote(signature)
    00572     if len(reflines) > 1 or signature != reflines[0]:
    00573         result[res_field + ".expected"] = result.Quote("\n".join(reflines))
    00574
    00575     return causes
    00576
    def countErrorLines(expected = {'ERROR':0, 'FATAL':0}, **kwargs):

def GaudiTest::findTTreeSummaries | ( |  | stdout | ) |

    Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 658 of file GaudiTest.py.

    00658                               :
    00659     """
    00660     Scan stdout to find ROOT TTree summaries and digest them.
    00661     """
    00662     stars = re.compile(r"^\*+$")
    00663     outlines = stdout.splitlines()
    00664     nlines = len(outlines)
    00665     trees = {}
    00666
    00667     i = 0
    00668     while i < nlines: #loop over the output
    00669         # look for
    00670         while i < nlines and not stars.match(outlines[i]):
    00671             i += 1
    00672         if i < nlines:
    00673             tree, i = _parseTTreeSummary(outlines, i)
    00674             if tree:
    00675                 trees[tree["Name"]] = tree
    00676
    00677     return trees
    00678
    def cmpTreesDicts(reference, to_check, ignore = None):

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

Definition at line 710 of file GaudiTest.py.

    00710                                                        :
    00711     c = to_check
    00712     r = reference
    00713     for k in fail_path:
    00714         c = c.get(k,None)
    00715         r = r.get(k,None)
    00716         if c is None or r is None:
    00717             break # one of the dictionaries is not deep enough
    00718     return (fail_path, r, c)
    00719
    00720 # signature of the print-out of the histograms
    h_count_re = re.compile(r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)")

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 723 of file GaudiTest.py.

    00723                                   :
    00724     """
    00725     Extract the histograms infos from the lines starting at pos.
    00726     Returns the position of the first line after the summary block.
    00727     """
    00728     global h_count_re
    00729     h_table_head = re.compile(r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"')
    00730     h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
    00731
    00732     nlines = len(lines)
    00733
    00734     # decode header
    00735     m = h_count_re.search(lines[pos])
    00736     name = m.group(1).strip()
    00737     total = int(m.group(2))
    00738     header = {}
    00739     for k, v in [ x.split("=") for x in  m.group(3).split() ]:
    00740         header[k] = int(v)
    00741     pos += 1
    00742     header["Total"] = total
    00743
    00744     summ = {}
    00745     while pos < nlines:
    00746         m = h_table_head.search(lines[pos])
    00747         if m:
    00748             t, d = m.groups(1) # type and directory
    00749             t = t.replace(" profile", "Prof")
    00750             pos += 1
    00751             if pos < nlines:
    00752                 l = lines[pos]
    00753             else:
    00754                 l = ""
    00755             cont = {}
    00756             if l.startswith(" | ID"):
    00757                 # table format
    00758                 titles = [ x.strip() for x in l.split("|")][1:]
    00759                 pos += 1
    00760                 while pos < nlines and lines[pos].startswith(" |"):
    00761                     l = lines[pos]
    00762                     values = [ x.strip() for x in l.split("|")][1:]
    00763                     hcont = {}
    00764                     for i in range(len(titles)):
    00765                         hcont[titles[i]] = values[i]
    00766                     cont[hcont["ID"]] = hcont
    00767                     pos += 1
    00768             elif l.startswith(" ID="):
    00769                 while pos < nlines and lines[pos].startswith(" ID="):
    00770                     values = [ x.strip() for x in  h_short_summ.search(lines[pos]).groups() ]
    00771                     cont[values[0]] = values
    00772                     pos += 1
    00773             else: # not interpreted
    00774                 raise RuntimeError("Cannot understand line %d: '%s'" % (pos, l))
    00775             if not d in summ:
    00776                 summ[d] = {}
    00777             summ[d][t] = cont
    00778             summ[d]["header"] = header
    00779         else:
    00780             break
    00781     if not summ:
    00782         # If the full table is not present, we use only the header
    00783         summ[name] = {"header": header}
    00784     return summ, pos
    00785
    def findHistosSummaries(stdout):

def GaudiTest::rationalizepath | ( |  | p | ) |

Definition at line 271 of file GaudiTest.py.

    00271                       :
    00272     p = os.path.normpath(os.path.expandvars(p))
    00273     if os.path.exists(p):
    00274         p = os.path.realpath(p)
    00275     return p
    00276
    ########################################################################

def GaudiTest::which | ( |  | executable | ) |

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

If the excutable cannot be found, None is returned

Definition at line 264 of file GaudiTest.py.

    00264                      :
    00265     for d in os.environ.get("PATH").split(os.pathsep):
    00266         fullpath = os.path.join(d,executable)
    00267         if os.path.exists(fullpath):
    00268             return fullpath
    00269     return None
    00270
    def rationalizepath(p):

## Variable Documentation

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

Definition at line 5 of file GaudiTest.py.

string GaudiTest::__tag__ = "$Name: $"

Definition at line 7 of file GaudiTest.py.

string GaudiTest::__version__ = "$Revision: 1.52 $"

Definition at line 6 of file GaudiTest.py.

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

Definition at line 721 of file GaudiTest.py.

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

Definition at line 406 of file GaudiTest.py.

tuple GaudiTest::normalizeDate

Initial value:

    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)?",
                                   "00:00:00 1970-01-01")

Definition at line 407 of file GaudiTest.py.

tuple GaudiTest::normalizeEOL = FilePreprocessor()

Definition at line 409 of file GaudiTest.py.

tuple GaudiTest::normalizeExamples = maskPointers+normalizeDate

Definition at line 432 of file GaudiTest.py.

tuple GaudiTest::skipEmptyLines = FilePreprocessor()

Definition at line 412 of file GaudiTest.py.