The Gaudi Framework  v32r2 (46d42edc)
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 802 of file GaudiTest.py.

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

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

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

◆ convert_xml_illegal_chars()

def GaudiTest.convert_xml_illegal_chars (   val)

Definition at line 365 of file GaudiTest.py.

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

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

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

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

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

◆ findHistosSummaries()

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

Definition at line 994 of file GaudiTest.py.

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

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

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

◆ findTTreeSummaries()

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

Definition at line 854 of file GaudiTest.py.

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

◆ getCmpFailingValues()

def GaudiTest.getCmpFailingValues (   reference,
  to_check,
  fail_path 
)

Definition at line 909 of file GaudiTest.py.

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

◆ hexConvert()

def GaudiTest.hexConvert (   char)

Definition at line 361 of file GaudiTest.py.

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

◆ hexreplace()

def GaudiTest.hexreplace (   match)

Definition at line 356 of file GaudiTest.py.

356 def hexreplace(match):
357  "Return the hex string "
358  return "".join(map(hexConvert, match.group()))
359 
360 
def hexreplace(match)
Definition: GaudiTest.py:356
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 924 of file GaudiTest.py.

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

◆ rationalizepath()

def GaudiTest.rationalizepath (   p)

Definition at line 331 of file GaudiTest.py.

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

◆ ROOT6WorkAroundEnabled()

def GaudiTest.ROOT6WorkAroundEnabled (   id = None)

Definition at line 27 of file GaudiTest.py.

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

◆ total_seconds_replacement()

def GaudiTest.total_seconds_replacement (   timedelta)

Definition at line 44 of file GaudiTest.py.

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

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

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

Variable Documentation

◆ __author__

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

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

Definition at line 6 of file GaudiTest.py.

◆ __processLine__

GaudiTest.__processLine__
private

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

◆ h_count_re

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

Definition at line 921 of file GaudiTest.py.

◆ lineSkipper

GaudiTest.lineSkipper

Definition at line 597 of file GaudiTest.py.

◆ maskPointers

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

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

◆ normalizeEOL

GaudiTest.normalizeEOL = FilePreprocessor()

Definition at line 543 of file GaudiTest.py.

◆ normalizeExamples

tuple GaudiTest.normalizeExamples = maskPointers + normalizeDate

Definition at line 570 of file GaudiTest.py.

◆ regexps

GaudiTest.regexps

Definition at line 662 of file GaudiTest.py.

◆ skipEmptyLines

GaudiTest.skipEmptyLines = FilePreprocessor()

Definition at line 546 of file GaudiTest.py.