The Gaudi Framework  v31r0 (aeb156f0)
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 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 
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:801
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 
def cmpTreesDicts(reference, to_check, ignore=None)
Definition: GaudiTest.py:875
def GaudiTest.convert_xml_illegal_chars (   val)

Definition at line 364 of file GaudiTest.py.

365  return _illegal_xml_chars_RE.sub(hexreplace, val)
366 
367 
def convert_xml_illegal_chars(val)
Definition: GaudiTest.py:364
def GaudiTest.countErrorLines (   expected = {'ERROR': 0,
  FATAL 
)

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 
def countErrorLines
Definition: GaudiTest.py:763
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 
def escape_xml_illegal_chars(val, replacement='?')
Definition: GaudiTest.py:368
def GaudiTest.findHistosSummaries (   stdout)
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 993 of file GaudiTest.py.

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 
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:923
def findHistosSummaries(stdout)
Definition: GaudiTest.py:993
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 715 of file GaudiTest.py.

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 
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 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 
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:801
def findTTreeSummaries(stdout)
Definition: GaudiTest.py:853
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
def getCmpFailingValues(reference, to_check, fail_path)
Definition: GaudiTest.py:908
def GaudiTest.hexConvert (   char)

Definition at line 360 of file GaudiTest.py.

360 def hexConvert(char):
361  return hex(ord(char))
362 
363 
def hexConvert(char)
Definition: GaudiTest.py:360
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:271
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 
def hexreplace(match)
Definition: GaudiTest.py:355
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 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 
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:923
decltype(auto) range(Args &&...args)
Zips multiple containers together to form a single range.
def GaudiTest.rationalizepath (   p)

Definition at line 330 of file GaudiTest.py.

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
def rationalizepath(p)
Definition: GaudiTest.py:330
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:
def ROOT6WorkAroundEnabled(id=None)
Definition: GaudiTest.py:26
def GaudiTest.total_seconds_replacement (   timedelta)

Definition at line 43 of file GaudiTest.py.

44  return timedelta.days * 86400 + timedelta.seconds + timedelta.microseconds / 1000000
45 
46 
def total_seconds_replacement(timedelta)
Definition: GaudiTest.py:43
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 
def which(executable)
Definition: GaudiTest.py:309

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

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

Definition at line 920 of file GaudiTest.py.

GaudiTest.lineSkipper

Definition at line 596 of file GaudiTest.py.

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

Definition at line 538 of file GaudiTest.py.

GaudiTest.normalizeDate
Initial value:
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.

GaudiTest.normalizeEOL = FilePreprocessor()

Definition at line 542 of file GaudiTest.py.

tuple GaudiTest.normalizeExamples = maskPointers+normalizeDate

Definition at line 569 of file GaudiTest.py.

GaudiTest.regexps

Definition at line 661 of file GaudiTest.py.

GaudiTest.skipEmptyLines = FilePreprocessor()

Definition at line 545 of file GaudiTest.py.