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
 
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
 
def findReferenceBlock
 
def countErrorLines
 
def _parseTTreeSummary (lines, pos)
 
def findTTreeSummaries (stdout)
 
def cmpTreesDicts
 
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...
 
tuple _illegal_xml_chars_RE = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')
 
tuple maskPointers = RegexpReplacer("0x[0-9a-fA-F]{4,16}","0x########")
 
tuple normalizeDate
 
tuple normalizeEOL = FilePreprocessor()
 
tuple skipEmptyLines = FilePreprocessor()
 
 normalizeExamples = maskPointers+normalizeDate
 
tuple lineSkipper
 
tuple 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 707 of file GaudiTest.py.

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

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

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

Definition at line 884 of file GaudiTest.py.

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

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

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

Definition at line 808 of file GaudiTest.py.

808 def getCmpFailingValues(reference, to_check, fail_path):
809  c = to_check
810  r = reference
811  for k in fail_path:
812  c = c.get(k,None)
813  r = r.get(k,None)
814  if c is None or r is None:
815  break # one of the dictionaries is not deep enough
816  return (fail_path, r, c)
817 
818 # signature of the print-out of the histograms
def getCmpFailingValues(reference, to_check, fail_path)
Definition: GaudiTest.py:808
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
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 
struct GAUDI_API map
Parametrisation class for map-like implementation.
def hexreplace(match)
Definition: GaudiTest.py:331
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 821 of file GaudiTest.py.

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

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

Definition at line 5 of file GaudiTest.py.

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

Definition at line 329 of file GaudiTest.py.

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

Definition at line 819 of file GaudiTest.py.

tuple GaudiTest.lineSkipper

Definition at line 530 of file GaudiTest.py.

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

Definition at line 483 of file GaudiTest.py.

tuple 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.

tuple GaudiTest.normalizeEOL = FilePreprocessor()

Definition at line 486 of file GaudiTest.py.

tuple GaudiTest.normalizeExamples = maskPointers+normalizeDate

Definition at line 509 of file GaudiTest.py.

tuple GaudiTest.skipEmptyLines = FilePreprocessor()

Definition at line 489 of file GaudiTest.py.