| # Gaudi Framework, version v23r0 |

Home |  | Generated: Mon Jan 30 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 620 of file GaudiTest.py.

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

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

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

Definition at line 584 of file GaudiTest.py.

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

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

    Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 797 of file GaudiTest.py.

    00798                                :
    00799     """
    00800     Scan stdout to find ROOT TTree summaries and digest them.
    00801     """
    00802     outlines = stdout.splitlines()
    00803     nlines = len(outlines) - 1
    00804     summaries = {}
    00805     global h_count_re
    00806
    00807     pos = 0
    00808     while pos < nlines:
    00809         summ = {}
    00810         # find first line of block:
    00811         match = h_count_re.search(outlines[pos])
    00812         while pos < nlines and not match:
    00813             pos += 1
    00814             match = h_count_re.search(outlines[pos])
    00815         if match:
    00816             summ, pos = parseHistosSummary(outlines, pos)
    00817         summaries.update(summ)
    00818     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 537 of file GaudiTest.py.

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

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

    Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 669 of file GaudiTest.py.

    00670                               :
    00671     """
    00672     Scan stdout to find ROOT TTree summaries and digest them.
    00673     """
    00674     stars = re.compile(r"^\*+$")
    00675     outlines = stdout.splitlines()
    00676     nlines = len(outlines)
    00677     trees = {}
    00678
    00679     i = 0
    00680     while i < nlines: #loop over the output
    00681         # look for
    00682         while i < nlines and not stars.match(outlines[i]):
    00683             i += 1
    00684         if i < nlines:
    00685             tree, i = _parseTTreeSummary(outlines, i)
    00686             if tree:
    00687                 trees[tree["Name"]] = tree
    00688
    00689     return trees

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

Definition at line 721 of file GaudiTest.py.

    00722                                                        :
    00723     c = to_check
    00724     r = reference
    00725     for k in fail_path:
    00726         c = c.get(k,None)
    00727         r = r.get(k,None)
    00728         if c is None or r is None:
    00729             break # one of the dictionaries is not deep enough
    00730     return (fail_path, r, c)
    00731
    # 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 734 of file GaudiTest.py.

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

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

Definition at line 272 of file GaudiTest.py.

    00273                       :
    00274     p = os.path.normpath(os.path.expandvars(p))
    00275     if os.path.exists(p):
    00276         p = os.path.realpath(p)
    00277     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

Definition at line 263 of file GaudiTest.py.

    00264                      :
    00265     if os.path.isabs(executable):
    00266         return executable
    00267     for d in os.environ.get("PATH").split(os.pathsep):
    00268         fullpath = os.path.join(d,executable)
    00269         if os.path.exists(fullpath):
    00270             return fullpath
    00271     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 732 of file GaudiTest.py.

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

Definition at line 407 of file GaudiTest.py.

tuple GaudiTest::normalizeDate

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

tuple GaudiTest::normalizeEOL = FilePreprocessor()

Definition at line 410 of file GaudiTest.py.

tuple GaudiTest::normalizeExamples = maskPointers+normalizeDate

Definition at line 433 of file GaudiTest.py.

tuple GaudiTest::skipEmptyLines = FilePreprocessor()

Definition at line 413 of file GaudiTest.py.

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