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
 Special preprocessor sorting the list of strings (whitespace separated) that follow a signature on a single line. More...
 
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)
 Locates an executable in the executables path ($PATH) and returns the full path to it. More...
 
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 = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')
 
 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 712 of file GaudiTest.py.

712 def _parseTTreeSummary(lines, pos):
713  """
714  Parse the TTree summary table in lines, starting from pos.
715  Returns a tuple with the dictionary with the digested informations and the
716  position of the first line after the summary.
717  """
718  result = {}
719  i = pos + 1 # first line is a sequence of '*'
720  count = len(lines)
721 
722  splitcols = lambda l: [ f.strip() for f in l.strip("*\n").split(':',2) ]
723  def parseblock(ll):
724  r = {}
725  cols = splitcols(ll[0])
726  r["Name"], r["Title"] = cols[1:]
727 
728  cols = splitcols(ll[1])
729  r["Entries"] = int(cols[1])
730 
731  sizes = cols[2].split()
732  r["Total size"] = int(sizes[2])
733  if sizes[-1] == "memory":
734  r["File size"] = 0
735  else:
736  r["File size"] = int(sizes[-1])
737 
738  cols = splitcols(ll[2])
739  sizes = cols[2].split()
740  if cols[0] == "Baskets":
741  r["Baskets"] = int(cols[1])
742  r["Basket size"] = int(sizes[2])
743  r["Compression"] = float(sizes[-1])
744  return r
745 
746  if i < (count - 3) and lines[i].startswith("*Tree"):
747  result = parseblock(lines[i:i+3])
748  result["Branches"] = {}
749  i += 4
750  while i < (count - 3) and lines[i].startswith("*Br"):
751  if i < (count - 2) and lines[i].startswith("*Branch "):
752  # skip branch header
753  i += 3
754  continue
755  branch = parseblock(lines[i:i+3])
756  result["Branches"][branch["Name"]] = branch
757  i += 4
758 
759  return (result, i)
760 
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:712
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 782 of file GaudiTest.py.

782 def cmpTreesDicts(reference, to_check, ignore = None):
783  """
784  Check that all the keys in reference are in to_check too, with the same value.
785  If the value is a dict, the function is called recursively. to_check can
786  contain more keys than reference, that will not be tested.
787  The function returns at the first difference found.
788  """
789  fail_keys = []
790  # filter the keys in the reference dictionary
791  if ignore:
792  ignore_re = re.compile(ignore)
793  keys = [ key for key in reference if not ignore_re.match(key) ]
794  else:
795  keys = reference.keys()
796  # loop over the keys (not ignored) in the reference dictionary
797  for k in keys:
798  if k in to_check: # the key must be in the dictionary to_check
799  if (type(reference[k]) is dict) and (type(to_check[k]) is dict):
800  # if both reference and to_check values are dictionaries, recurse
801  failed = fail_keys = cmpTreesDicts(reference[k], to_check[k], ignore)
802  else:
803  # compare the two values
804  failed = to_check[k] != reference[k]
805  else: # handle missing keys in the dictionary to check (i.e. failure)
806  to_check[k] = None
807  failed = True
808  if failed:
809  fail_keys.insert(0, k)
810  break # exit from the loop at the first failure
811  return fail_keys # return the list of keys bringing to the different values
812 
def cmpTreesDicts(reference, to_check, ignore=None)
Definition: GaudiTest.py:782
def GaudiTest.convert_xml_illegal_chars (   val)

Definition at line 337 of file GaudiTest.py.

338  return _illegal_xml_chars_RE.sub(hexreplace, val)
339 
def convert_xml_illegal_chars(val)
Definition: GaudiTest.py:337
def GaudiTest.countErrorLines (   expected = {'ERROR':0,
  FATAL 
)

Definition at line 676 of file GaudiTest.py.

676 def countErrorLines(expected = {'ERROR':0, 'FATAL':0}, **kwargs):
677  """
678  Count the number of messages with required severity (by default ERROR and FATAL)
679  and check if their numbers match the expected ones (0 by default).
680  The dictionary "expected" can be used to tune the number of errors and fatals
681  allowed, or to limit the number of expected warnings etc.
682  """
683  stdout = kwargs["stdout"]
684  result = kwargs["result"]
685  causes = kwargs["causes"]
686 
687  # prepare the dictionary to record the extracted lines
688  errors = {}
689  for sev in expected:
690  errors[sev] = []
691 
692  outlines = stdout.splitlines()
693  from math import log10
694  fmt = "%%%dd - %%s" % (int(log10(len(outlines))+1))
695 
696  linecount = 0
697  for l in outlines:
698  linecount += 1
699  words = l.split()
700  if len(words) >= 2 and words[1] in errors:
701  errors[words[1]].append(fmt%(linecount,l.rstrip()))
702 
703  for e in errors:
704  if len(errors[e]) != expected[e]:
705  causes.append('%s(%d)'%(e,len(errors[e])))
706  result["GaudiTest.lines.%s"%e] = result.Quote('\n'.join(errors[e]))
707  result["GaudiTest.lines.%s.expected#"%e] = result.Quote(str(expected[e]))
708 
709  return causes
710 
711 
def countErrorLines
Definition: GaudiTest.py:676
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 340 of file GaudiTest.py.

340 def escape_xml_illegal_chars(val, replacement='?'):
341  """Filter out characters that are illegal in XML.
342  Looks for any character in val that is not allowed in XML
343  and replaces it with replacement ('?' by default).
344 
345  """
346  return _illegal_xml_chars_RE.sub(replacement, val)
347 
def escape_xml_illegal_chars(val, replacement='?')
Definition: GaudiTest.py:340
def GaudiTest.findHistosSummaries (   stdout)
Scan stdout to find ROOT TTree summaries and digest them.

Definition at line 889 of file GaudiTest.py.

890  """
891  Scan stdout to find ROOT TTree summaries and digest them.
892  """
893  outlines = stdout.splitlines()
894  nlines = len(outlines) - 1
895  summaries = {}
896  global h_count_re
897 
898  pos = 0
899  while pos < nlines:
900  summ = {}
901  # find first line of block:
902  match = h_count_re.search(outlines[pos])
903  while pos < nlines and not match:
904  pos += 1
905  match = h_count_re.search(outlines[pos])
906  if match:
907  summ, pos = parseHistosSummary(outlines, pos)
908  summaries.update(summ)
909  return summaries
910 
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:826
def findHistosSummaries(stdout)
Definition: GaudiTest.py:889
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 630 of file GaudiTest.py.

630  id = None):
631  """
632  Given a block of text, tries to find it in the output.
633  The block had to be identified by a signature line. By default, the first
634  line is used as signature, or the line pointed to by signature_offset. If
635  signature_offset points outside the block, a signature line can be passed as
636  signature argument. Note: if 'signature' is None (the default), a negative
637  signature_offset is interpreted as index in a list (e.g. -1 means the last
638  line), otherwise the it is interpreted as the number of lines before the
639  first one of the block the signature must appear.
640  The parameter 'id' allow to distinguish between different calls to this
641  function in the same validation code.
642  """
643  # split reference file, sanitize EOLs and remove empty lines
644  reflines = filter(None,map(lambda s: s.rstrip(), reference.splitlines()))
645  if not reflines:
646  raise RuntimeError("Empty (or null) reference")
647  # the same on standard output
648  outlines = filter(None,map(lambda s: s.rstrip(), stdout.splitlines()))
649 
650  res_field = "GaudiTest.RefBlock"
651  if id:
652  res_field += "_%s" % id
653 
654  if signature is None:
655  if signature_offset < 0:
656  signature_offset = len(reference)+signature_offset
657  signature = reflines[signature_offset]
658  # find the reference block in the output file
659  try:
660  pos = outlines.index(signature)
661  outlines = outlines[pos-signature_offset:pos+len(reflines)-signature_offset]
662  if reflines != outlines:
663  msg = "standard output"
664  # I do not want 2 messages in causes if teh function is called twice
665  if not msg in causes:
666  causes.append(msg)
667  result[res_field + ".observed"] = result.Quote("\n".join(outlines))
668  except ValueError:
669  causes.append("missing signature")
670  result[res_field + ".signature"] = result.Quote(signature)
671  if len(reflines) > 1 or signature != reflines[0]:
672  result[res_field + ".expected"] = result.Quote("\n".join(reflines))
673 
674  return causes
675 
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 761 of file GaudiTest.py.

761 def findTTreeSummaries(stdout):
762  """
763  Scan stdout to find ROOT TTree summaries and digest them.
764  """
765  stars = re.compile(r"^\*+$")
766  outlines = stdout.splitlines()
767  nlines = len(outlines)
768  trees = {}
769 
770  i = 0
771  while i < nlines: #loop over the output
772  # look for
773  while i < nlines and not stars.match(outlines[i]):
774  i += 1
775  if i < nlines:
776  tree, i = _parseTTreeSummary(outlines, i)
777  if tree:
778  trees[tree["Name"]] = tree
779 
780  return trees
781 
def _parseTTreeSummary(lines, pos)
Definition: GaudiTest.py:712
def findTTreeSummaries(stdout)
Definition: GaudiTest.py:761
def GaudiTest.getCmpFailingValues (   reference,
  to_check,
  fail_path 
)

Definition at line 813 of file GaudiTest.py.

813 def getCmpFailingValues(reference, to_check, fail_path):
814  c = to_check
815  r = reference
816  for k in fail_path:
817  c = c.get(k,None)
818  r = r.get(k,None)
819  if c is None or r is None:
820  break # one of the dictionaries is not deep enough
821  return (fail_path, r, c)
822 
823 # signature of the print-out of the histograms
def getCmpFailingValues(reference, to_check, fail_path)
Definition: GaudiTest.py:813
def GaudiTest.hexConvert (   char)

Definition at line 335 of file GaudiTest.py.

335 def hexConvert(char):
336  return hex(ord(char))
def hexConvert(char)
Definition: GaudiTest.py:335
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:339
def GaudiTest.hexreplace (   match)

Definition at line 331 of file GaudiTest.py.

331 def hexreplace( match ):
332  "Return the hex string "
333  return "".join(map(hexConvert,match.group()))
334 
def hexreplace(match)
Definition: GaudiTest.py:331
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 826 of file GaudiTest.py.

826 def parseHistosSummary(lines, pos):
827  """
828  Extract the histograms infos from the lines starting at pos.
829  Returns the position of the first line after the summary block.
830  """
831  global h_count_re
832  h_table_head = re.compile(r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"')
833  h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
834 
835  nlines = len(lines)
836 
837  # decode header
838  m = h_count_re.search(lines[pos])
839  name = m.group(1).strip()
840  total = int(m.group(2))
841  header = {}
842  for k, v in [ x.split("=") for x in m.group(3).split() ]:
843  header[k] = int(v)
844  pos += 1
845  header["Total"] = total
846 
847  summ = {}
848  while pos < nlines:
849  m = h_table_head.search(lines[pos])
850  if m:
851  t, d = m.groups(1) # type and directory
852  t = t.replace(" profile", "Prof")
853  pos += 1
854  if pos < nlines:
855  l = lines[pos]
856  else:
857  l = ""
858  cont = {}
859  if l.startswith(" | ID"):
860  # table format
861  titles = [ x.strip() for x in l.split("|")][1:]
862  pos += 1
863  while pos < nlines and lines[pos].startswith(" |"):
864  l = lines[pos]
865  values = [ x.strip() for x in l.split("|")][1:]
866  hcont = {}
867  for i in range(len(titles)):
868  hcont[titles[i]] = values[i]
869  cont[hcont["ID"]] = hcont
870  pos += 1
871  elif l.startswith(" ID="):
872  while pos < nlines and lines[pos].startswith(" ID="):
873  values = [ x.strip() for x in h_short_summ.search(lines[pos]).groups() ]
874  cont[values[0]] = values
875  pos += 1
876  else: # not interpreted
877  raise RuntimeError("Cannot understand line %d: '%s'" % (pos, l))
878  if not d in summ:
879  summ[d] = {}
880  summ[d][t] = cont
881  summ[d]["header"] = header
882  else:
883  break
884  if not summ:
885  # If the full table is not present, we use only the header
886  summ[name] = {"header": header}
887  return summ, pos
888 
def parseHistosSummary(lines, pos)
Definition: GaudiTest.py:826
NamedRange_< CONTAINER > range(const CONTAINER &cnt, std::string name)
simple function to create the named range form arbitrary container
Definition: NamedRange.h:130
def GaudiTest.rationalizepath (   p)

Definition at line 309 of file GaudiTest.py.

310  np = os.path.normpath(os.path.expandvars(p))
311  if os.path.exists(np):
312  p = os.path.realpath(np)
313  return p
314 
315 # XML Escaping character
def rationalizepath(p)
Definition: GaudiTest.py:309
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 39 of file GaudiTest.py.

39 def total_seconds_replacement(timedelta) :
40  return timedelta.days*86400 + timedelta.seconds + timedelta.microseconds/1000000
41 
42 
def total_seconds_replacement(timedelta)
Definition: GaudiTest.py:39
def GaudiTest.which (   executable)

Locates an executable in the executables path ($PATH) and returns the full path to it.

If the executable cannot be found, None is returned

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

289 def which(executable):
290  """
291  Locates an executable in the executables path ($PATH) and returns the full
292  path to it. An application is looked for with or without the '.exe' suffix.
293  If the executable cannot be found, None is returned
294  """
295  if os.path.isabs(executable):
296  if not os.path.exists(executable):
297  if executable.endswith('.exe'):
298  if os.path.exists(executable[:-4]):
299  return executable[:-4]
300  return executable
301  for d in os.environ.get("PATH").split(os.pathsep):
302  fullpath = os.path.join(d, executable)
303  if os.path.exists(fullpath):
304  return fullpath
305  if executable.endswith('.exe'):
306  return which(executable[:-4])
307  return None
308 
def which(executable)
Locates an executable in the executables path ($PATH) and returns the full path to it...
Definition: GaudiTest.py:289

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

GaudiTest._illegal_xml_chars_RE = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')
private

Definition at line 329 of file GaudiTest.py.

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

Definition at line 824 of file GaudiTest.py.

GaudiTest.lineSkipper

Definition at line 530 of file GaudiTest.py.

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

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

GaudiTest.normalizeEOL = FilePreprocessor()

Definition at line 486 of file GaudiTest.py.

tuple GaudiTest.normalizeExamples = maskPointers+normalizeDate

Definition at line 509 of file GaudiTest.py.

GaudiTest.regexps

Definition at line 587 of file GaudiTest.py.

GaudiTest.skipEmptyLines = FilePreprocessor()

Definition at line 489 of file GaudiTest.py.