The Gaudi Framework  v33r0 (d5ea422b)
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 812 of file GaudiTest.py.

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

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

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

◆ convert_xml_illegal_chars()

def GaudiTest.convert_xml_illegal_chars (   val)

Definition at line 375 of file GaudiTest.py.

376  return _illegal_xml_chars_RE.sub(hexreplace, val)
377 
378 
def convert_xml_illegal_chars(val)
Definition: GaudiTest.py:375

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

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

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

379 def escape_xml_illegal_chars(val, replacement='?'):
380  """Filter out characters that are illegal in XML.
381  Looks for any character in val that is not allowed in XML
382  and replaces it with replacement ('?' by default).
383 
384  """
385  return _illegal_xml_chars_RE.sub(replacement, val)
386 
387 
def escape_xml_illegal_chars(val, replacement='?')
Definition: GaudiTest.py:379

◆ findHistosSummaries()

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

Definition at line 1004 of file GaudiTest.py.

1004 def findHistosSummaries(stdout):
1005  """
1006  Scan stdout to find ROOT TTree summaries and digest them.
1007  """
1008  outlines = stdout.splitlines()
1009  nlines = len(outlines) - 1
1010  summaries = {}
1011  global h_count_re
1012 
1013  pos = 0
1014  while pos < nlines:
1015  summ = {}
1016  # find first line of block:
1017  match = h_count_re.search(outlines[pos])
1018  while pos < nlines and not match:
1019  pos += 1
1020  match = h_count_re.search(outlines[pos])
1021  if match:
1022  summ, pos = parseHistosSummary(outlines, pos)
1023  summaries.update(summ)
1024  return summaries
1025 
1026 
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:934
def findHistosSummaries(stdout)
Definition: GaudiTest.py:1004

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

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

◆ findTTreeSummaries()

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

Definition at line 864 of file GaudiTest.py.

864 def findTTreeSummaries(stdout):
865  """
866  Scan stdout to find ROOT TTree summaries and digest them.
867  """
868  stars = re.compile(r"^\*+$")
869  outlines = stdout.splitlines()
870  nlines = len(outlines)
871  trees = {}
872 
873  i = 0
874  while i < nlines: # loop over the output
875  # look for
876  while i < nlines and not stars.match(outlines[i]):
877  i += 1
878  if i < nlines:
879  tree, i = _parseTTreeSummary(outlines, i)
880  if tree:
881  trees[tree["Name"]] = tree
882 
883  return trees
884 
885 
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:812
def findTTreeSummaries(stdout)
Definition: GaudiTest.py:864

◆ getCmpFailingValues()

def GaudiTest.getCmpFailingValues (   reference,
  to_check,
  fail_path 
)

Definition at line 919 of file GaudiTest.py.

919 def getCmpFailingValues(reference, to_check, fail_path):
920  c = to_check
921  r = reference
922  for k in fail_path:
923  c = c.get(k, None)
924  r = r.get(k, None)
925  if c is None or r is None:
926  break # one of the dictionaries is not deep enough
927  return (fail_path, r, c)
928 
929 
930 # signature of the print-out of the histograms
def getCmpFailingValues(reference, to_check, fail_path)
Definition: GaudiTest.py:919

◆ hexConvert()

def GaudiTest.hexConvert (   char)

Definition at line 371 of file GaudiTest.py.

371 def hexConvert(char):
372  return hex(ord(char))
373 
374 
def hexConvert(char)
Definition: GaudiTest.py:371
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:281

◆ hexreplace()

def GaudiTest.hexreplace (   match)

Definition at line 366 of file GaudiTest.py.

366 def hexreplace(match):
367  "Return the hex string "
368  return "".join(map(hexConvert, match.group()))
369 
370 
def hexreplace(match)
Definition: GaudiTest.py:366
struct GAUDI_API map
Parametrisation class for map-like implementation.

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

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

◆ rationalizepath()

def GaudiTest.rationalizepath (   p)

Definition at line 341 of file GaudiTest.py.

341 def rationalizepath(p):
342  np = os.path.normpath(os.path.expandvars(p))
343  if os.path.exists(np):
344  p = os.path.realpath(np)
345  return p
346 
347 
348 # XML Escaping character
def rationalizepath(p)
Definition: GaudiTest.py:341

◆ ROOT6WorkAroundEnabled()

def GaudiTest.ROOT6WorkAroundEnabled (   id = None)

Definition at line 37 of file GaudiTest.py.

37  def ROOT6WorkAroundEnabled(id=None):
38  # dummy implementation
39  return False
40 
41 
42 # ensure the preferred locale
43 os.environ['LC_ALL'] = 'C'
44 
45 # Needed for the XML wrapper
46 try:
def ROOT6WorkAroundEnabled(id=None)
Definition: GaudiTest.py:37

◆ total_seconds_replacement()

def GaudiTest.total_seconds_replacement (   timedelta)

Definition at line 54 of file GaudiTest.py.

54 def total_seconds_replacement(timedelta):
55  return timedelta.days * 86400 + timedelta.seconds + timedelta.microseconds / 1000000
56 
57 
def total_seconds_replacement(timedelta)
Definition: GaudiTest.py:54

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

320 def which(executable):
321  """
322  Locates an executable in the executables path ($PATH) and returns the full
323  path to it. An application is looked for with or without the '.exe' suffix.
324  If the executable cannot be found, None is returned
325  """
326  if os.path.isabs(executable):
327  if not os.path.exists(executable):
328  if executable.endswith('.exe'):
329  if os.path.exists(executable[:-4]):
330  return executable[:-4]
331  return executable
332  for d in os.environ.get("PATH").split(os.pathsep):
333  fullpath = os.path.join(d, executable)
334  if os.path.exists(fullpath):
335  return fullpath
336  if executable.endswith('.exe'):
337  return which(executable[:-4])
338  return None
339 
340 
def which(executable)
Definition: GaudiTest.py:320

Variable Documentation

◆ __author__

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

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

Definition at line 16 of file GaudiTest.py.

◆ __processLine__

GaudiTest.__processLine__
private

Definition at line 554 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 362 of file GaudiTest.py.

◆ h_count_re

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

Definition at line 931 of file GaudiTest.py.

◆ lineSkipper

GaudiTest.lineSkipper

Definition at line 607 of file GaudiTest.py.

◆ maskPointers

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

Definition at line 549 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 550 of file GaudiTest.py.

◆ normalizeEOL

GaudiTest.normalizeEOL = FilePreprocessor()

Definition at line 553 of file GaudiTest.py.

◆ normalizeExamples

tuple GaudiTest.normalizeExamples = maskPointers + normalizeDate

Definition at line 580 of file GaudiTest.py.

◆ regexps

GaudiTest.regexps

Definition at line 672 of file GaudiTest.py.

◆ skipEmptyLines

GaudiTest.skipEmptyLines = FilePreprocessor()

Definition at line 556 of file GaudiTest.py.