| The Gaudi Framework v32r1 (f65d50dc)

Classes | Functions | Variables
GaudiTest Namespace Reference

## Classes
class | BasicOutputValidator
| Output Validation Classes. More...
class | BlockSkipper
class | CMT
class | FilePreprocessor
class | FilePreprocessorSequence
class | GaudiExeTest
class | GaudiFilterExecutable
class | HTMLResultStream
class | LineSkipper
class | LineSorter
class | ReferenceFileValidator
class | RegexpReplacer
class | TempDir
class | TempFile
class | TemporaryEnvironment
| Utility Classes. More...
class | XMLResultStream

## Functions
def | ROOT6WorkAroundEnabled (id=None)
def | total_seconds_replacement (timedelta)
def | which (executable)
def | rationalizepath (p)
def | hexreplace (match)
def | hexConvert (char)
def | convert_xml_illegal_chars (val)
def | escape_xml_illegal_chars (val, replacement='?')
def | findReferenceBlock (reference, stdout, result, causes, signature_offset=0, signature=None, id=None)
def | countErrorLines (expected={ 'ERROR':0, 'FATAL':0}, **kwargs)
def | _parseTTreeSummary (lines, pos)
def | findTTreeSummaries (stdout)
def | cmpTreesDicts (reference, to_check, ignore=None)
def | getCmpFailingValues (reference, to_check, fail_path)
def | parseHistosSummary (lines, pos)
def | findHistosSummaries (stdout)

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

## Function Documentation

## ◆ _parseTTreeSummary()

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

801 def _parseTTreeSummary(lines, pos):
802 """
803 Parse the TTree summary table in lines, starting from pos.
804 Returns a tuple with the dictionary with the digested informations and the
805 position of the first line after the summary.
806 """
807 result = {}
808 i = pos + 1 # first line is a sequence of '*'
809 count = len(lines)
810
811 def splitcols(l):
812 return [f.strip() for f in l.strip("*\n").split(':', 2)]
813
814 def parseblock(ll):
815 r = {}
816 cols = splitcols(ll[0])
817 r["Name"], r["Title"] = cols[1:]
818
819 cols = splitcols(ll[1])
820 r["Entries"] = int(cols[1])
821
822 sizes = cols[2].split()
823 r["Total size"] = int(sizes[2])
824 if sizes[-1] == "memory":
825 r["File size"] = 0
826 else:
827 r["File size"] = int(sizes[-1])
828
829 cols = splitcols(ll[2])
830 sizes = cols[2].split()
831 if cols[0] == "Baskets":
832 r["Baskets"] = int(cols[1])
833 r["Basket size"] = int(sizes[2])
834 r["Compression"] = float(sizes[-1])
835 return r
836
837 if i < (count - 3) and lines[i].startswith("*Tree"):
838 result = parseblock(lines[i:i + 3])
839 result["Branches"] = {}
840 i += 4
841 while i < (count - 3) and lines[i].startswith("*Br"):
842 if i < (count - 2) and lines[i].startswith("*Branch "):
843 # skip branch header
844 i += 3
845 continue
846 branch = parseblock(lines[i:i + 3])
847 result["Branches"][branch["Name"]] = branch
848 i += 4
849
850 return (result, i)
851
852
GaudiTest._parseTTreeSummary
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:801

## ◆ cmpTreesDicts()

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

875 def cmpTreesDicts(reference, to_check, ignore=None):
876 """
877 Check that all the keys in reference are in to_check too, with the same value.
878 If the value is a dict, the function is called recursively. to_check can
879 contain more keys than reference, that will not be tested.
880 The function returns at the first difference found.
881 """
882 fail_keys = []
883 # filter the keys in the reference dictionary
884 if ignore:
885 ignore_re = re.compile(ignore)
886 keys = [key for key in reference if not ignore_re.match(key)]
887 else:
888 keys = reference.keys()
889 # loop over the keys (not ignored) in the reference dictionary
890 for k in keys:
891 if k in to_check: # the key must be in the dictionary to_check
892 if (type(reference[k]) is dict) and (type(to_check[k]) is dict):
893 # if both reference and to_check values are dictionaries, recurse
894 failed = fail_keys = cmpTreesDicts(reference[k], to_check[k],
895 ignore)
896 else:
897 # compare the two values
898 failed = to_check[k] != reference[k]
899 else: # handle missing keys in the dictionary to check (i.e. failure)
900 to_check[k] = None
901 failed = True
902 if failed:
903 fail_keys.insert(0, k)
904 break # exit from the loop at the first failure
905 return fail_keys # return the list of keys bringing to the different values
906
907
GaudiTest.cmpTreesDicts
def cmpTreesDicts(reference, to_check, ignore=None)
Definition: GaudiTest.py:875
gaudirun.type
type
Definition: gaudirun.py:142

## ◆ convert_xml_illegal_chars()

def GaudiTest.convert_xml_illegal_chars | ( |  | val | ) |

Definition at line 364 of file GaudiTest.py.

364 def convert_xml_illegal_chars(val):
365 return _illegal_xml_chars_RE.sub(hexreplace, val)
366
367
GaudiTest.convert_xml_illegal_chars
def convert_xml_illegal_chars(val)
Definition: GaudiTest.py:364

## ◆ countErrorLines()

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

    Count the number of messages with required severity (by default ERROR and FATAL)
    and check if their numbers match the expected ones (0 by default).
    The dictionary "expected" can be used to tune the number of errors and fatals
    allowed, or to limit the number of expected warnings etc.

Definition at line 763 of file GaudiTest.py.

763 def countErrorLines(expected={'ERROR': 0, 'FATAL': 0}, **kwargs):
764 """
765 Count the number of messages with required severity (by default ERROR and FATAL)
766 and check if their numbers match the expected ones (0 by default).
767 The dictionary "expected" can be used to tune the number of errors and fatals
768 allowed, or to limit the number of expected warnings etc.
769 """
770 stdout = kwargs["stdout"]
771 result = kwargs["result"]
772 causes = kwargs["causes"]
773
774 # prepare the dictionary to record the extracted lines
775 errors = {}
776 for sev in expected:
777 errors[sev] = []
778
779 outlines = stdout.splitlines()
780 from math import log10
781 fmt = "%%%dd - %%s" % (int(log10(len(outlines)) + 1))
782
783 linecount = 0
784 for l in outlines:
785 linecount += 1
786 words = l.split()
787 if len(words) >= 2 and words[1] in errors:
788 errors[words[1]].append(fmt % (linecount, l.rstrip()))
789
790 for e in errors:
791 if len(errors[e]) != expected[e]:
792 causes.append('%s(%d)' % (e, len(errors[e])))
793 result["GaudiTest.lines.%s" % e] = result.Quote('\n'.join(
794 errors[e]))
795 result["GaudiTest.lines.%s.expected#" % e] = result.Quote(
796 str(expected[e]))
797
798 return causes
799
800
GaudiTest.countErrorLines
def countErrorLines(expected={ 'ERROR':0, 'FATAL':0}, **kwargs)
Definition: GaudiTest.py:763

## ◆ escape_xml_illegal_chars()

def GaudiTest.escape_xml_illegal_chars | ( |  | val,
|  |  | replacement = `'?'`
| ) |  |

    Filter out characters that are illegal in XML.
    Looks for any character in val that is not allowed in XML
    and replaces it with replacement ('?' by default).

Definition at line 368 of file GaudiTest.py.

368 def escape_xml_illegal_chars(val, replacement='?'):
369 """Filter out characters that are illegal in XML.
370 Looks for any character in val that is not allowed in XML
371 and replaces it with replacement ('?' by default).
372
373 """
374 return _illegal_xml_chars_RE.sub(replacement, val)
375
376
GaudiTest.escape_xml_illegal_chars
def escape_xml_illegal_chars(val, replacement='?')
Definition: GaudiTest.py:368

## ◆ findHistosSummaries()

def GaudiTest.findHistosSummaries | ( |  | stdout | ) |

    Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 993 of file GaudiTest.py.

993 def findHistosSummaries(stdout):
994 """
995 Scan stdout to find ROOT TTree summaries and digest them.
996 """
997 outlines = stdout.splitlines()
998 nlines = len(outlines) - 1
999 summaries = {}
1000 global h_count_re
1001
1002 pos = 0
1003 while pos < nlines:
1004 summ = {}
1005 # find first line of block:
1006 match = h_count_re.search(outlines[pos])
1007 while pos < nlines and not match:
1008 pos += 1
1009 match = h_count_re.search(outlines[pos])
1010 if match:
1011 summ, pos = parseHistosSummary(outlines, pos)
1012 summaries.update(summ)
1013 return summaries
1014
1015
GaudiTest.parseHistosSummary
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:923
GaudiTest.findHistosSummaries
def findHistosSummaries(stdout)
Definition: GaudiTest.py:993

## ◆ findReferenceBlock()

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

709 def findReferenceBlock(reference,
710 stdout,
711 result,
712 causes,
713 signature_offset=0,
714 signature=None,
715 id=None):
716 """
717 Given a block of text, tries to find it in the output.
718 The block had to be identified by a signature line. By default, the first
719 line is used as signature, or the line pointed to by signature_offset. If
720 signature_offset points outside the block, a signature line can be passed as
721 signature argument. Note: if 'signature' is None (the default), a negative
722 signature_offset is interpreted as index in a list (e.g. -1 means the last
723 line), otherwise the it is interpreted as the number of lines before the
724 first one of the block the signature must appear.
725 The parameter 'id' allow to distinguish between different calls to this
726 function in the same validation code.
727 """
728 # split reference file, sanitize EOLs and remove empty lines
729 reflines = filter(None, map(lambda s: s.rstrip(), reference.splitlines()))
730 if not reflines:
731 raise RuntimeError("Empty (or null) reference")
732 # the same on standard output
733 outlines = filter(None, map(lambda s: s.rstrip(), stdout.splitlines()))
734
735 res_field = "GaudiTest.RefBlock"
736 if id:
737 res_field += "_%s" % id
738
739 if signature is None:
740 if signature_offset < 0:
741 signature_offset = len(reference) + signature_offset
742 signature = reflines[signature_offset]
743 # find the reference block in the output file
744 try:
745 pos = outlines.index(signature)
746 outlines = outlines[pos - signature_offset:pos + len(reflines) -
747 signature_offset]
748 if reflines != outlines:
749 msg = "standard output"
750 # I do not want 2 messages in causes if teh function is called twice
751 if not msg in causes:
752 causes.append(msg)
753 result[res_field + ".observed"] = result.Quote("\n".join(outlines))
754 except ValueError:
755 causes.append("missing signature")
756 result[res_field + ".signature"] = result.Quote(signature)
757 if len(reflines) > 1 or signature != reflines[0]:
758 result[res_field + ".expected"] = result.Quote("\n".join(reflines))
759
760 return causes
761
762
Containers::map
struct GAUDI_API map
Parametrisation class for map-like implementation.
Definition: KeyedObjectManager.h:25
GaudiTest.findReferenceBlock
def findReferenceBlock(reference, stdout, result, causes, signature_offset=0, signature=None, id=None)
Definition: GaudiTest.py:709

## ◆ findTTreeSummaries()

def GaudiTest.findTTreeSummaries | ( |  | stdout | ) |

    Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 853 of file GaudiTest.py.

853 def findTTreeSummaries(stdout):
854 """
855 Scan stdout to find ROOT TTree summaries and digest them.
856 """
857 stars = re.compile(r"^\*+$")
858 outlines = stdout.splitlines()
859 nlines = len(outlines)
860 trees = {}
861
862 i = 0
863 while i < nlines: # loop over the output
864 # look for
865 while i < nlines and not stars.match(outlines[i]):
866 i += 1
867 if i < nlines:
868 tree, i = _parseTTreeSummary(outlines, i)
869 if tree:
870 trees[tree["Name"]] = tree
871
872 return trees
873
874
GaudiTest._parseTTreeSummary
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:801
GaudiTest.findTTreeSummaries
def findTTreeSummaries(stdout)
Definition: GaudiTest.py:853

## ◆ getCmpFailingValues()

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

Definition at line 908 of file GaudiTest.py.

908 def getCmpFailingValues(reference, to_check, fail_path):
909 c = to_check
910 r = reference
911 for k in fail_path:
912 c = c.get(k, None)
913 r = r.get(k, None)
914 if c is None or r is None:
915 break # one of the dictionaries is not deep enough
916 return (fail_path, r, c)
917
918
919 # signature of the print-out of the histograms
GaudiTest.getCmpFailingValues
def getCmpFailingValues(reference, to_check, fail_path)
Definition: GaudiTest.py:908

## ◆ hexConvert()

def GaudiTest.hexConvert | ( |  | char | ) |

Definition at line 360 of file GaudiTest.py.

360 def hexConvert(char):
361 return hex(ord(char))
362
363
GaudiTest.hexConvert
def hexConvert(char)
Definition: GaudiTest.py:360
MSG::hex
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:271

## ◆ hexreplace()

def GaudiTest.hexreplace | ( |  | match | ) |

Definition at line 355 of file GaudiTest.py.

355 def hexreplace(match):
356 "Return the hex string "
357 return "".join(map(hexConvert, match.group()))
358
359
GaudiTest.hexreplace
def hexreplace(match)
Definition: GaudiTest.py:355
Containers::map
struct GAUDI_API map
Parametrisation class for map-like implementation.
Definition: KeyedObjectManager.h:25

## ◆ parseHistosSummary()

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

923 def parseHistosSummary(lines, pos):
924 """
925 Extract the histograms infos from the lines starting at pos.
926 Returns the position of the first line after the summary block.
927 """
928 global h_count_re
929 h_table_head = re.compile(
930 r'SUCCESS\s+(1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"'
931 )
932 h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
933
934 nlines = len(lines)
935
936 # decode header
937 m = h_count_re.search(lines[pos])
938 name = m.group(1).strip()
939 total = int(m.group(2))
940 header = {}
941 for k, v in [x.split("=") for x in m.group(3).split()]:
942 header[k] = int(v)
943 pos += 1
944 header["Total"] = total
945
946 summ = {}
947 while pos < nlines:
948 m = h_table_head.search(lines[pos])
949 if m:
950 t, d = m.groups(1) # type and directory
951 t = t.replace(" profile", "Prof")
952 pos += 1
953 if pos < nlines:
954 l = lines[pos]
955 else:
956 l = ""
957 cont = {}
958 if l.startswith(" | ID"):
959 # table format
960 titles = [x.strip() for x in l.split("|")][1:]
961 pos += 1
962 while pos < nlines and lines[pos].startswith(" |"):
963 l = lines[pos]
964 values = [x.strip() for x in l.split("|")][1:]
965 hcont = {}
966 for i in range(len(titles)):
967 hcont[titles[i]] = values[i]
968 cont[hcont["ID"]] = hcont
969 pos += 1
970 elif l.startswith(" ID="):
971 while pos < nlines and lines[pos].startswith(" ID="):
972 values = [
973 x.strip()
974 for x in h_short_summ.search(lines[pos]).groups()
975 ]
976 cont[values[0]] = values
977 pos += 1
978 else: # not interpreted
979 raise RuntimeError(
980 "Cannot understand line %d: '%s'" % (pos, l))
981 if not d in summ:
982 summ[d] = {}
983 summ[d][t] = cont
984 summ[d]["header"] = header
985 else:
986 break
987 if not summ:
988 # If the full table is not present, we use only the header
989 summ[name] = {"header": header}
990 return summ, pos
991
992
GaudiTest.parseHistosSummary
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:923
Gaudi::Functional::details::zip::range
decltype(auto) range(Args &&... args)
Zips multiple containers together to form a single range.
Definition: FunctionalDetails.h:71

## ◆ rationalizepath()

def GaudiTest.rationalizepath | ( |  | p | ) |

Definition at line 330 of file GaudiTest.py.

330 def rationalizepath(p):
331 np = os.path.normpath(os.path.expandvars(p))
332 if os.path.exists(np):
333 p = os.path.realpath(np)
334 return p
335
336
337 # XML Escaping character
GaudiTest.rationalizepath
def rationalizepath(p)
Definition: GaudiTest.py:330

## ◆ ROOT6WorkAroundEnabled()

def GaudiTest.ROOT6WorkAroundEnabled | ( |  | id = `None` | ) |

Definition at line 26 of file GaudiTest.py.

26 def ROOT6WorkAroundEnabled(id=None):
27 # dummy implementation
28 return False
29
30
31 # ensure the preferred locale
32 os.environ['LC_ALL'] = 'C'
33
34 # Needed for the XML wrapper
35 try:
GaudiTest.ROOT6WorkAroundEnabled
def ROOT6WorkAroundEnabled(id=None)
Definition: GaudiTest.py:26

## ◆ total_seconds_replacement()

def GaudiTest.total_seconds_replacement | ( |  | timedelta | ) |

Definition at line 43 of file GaudiTest.py.

43 def total_seconds_replacement(timedelta):
44 return timedelta.days * 86400 + timedelta.seconds + timedelta.microseconds / 1000000
45
46
GaudiTest.total_seconds_replacement
def total_seconds_replacement(timedelta)
Definition: GaudiTest.py:43

## ◆ which()

def GaudiTest.which | ( |  | executable | ) |

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

309 def which(executable):
310 """
311 Locates an executable in the executables path ($PATH) and returns the full
312 path to it. An application is looked for with or without the '.exe' suffix.
313 If the executable cannot be found, None is returned
314 """
315 if os.path.isabs(executable):
316 if not os.path.exists(executable):
317 if executable.endswith('.exe'):
318 if os.path.exists(executable[:-4]):
319 return executable[:-4]
320 return executable
321 for d in os.environ.get("PATH").split(os.pathsep):
322 fullpath = os.path.join(d, executable)
323 if os.path.exists(fullpath):
324 return fullpath
325 if executable.endswith('.exe'):
326 return which(executable[:-4])
327 return None
328
329
GaudiTest.which
def which(executable)
Definition: GaudiTest.py:309

## Variable Documentation

## ◆ __author__

string GaudiTest.__author__ = 'Marco Clemencic CERN/PH-LBC'
| private

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

Definition at line 5 of file GaudiTest.py.

## ◆ __processLine__

GaudiTest.__processLine__
| private

Definition at line 543 of file GaudiTest.py.

## ◆ _illegal_xml_chars_RE

GaudiTest._illegal_xml_chars_RE
| private

Initial value:
1 = re.compile(
2 u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')

Definition at line 351 of file GaudiTest.py.

## ◆ h_count_re

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

Definition at line 920 of file GaudiTest.py.

## ◆ lineSkipper

GaudiTest.lineSkipper

Definition at line 596 of file GaudiTest.py.

## ◆ maskPointers

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

Definition at line 538 of file GaudiTest.py.

## ◆ normalizeDate

GaudiTest.normalizeDate

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

Definition at line 539 of file GaudiTest.py.

## ◆ normalizeEOL

GaudiTest.normalizeEOL = FilePreprocessor()

Definition at line 542 of file GaudiTest.py.

## ◆ normalizeExamples

tuple GaudiTest.normalizeExamples = maskPointers + normalizeDate

Definition at line 569 of file GaudiTest.py.

## ◆ regexps

GaudiTest.regexps

Definition at line 661 of file GaudiTest.py.

## ◆ skipEmptyLines

GaudiTest.skipEmptyLines = FilePreprocessor()

Definition at line 545 of file GaudiTest.py.