The Gaudi Framework  v29r0 (ff2e7097)
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
 
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

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

776 def _parseTTreeSummary(lines, pos):
777  """
778  Parse the TTree summary table in lines, starting from pos.
779  Returns a tuple with the dictionary with the digested informations and the
780  position of the first line after the summary.
781  """
782  result = {}
783  i = pos + 1 # first line is a sequence of '*'
784  count = len(lines)
785 
786  def splitcols(l): return [f.strip() for f in l.strip("*\n").split(':', 2)]
787 
788  def parseblock(ll):
789  r = {}
790  cols = splitcols(ll[0])
791  r["Name"], r["Title"] = cols[1:]
792 
793  cols = splitcols(ll[1])
794  r["Entries"] = int(cols[1])
795 
796  sizes = cols[2].split()
797  r["Total size"] = int(sizes[2])
798  if sizes[-1] == "memory":
799  r["File size"] = 0
800  else:
801  r["File size"] = int(sizes[-1])
802 
803  cols = splitcols(ll[2])
804  sizes = cols[2].split()
805  if cols[0] == "Baskets":
806  r["Baskets"] = int(cols[1])
807  r["Basket size"] = int(sizes[2])
808  r["Compression"] = float(sizes[-1])
809  return r
810 
811  if i < (count - 3) and lines[i].startswith("*Tree"):
812  result = parseblock(lines[i:i + 3])
813  result["Branches"] = {}
814  i += 4
815  while i < (count - 3) and lines[i].startswith("*Br"):
816  if i < (count - 2) and lines[i].startswith("*Branch "):
817  # skip branch header
818  i += 3
819  continue
820  branch = parseblock(lines[i:i + 3])
821  result["Branches"][branch["Name"]] = branch
822  i += 4
823 
824  return (result, i)
825 
826 
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:776
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 849 of file GaudiTest.py.

849 def cmpTreesDicts(reference, to_check, ignore=None):
850  """
851  Check that all the keys in reference are in to_check too, with the same value.
852  If the value is a dict, the function is called recursively. to_check can
853  contain more keys than reference, that will not be tested.
854  The function returns at the first difference found.
855  """
856  fail_keys = []
857  # filter the keys in the reference dictionary
858  if ignore:
859  ignore_re = re.compile(ignore)
860  keys = [key for key in reference if not ignore_re.match(key)]
861  else:
862  keys = reference.keys()
863  # loop over the keys (not ignored) in the reference dictionary
864  for k in keys:
865  if k in to_check: # the key must be in the dictionary to_check
866  if (type(reference[k]) is dict) and (type(to_check[k]) is dict):
867  # if both reference and to_check values are dictionaries, recurse
868  failed = fail_keys = cmpTreesDicts(
869  reference[k], to_check[k], ignore)
870  else:
871  # compare the two values
872  failed = to_check[k] != reference[k]
873  else: # handle missing keys in the dictionary to check (i.e. failure)
874  to_check[k] = None
875  failed = True
876  if failed:
877  fail_keys.insert(0, k)
878  break # exit from the loop at the first failure
879  return fail_keys # return the list of keys bringing to the different values
880 
881 
def cmpTreesDicts(reference, to_check, ignore=None)
Definition: GaudiTest.py:849
def GaudiTest.convert_xml_illegal_chars (   val)

Definition at line 357 of file GaudiTest.py.

358  return _illegal_xml_chars_RE.sub(hexreplace, val)
359 
360 
def convert_xml_illegal_chars(val)
Definition: GaudiTest.py:357
def GaudiTest.countErrorLines (   expected = {'ERROR': 0,
  FATAL 
)

Definition at line 738 of file GaudiTest.py.

738 def countErrorLines(expected={'ERROR': 0, 'FATAL': 0}, **kwargs):
739  """
740  Count the number of messages with required severity (by default ERROR and FATAL)
741  and check if their numbers match the expected ones (0 by default).
742  The dictionary "expected" can be used to tune the number of errors and fatals
743  allowed, or to limit the number of expected warnings etc.
744  """
745  stdout = kwargs["stdout"]
746  result = kwargs["result"]
747  causes = kwargs["causes"]
748 
749  # prepare the dictionary to record the extracted lines
750  errors = {}
751  for sev in expected:
752  errors[sev] = []
753 
754  outlines = stdout.splitlines()
755  from math import log10
756  fmt = "%%%dd - %%s" % (int(log10(len(outlines)) + 1))
757 
758  linecount = 0
759  for l in outlines:
760  linecount += 1
761  words = l.split()
762  if len(words) >= 2 and words[1] in errors:
763  errors[words[1]].append(fmt % (linecount, l.rstrip()))
764 
765  for e in errors:
766  if len(errors[e]) != expected[e]:
767  causes.append('%s(%d)' % (e, len(errors[e])))
768  result["GaudiTest.lines.%s" %
769  e] = result.Quote('\n'.join(errors[e]))
770  result["GaudiTest.lines.%s.expected#" %
771  e] = result.Quote(str(expected[e]))
772 
773  return causes
774 
775 
def countErrorLines
Definition: GaudiTest.py:738
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 361 of file GaudiTest.py.

361 def escape_xml_illegal_chars(val, replacement='?'):
362  """Filter out characters that are illegal in XML.
363  Looks for any character in val that is not allowed in XML
364  and replaces it with replacement ('?' by default).
365 
366  """
367  return _illegal_xml_chars_RE.sub(replacement, val)
368 
def escape_xml_illegal_chars(val, replacement='?')
Definition: GaudiTest.py:361
def GaudiTest.findHistosSummaries (   stdout)
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 964 of file GaudiTest.py.

965  """
966  Scan stdout to find ROOT TTree summaries and digest them.
967  """
968  outlines = stdout.splitlines()
969  nlines = len(outlines) - 1
970  summaries = {}
971  global h_count_re
972 
973  pos = 0
974  while pos < nlines:
975  summ = {}
976  # find first line of block:
977  match = h_count_re.search(outlines[pos])
978  while pos < nlines and not match:
979  pos += 1
980  match = h_count_re.search(outlines[pos])
981  if match:
982  summ, pos = parseHistosSummary(outlines, pos)
983  summaries.update(summ)
984  return summaries
985 
986 
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:897
def findHistosSummaries(stdout)
Definition: GaudiTest.py:964
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 690 of file GaudiTest.py.

690  id=None):
691  """
692  Given a block of text, tries to find it in the output.
693  The block had to be identified by a signature line. By default, the first
694  line is used as signature, or the line pointed to by signature_offset. If
695  signature_offset points outside the block, a signature line can be passed as
696  signature argument. Note: if 'signature' is None (the default), a negative
697  signature_offset is interpreted as index in a list (e.g. -1 means the last
698  line), otherwise the it is interpreted as the number of lines before the
699  first one of the block the signature must appear.
700  The parameter 'id' allow to distinguish between different calls to this
701  function in the same validation code.
702  """
703  # split reference file, sanitize EOLs and remove empty lines
704  reflines = filter(None, map(lambda s: s.rstrip(), reference.splitlines()))
705  if not reflines:
706  raise RuntimeError("Empty (or null) reference")
707  # the same on standard output
708  outlines = filter(None, map(lambda s: s.rstrip(), stdout.splitlines()))
709 
710  res_field = "GaudiTest.RefBlock"
711  if id:
712  res_field += "_%s" % id
713 
714  if signature is None:
715  if signature_offset < 0:
716  signature_offset = len(reference) + signature_offset
717  signature = reflines[signature_offset]
718  # find the reference block in the output file
719  try:
720  pos = outlines.index(signature)
721  outlines = outlines[pos - signature_offset:pos +
722  len(reflines) - signature_offset]
723  if reflines != outlines:
724  msg = "standard output"
725  # I do not want 2 messages in causes if teh function is called twice
726  if not msg in causes:
727  causes.append(msg)
728  result[res_field + ".observed"] = result.Quote("\n".join(outlines))
729  except ValueError:
730  causes.append("missing signature")
731  result[res_field + ".signature"] = result.Quote(signature)
732  if len(reflines) > 1 or signature != reflines[0]:
733  result[res_field + ".expected"] = result.Quote("\n".join(reflines))
734 
735  return causes
736 
737 
struct GAUDI_API map
Parametrisation class for map-like implementation.
def GaudiTest.findTTreeSummaries (   stdout)
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 827 of file GaudiTest.py.

827 def findTTreeSummaries(stdout):
828  """
829  Scan stdout to find ROOT TTree summaries and digest them.
830  """
831  stars = re.compile(r"^\*+$")
832  outlines = stdout.splitlines()
833  nlines = len(outlines)
834  trees = {}
835 
836  i = 0
837  while i < nlines: # loop over the output
838  # look for
839  while i < nlines and not stars.match(outlines[i]):
840  i += 1
841  if i < nlines:
842  tree, i = _parseTTreeSummary(outlines, i)
843  if tree:
844  trees[tree["Name"]] = tree
845 
846  return trees
847 
848 
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:776
def findTTreeSummaries(stdout)
Definition: GaudiTest.py:827
def GaudiTest.getCmpFailingValues (   reference,
  to_check,
  fail_path 
)

Definition at line 882 of file GaudiTest.py.

882 def getCmpFailingValues(reference, to_check, fail_path):
883  c = to_check
884  r = reference
885  for k in fail_path:
886  c = c.get(k, None)
887  r = r.get(k, None)
888  if c is None or r is None:
889  break # one of the dictionaries is not deep enough
890  return (fail_path, r, c)
891 
892 
893 # signature of the print-out of the histograms
def getCmpFailingValues(reference, to_check, fail_path)
Definition: GaudiTest.py:882
def GaudiTest.hexConvert (   char)

Definition at line 353 of file GaudiTest.py.

353 def hexConvert(char):
354  return hex(ord(char))
355 
356 
def hexConvert(char)
Definition: GaudiTest.py:353
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:304
def GaudiTest.hexreplace (   match)

Definition at line 348 of file GaudiTest.py.

348 def hexreplace(match):
349  "Return the hex string "
350  return "".join(map(hexConvert, match.group()))
351 
352 
def hexreplace(match)
Definition: GaudiTest.py:348
struct GAUDI_API map
Parametrisation class for map-like implementation.
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 897 of file GaudiTest.py.

897 def parseHistosSummary(lines, pos):
898  """
899  Extract the histograms infos from the lines starting at pos.
900  Returns the position of the first line after the summary block.
901  """
902  global h_count_re
903  h_table_head = re.compile(
904  r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"')
905  h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
906 
907  nlines = len(lines)
908 
909  # decode header
910  m = h_count_re.search(lines[pos])
911  name = m.group(1).strip()
912  total = int(m.group(2))
913  header = {}
914  for k, v in [x.split("=") for x in m.group(3).split()]:
915  header[k] = int(v)
916  pos += 1
917  header["Total"] = total
918 
919  summ = {}
920  while pos < nlines:
921  m = h_table_head.search(lines[pos])
922  if m:
923  t, d = m.groups(1) # type and directory
924  t = t.replace(" profile", "Prof")
925  pos += 1
926  if pos < nlines:
927  l = lines[pos]
928  else:
929  l = ""
930  cont = {}
931  if l.startswith(" | ID"):
932  # table format
933  titles = [x.strip() for x in l.split("|")][1:]
934  pos += 1
935  while pos < nlines and lines[pos].startswith(" |"):
936  l = lines[pos]
937  values = [x.strip() for x in l.split("|")][1:]
938  hcont = {}
939  for i in range(len(titles)):
940  hcont[titles[i]] = values[i]
941  cont[hcont["ID"]] = hcont
942  pos += 1
943  elif l.startswith(" ID="):
944  while pos < nlines and lines[pos].startswith(" ID="):
945  values = [x.strip()
946  for x in h_short_summ.search(lines[pos]).groups()]
947  cont[values[0]] = values
948  pos += 1
949  else: # not interpreted
950  raise RuntimeError(
951  "Cannot understand line %d: '%s'" % (pos, l))
952  if not d in summ:
953  summ[d] = {}
954  summ[d][t] = cont
955  summ[d]["header"] = header
956  else:
957  break
958  if not summ:
959  # If the full table is not present, we use only the header
960  summ[name] = {"header": header}
961  return summ, pos
962 
963 
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:897
decltype(auto) range(Args &&...args)
Zips multiple containers together to form a single range.
def GaudiTest.rationalizepath (   p)

Definition at line 323 of file GaudiTest.py.

324  np = os.path.normpath(os.path.expandvars(p))
325  if os.path.exists(np):
326  p = os.path.realpath(np)
327  return p
328 
329 
330 # XML Escaping character
def rationalizepath(p)
Definition: GaudiTest.py:323
def GaudiTest.ROOT6WorkAroundEnabled (   id = None)

Definition at line 25 of file GaudiTest.py.

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

Definition at line 41 of file GaudiTest.py.

42  return timedelta.days * 86400 + timedelta.seconds + timedelta.microseconds / 1000000
43 
44 
def total_seconds_replacement(timedelta)
Definition: GaudiTest.py:41
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 302 of file GaudiTest.py.

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

Variable Documentation

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.

GaudiTest.__processLine__
private

Definition at line 530 of file GaudiTest.py.

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

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

Definition at line 894 of file GaudiTest.py.

GaudiTest.lineSkipper

Definition at line 581 of file GaudiTest.py.

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

Definition at line 526 of file GaudiTest.py.

GaudiTest.normalizeDate
Initial value:
1 = 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)?",
2  "00:00:00 1970-01-01")

Definition at line 527 of file GaudiTest.py.

GaudiTest.normalizeEOL = FilePreprocessor()

Definition at line 529 of file GaudiTest.py.

tuple GaudiTest.normalizeExamples = maskPointers+normalizeDate

Definition at line 556 of file GaudiTest.py.

GaudiTest.regexps

Definition at line 642 of file GaudiTest.py.

GaudiTest.skipEmptyLines = FilePreprocessor()

Definition at line 532 of file GaudiTest.py.