Processing math: 100%
The Gaudi Framework  v36r1 (3e2fb5a8)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
BaseTest.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 
12 
13 import os
14 import sys
15 import time
16 import signal
17 import threading
18 import platform
19 import tempfile
20 import inspect
21 import re
22 import logging
23 
24 from subprocess import Popen, PIPE, STDOUT
25 
26 try:
27  from html import escape as escape_for_html
28 except ImportError: # Python2
29  from cgi import escape as escape_for_html
30 
31 import six
32 
33 if sys.version_info < (3, 5):
34  # backport of 'backslashreplace' handling of UnicodeDecodeError
35  # to Python < 3.5
36  from codecs import register_error, backslashreplace_errors
37 
39  if isinstance(exc, UnicodeDecodeError):
40  code = hex(ord(exc.object[exc.start]))
41  return (u'\\' + code[1:], exc.start + 1)
42  else:
43  return backslashreplace_errors(exc)
44 
45  register_error('backslashreplace', _new_backslashreplace_errors)
46  del register_error
47  del backslashreplace_errors
48  del _new_backslashreplace_errors
49 
50 
51 def sanitize_for_xml(data):
52  '''
53  Take a string with invalid ASCII/UTF characters and quote them so that the
54  string can be used in an XML text.
55 
56  >>> sanitize_for_xml('this is \x1b')
57  'this is [NON-XML-CHAR-0x1B]'
58  '''
59  bad_chars = re.compile(
60  u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')
61 
62  def quote(match):
63  'helper function'
64  return ''.join('[NON-XML-CHAR-0x%2X]' % ord(c) for c in match.group())
65 
66  return bad_chars.sub(quote, data)
67 
68 
69 def dumpProcs(name):
70  '''helper to debug GAUDI-1084, dump the list of processes'''
71  from getpass import getuser
72  if 'WORKSPACE' in os.environ:
73  p = Popen(['ps', '-fH', '-U', getuser()], stdout=PIPE)
74  with open(os.path.join(os.environ['WORKSPACE'], name), 'wb') as f:
75  f.write(p.communicate()[0])
76 
77 
78 def kill_tree(ppid, sig):
79  '''
80  Send a signal to a process and all its child processes (starting from the
81  leaves).
82  '''
83  log = logging.getLogger('kill_tree')
84  ps_cmd = ['ps', '--no-headers', '-o', 'pid', '--ppid', str(ppid)]
85  get_children = Popen(ps_cmd, stdout=PIPE, stderr=PIPE)
86  children = map(int, get_children.communicate()[0].split())
87  for child in children:
88  kill_tree(child, sig)
89  try:
90  log.debug('killing process %d', ppid)
91  os.kill(ppid, sig)
92  except OSError as err:
93  if err.errno != 3: # No such process
94  raise
95  log.debug('no such process %d', ppid)
96 
97 
98 # -------------------------------------------------------------------------#
99 
100 
101 class BaseTest(object):
102 
103  _common_tmpdir = None
104 
105  def __init__(self):
106  self.program = ''
107  self.args = []
108  self.reference = ''
109  self.error_reference = ''
110  self.options = ''
111  self.stderr = ''
112  self.timeout = 600
113  self.exit_code = None
114  self.environment = dict(os.environ)
116  self.signal = None
117  self.workdir = os.curdir
118  self.use_temp_dir = False
119  # Variables not for users
120  self.status = None
121  self.name = ''
122  self.causes = []
123  self.result = Result(self)
124  self.returnedCode = 0
125  self.out = ''
126  self.err = ''
127  self.proc = None
128  self.stack_trace = None
129  self.basedir = os.getcwd()
130 
131  def run(self):
132  logging.debug('running test %s', self.name)
133 
134  self.result = Result({
135  'CAUSE': None,
136  'EXCEPTION': None,
137  'RESOURCE': None,
138  'TARGET': None,
139  'TRACEBACK': None,
140  'START_TIME': None,
141  'END_TIME': None,
142  'TIMEOUT_DETAIL': None
143  })
144 
145  if self.options:
146  if re.search(
147  r'from\s+Gaudi.Configuration\s+import\s+\*|'
148  'from\s+Configurables\s+import', self.options):
149  suffix, lang = '.py', 'python'
150  else:
151  suffix, lang = '.opts', 'c++'
152  self.result[
153  "Options"] = '<code lang="{}"><pre>{}</pre></code>'.format(
154  lang, escape_for_html(self.options))
155  optionFile = tempfile.NamedTemporaryFile(suffix=suffix)
156  optionFile.file.write(self.options.encode('utf-8'))
157  optionFile.seek(0)
158  self.args.append(RationalizePath(optionFile.name))
159 
160  platform_id = (self.environment.get('BINARY_TAG')
161  or self.environment.get('CMTCONFIG')
162  or platform.platform())
163  # If at least one regex matches we skip the test.
164  skip_test = bool([
165  None for prex in self.unsupported_platforms
166  if re.search(prex, platform_id)
167  ])
168 
169  if not skip_test:
170  # handle working/temporary directory options
171  workdir = self.workdir
172  if self.use_temp_dir:
173  if self._common_tmpdir:
174  workdir = self._common_tmpdir
175  else:
176  workdir = tempfile.mkdtemp()
177 
178  # prepare the command to execute
179  prog = ''
180  if self.program != '':
181  prog = self.program
182  elif "GAUDIEXE" in self.environment:
183  prog = self.environment["GAUDIEXE"]
184  else:
185  prog = "Gaudi.exe"
186 
187  prog_ext = os.path.splitext(prog)[1]
188  if prog_ext not in [".exe", ".py", ".bat"]:
189  prog += ".exe"
190  prog_ext = ".exe"
191 
192  prog = which(prog) or prog
193 
194  args = list(map(RationalizePath, self.args))
195 
196  if prog_ext == ".py":
197  params = ['python', RationalizePath(prog)] + args
198  else:
199  params = [RationalizePath(prog)] + args
200 
201  # we need to switch directory because the validator expects to run
202  # in the same dir as the program
203  os.chdir(workdir)
204 
205  # launching test in a different thread to handle timeout exception
206  def target():
207  logging.debug('executing %r in %s', params, workdir)
208  self.proc = Popen(
209  params, stdout=PIPE, stderr=PIPE, env=self.environment)
210  logging.debug('(pid: %d)', self.proc.pid)
211  out, err = self.proc.communicate()
212  self.out = out.decode('utf-8', errors='backslashreplace')
213  self.err = err.decode('utf-8', errors='backslashreplace')
214 
215  thread = threading.Thread(target=target)
216  thread.start()
217  # catching timeout
218  thread.join(self.timeout)
219 
220  if thread.is_alive():
221  logging.debug('time out in test %s (pid %d)', self.name,
222  self.proc.pid)
223  # get the stack trace of the stuck process
224  cmd = [
225  'gdb', '--pid',
226  str(self.proc.pid), '--batch',
227  '--eval-command=thread apply all backtrace'
228  ]
229  gdb = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
230  self.stack_trace = gdb.communicate()[0].decode(
231  'utf-8', errors='backslashreplace')
232 
233  kill_tree(self.proc.pid, signal.SIGTERM)
234  thread.join(60)
235  if thread.is_alive():
236  kill_tree(self.proc.pid, signal.SIGKILL)
237  self.causes.append('timeout')
238  else:
239  logging.debug('completed test %s', self.name)
240 
241  # Getting the error code
242  logging.debug('returnedCode = %s', self.proc.returncode)
243  self.returnedCode = self.proc.returncode
244 
245  logging.debug('validating test...')
246  self.result, self.causes = self.ValidateOutput(
247  stdout=self.out, stderr=self.err, result=self.result)
248 
249  # remove the temporary directory if we created it
250  if self.use_temp_dir and not self._common_tmpdir:
251  shutil.rmtree(workdir, True)
252 
253  os.chdir(self.basedir)
254 
255  # handle application exit code
256  if self.signal is not None:
257  if int(self.returnedCode) != -int(self.signal):
258  self.causes.append('exit code')
259 
260  elif self.exit_code is not None:
261  if int(self.returnedCode) != int(self.exit_code):
262  self.causes.append('exit code')
263 
264  elif self.returnedCode != 0:
265  self.causes.append("exit code")
266 
267  if self.causes:
268  self.status = "failed"
269  else:
270  self.status = "passed"
271 
272  else:
273  self.status = "skipped"
274 
275  logging.debug('%s: %s', self.name, self.status)
276  field_mapping = {
277  'Exit Code': 'returnedCode',
278  'stderr': 'err',
279  'Arguments': 'args',
280  'Runtime Environment': 'environment',
281  'Status': 'status',
282  'stdout': 'out',
283  'Program Name': 'program',
284  'Name': 'name',
285  'Validator': 'validator',
286  'Output Reference File': 'reference',
287  'Error Reference File': 'error_reference',
288  'Causes': 'causes',
289  # 'Validator Result': 'result.annotations',
290  'Unsupported Platforms': 'unsupported_platforms',
291  'Stack Trace': 'stack_trace'
292  }
293  resultDict = [(key, getattr(self, attr))
294  for key, attr in field_mapping.items()
295  if getattr(self, attr)]
296  resultDict.append(('Working Directory',
298  os.path.join(os.getcwd(), self.workdir))))
299  # print(dict(resultDict).keys())
300  resultDict.extend(self.result.annotations.items())
301  # print(self.result.annotations.keys())
302  resultDict = dict(resultDict)
303 
304  # Special cases
305  if "Validator" in resultDict:
306  resultDict[
307  "Validator"] = '<code lang="{}"><pre>{}</pre></code>'.format(
308  "python", escape_for_html(resultDict["Validator"]))
309  return resultDict
310 
311  # -------------------------------------------------#
312  # ----------------Validating tool------------------#
313  # -------------------------------------------------#
314 
315  def ValidateOutput(self, stdout, stderr, result):
316  if not self.stderr:
317  self.validateWithReference(stdout, stderr, result, self.causes)
318  elif stderr.strip() != self.stderr.strip():
319  self.causes.append('standard error')
320  return result, self.causes
321 
323  reference=None,
324  stdout=None,
325  result=None,
326  causes=None,
327  signature_offset=0,
328  signature=None,
329  id=None):
330  """
331  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.
332  """
333 
334  if reference is None:
335  reference = self.reference
336  if stdout is None:
337  stdout = self.out
338  if result is None:
339  result = self.result
340  if causes is None:
341  causes = self.causes
342 
343  reflines = list(
344  filter(None, map(lambda s: s.rstrip(), reference.splitlines())))
345  if not reflines:
346  raise RuntimeError("Empty (or null) reference")
347  # the same on standard output
348  outlines = list(
349  filter(None, map(lambda s: s.rstrip(), stdout.splitlines())))
350 
351  res_field = "GaudiTest.RefBlock"
352  if id:
353  res_field += "_%s" % id
354 
355  if signature is None:
356  if signature_offset < 0:
357  signature_offset = len(reference) + signature_offset
358  signature = reflines[signature_offset]
359  # find the reference block in the output file
360  try:
361  pos = outlines.index(signature)
362  outlines = outlines[pos - signature_offset:pos + len(reflines) -
363  signature_offset]
364  if reflines != outlines:
365  msg = "standard output"
366  # I do not want 2 messages in causes if the function is called
367  # twice
368  if not msg in causes:
369  causes.append(msg)
370  result[res_field + ".observed"] = result.Quote(
371  "\n".join(outlines))
372  except ValueError:
373  causes.append("missing signature")
374  result[res_field + ".signature"] = result.Quote(signature)
375  if len(reflines) > 1 or signature != reflines[0]:
376  result[res_field + ".expected"] = result.Quote("\n".join(reflines))
377  return causes
378 
379  def countErrorLines(self,
380  expected={
381  'ERROR': 0,
382  'FATAL': 0
383  },
384  stdout=None,
385  result=None,
386  causes=None):
387  """
388  Count the number of messages with required severity (by default ERROR and FATAL)
389  and check if their numbers match the expected ones (0 by default).
390  The dictionary "expected" can be used to tune the number of errors and fatals
391  allowed, or to limit the number of expected warnings etc.
392  """
393 
394  if stdout is None:
395  stdout = self.out
396  if result is None:
397  result = self.result
398  if causes is None:
399  causes = self.causes
400 
401  # prepare the dictionary to record the extracted lines
402  errors = {}
403  for sev in expected:
404  errors[sev] = []
405 
406  outlines = stdout.splitlines()
407  from math import log10
408  fmt = "%%%dd - %%s" % (int(log10(len(outlines) + 1)))
409 
410  linecount = 0
411  for l in outlines:
412  linecount += 1
413  words = l.split()
414  if len(words) >= 2 and words[1] in errors:
415  errors[words[1]].append(fmt % (linecount, l.rstrip()))
416 
417  for e in errors:
418  if len(errors[e]) != expected[e]:
419  causes.append('%s(%d)' % (e, len(errors[e])))
420  result["GaudiTest.lines.%s" % e] = result.Quote('\n'.join(
421  errors[e]))
422  result["GaudiTest.lines.%s.expected#" % e] = result.Quote(
423  str(expected[e]))
424 
425  return causes
426 
428  stdout=None,
429  result=None,
430  causes=None,
431  trees_dict=None,
432  ignore=r"Basket|.*size|Compression"):
433  """
434  Compare the TTree summaries in stdout with the ones in trees_dict or in
435  the reference file. By default ignore the size, compression and basket
436  fields.
437  The presence of TTree summaries when none is expected is not a failure.
438  """
439  if stdout is None:
440  stdout = self.out
441  if result is None:
442  result = self.result
443  if causes is None:
444  causes = self.causes
445  if trees_dict is None:
446  lreference = self._expandReferenceFileName(self.reference)
447  # call the validator if the file exists
448  if lreference and os.path.isfile(lreference):
449  trees_dict = findTTreeSummaries(open(lreference).read())
450  else:
451  trees_dict = {}
452 
453  from pprint import PrettyPrinter
454  pp = PrettyPrinter()
455  if trees_dict:
456  result["GaudiTest.TTrees.expected"] = result.Quote(
457  pp.pformat(trees_dict))
458  if ignore:
459  result["GaudiTest.TTrees.ignore"] = result.Quote(ignore)
460 
461  trees = findTTreeSummaries(stdout)
462  failed = cmpTreesDicts(trees_dict, trees, ignore)
463  if failed:
464  causes.append("trees summaries")
465  msg = "%s: %s != %s" % getCmpFailingValues(trees_dict, trees,
466  failed)
467  result["GaudiTest.TTrees.failure_on"] = result.Quote(msg)
468  result["GaudiTest.TTrees.found"] = result.Quote(pp.pformat(trees))
469 
470  return causes
471 
473  stdout=None,
474  result=None,
475  causes=None,
476  dict=None,
477  ignore=None):
478  """
479  Compare the TTree summaries in stdout with the ones in trees_dict or in
480  the reference file. By default ignore the size, compression and basket
481  fields.
482  The presence of TTree summaries when none is expected is not a failure.
483  """
484  if stdout is None:
485  stdout = self.out
486  if result is None:
487  result = self.result
488  if causes is None:
489  causes = self.causes
490 
491  if dict is None:
492  lreference = self._expandReferenceFileName(self.reference)
493  # call the validator if the file exists
494  if lreference and os.path.isfile(lreference):
495  dict = findHistosSummaries(open(lreference).read())
496  else:
497  dict = {}
498 
499  from pprint import PrettyPrinter
500  pp = PrettyPrinter()
501  if dict:
502  result["GaudiTest.Histos.expected"] = result.Quote(
503  pp.pformat(dict))
504  if ignore:
505  result["GaudiTest.Histos.ignore"] = result.Quote(ignore)
506 
507  histos = findHistosSummaries(stdout)
508  failed = cmpTreesDicts(dict, histos, ignore)
509  if failed:
510  causes.append("histos summaries")
511  msg = "%s: %s != %s" % getCmpFailingValues(dict, histos, failed)
512  result["GaudiTest.Histos.failure_on"] = result.Quote(msg)
513  result["GaudiTest.Histos.found"] = result.Quote(pp.pformat(histos))
514 
515  return causes
516 
518  stdout=None,
519  stderr=None,
520  result=None,
521  causes=None,
522  preproc=None):
523  '''
524  Default validation acti*on: compare standard output and error to the
525  reference files.
526  '''
527 
528  if stdout is None:
529  stdout = self.out
530  if stderr is None:
531  stderr = self.err
532  if result is None:
533  result = self.result
534  if causes is None:
535  causes = self.causes
536 
537  # set the default output preprocessor
538  if preproc is None:
539  preproc = normalizeExamples
540  # check standard output
541  lreference = self._expandReferenceFileName(self.reference)
542  # call the validator if the file exists
543  if lreference and os.path.isfile(lreference):
544  causes += ReferenceFileValidator(
545  lreference, "standard output", "Output Diff",
546  preproc=preproc)(stdout, result)
547  elif lreference:
548  causes += ["missing reference file"]
549  # Compare TTree summaries
550  causes = self.CheckTTreesSummaries(stdout, result, causes)
551  causes = self.CheckHistosSummaries(stdout, result, causes)
552  if causes and lreference: # Write a new reference file for stdout
553  try:
554  cnt = 0
555  newrefname = '.'.join([lreference, 'new'])
556  while os.path.exists(newrefname):
557  cnt += 1
558  newrefname = '.'.join([lreference, '~%d~' % cnt, 'new'])
559  newref = open(newrefname, "w")
560  # sanitize newlines
561  for l in stdout.splitlines():
562  newref.write(l.rstrip() + '\n')
563  del newref # flush and close
564  result['New Output Reference File'] = os.path.relpath(
565  newrefname, self.basedir)
566  except IOError:
567  # Ignore IO errors when trying to update reference files
568  # because we may be in a read-only filesystem
569  pass
570 
571  # check standard error
572  lreference = self._expandReferenceFileName(self.error_reference)
573  # call the validator if we have a file to use
574  if lreference:
575  if os.path.isfile(lreference):
576  newcauses = ReferenceFileValidator(
577  lreference,
578  "standard error",
579  "Error Diff",
580  preproc=preproc)(stderr, result)
581  else:
582  newcauses = ["missing error reference file"]
583  causes += newcauses
584  if newcauses and lreference: # Write a new reference file for stdedd
585  cnt = 0
586  newrefname = '.'.join([lreference, 'new'])
587  while os.path.exists(newrefname):
588  cnt += 1
589  newrefname = '.'.join([lreference, '~%d~' % cnt, 'new'])
590  newref = open(newrefname, "w")
591  # sanitize newlines
592  for l in stderr.splitlines():
593  newref.write(l.rstrip() + '\n')
594  del newref # flush and close
595  result['New Error Reference File'] = os.path.relpath(
596  newrefname, self.basedir)
597  else:
598  causes += BasicOutputValidator(lreference, "standard error",
599  "ExecTest.expected_stderr")(stderr,
600  result)
601  return causes
602 
603  def _expandReferenceFileName(self, reffile):
604  # if no file is passed, do nothing
605  if not reffile:
606  return ""
607 
608  # function to split an extension in constituents parts
609  def platformSplit(p):
610  import re
611  delim = re.compile('-' in p and r"[-+]" or r"_")
612  return set(delim.split(p))
613 
614  reference = os.path.normpath(
615  os.path.join(self.basedir, os.path.expandvars(reffile)))
616 
617  # old-style platform-specific reference name
618  spec_ref = reference[:-3] + GetPlatform(self)[0:3] + reference[-3:]
619  if os.path.isfile(spec_ref):
620  reference = spec_ref
621  else: # look for new-style platform specific reference files:
622  # get all the files whose name start with the reference filename
623  dirname, basename = os.path.split(reference)
624  if not dirname:
625  dirname = '.'
626  head = basename + "."
627  head_len = len(head)
628  platform = platformSplit(GetPlatform(self))
629  if 'do0' in platform:
630  platform.add('dbg')
631  candidates = []
632  for f in os.listdir(dirname):
633  if f.startswith(head):
634  req_plat = platformSplit(f[head_len:])
635  if platform.issuperset(req_plat):
636  candidates.append((len(req_plat), f))
637  if candidates: # take the one with highest matching
638  # FIXME: it is not possible to say if x86_64-slc5-gcc43-dbg
639  # has to use ref.x86_64-gcc43 or ref.slc5-dbg
640  candidates.sort()
641  reference = os.path.join(dirname, candidates[-1][1])
642  return reference
643 
644 
645 # ======= GAUDI TOOLS =======
646 
647 import shutil
648 import string
649 import difflib
650 import calendar
651 
652 try:
653  from GaudiKernel import ROOT6WorkAroundEnabled
654 except ImportError:
655 
657  # dummy implementation
658  return False
659 
660 
661 # --------------------------------- TOOLS ---------------------------------#
662 
663 
665  """
666  Function used to normalize the used path
667  """
668  newPath = os.path.normpath(os.path.expandvars(p))
669  if os.path.exists(newPath):
670  p = os.path.realpath(newPath)
671  return p
672 
673 
674 def which(executable):
675  """
676  Locates an executable in the executables path ($PATH) and returns the full
677  path to it. An application is looked for with or without the '.exe' suffix.
678  If the executable cannot be found, None is returned
679  """
680  if os.path.isabs(executable):
681  if not os.path.isfile(executable):
682  if executable.endswith('.exe'):
683  if os.path.isfile(executable[:-4]):
684  return executable[:-4]
685  else:
686  executable = os.path.split(executable)[1]
687  else:
688  return executable
689  for d in os.environ.get("PATH").split(os.pathsep):
690  fullpath = os.path.join(d, executable)
691  if os.path.isfile(fullpath):
692  return fullpath
693  elif executable.endswith('.exe') and os.path.isfile(fullpath[:-4]):
694  return fullpath[:-4]
695  return None
696 
697 
698 # -------------------------------------------------------------------------#
699 # ----------------------------- Result Classe -----------------------------#
700 # -------------------------------------------------------------------------#
701 import types
702 
703 
704 class Result:
705 
706  PASS = 'PASS'
707  FAIL = 'FAIL'
708  ERROR = 'ERROR'
709  UNTESTED = 'UNTESTED'
710 
711  EXCEPTION = ""
712  RESOURCE = ""
713  TARGET = ""
714  TRACEBACK = ""
715  START_TIME = ""
716  END_TIME = ""
717  TIMEOUT_DETAIL = ""
718 
719  def __init__(self, kind=None, id=None, outcome=PASS, annotations={}):
720  self.annotations = annotations.copy()
721 
722  def __getitem__(self, key):
723  assert isinstance(key, six.string_types)
724  return self.annotations[key]
725 
726  def __setitem__(self, key, value):
727  assert isinstance(key, six.string_types)
728  assert isinstance(
729  value, six.string_types), '{!r} is not a string'.format(value)
730  self.annotations[key] = value
731 
732  def Quote(self, text):
733  """
734  Convert text to html by escaping special chars and adding <pre> tags.
735  """
736  return "<pre>{}</pre>".format(escape_for_html(text))
737 
738 
739 # -------------------------------------------------------------------------#
740 # --------------------------- Validator Classes ---------------------------#
741 # -------------------------------------------------------------------------#
742 
743 # Basic implementation of an option validator for Gaudi test. This
744 # implementation is based on the standard (LCG) validation functions used
745 # in QMTest.
746 
747 
749  def __init__(self, ref, cause, result_key):
750  self.ref = ref
751  self.cause = cause
752  self.result_key = result_key
753 
754  def __call__(self, out, result):
755  """Validate the output of the program.
756  'stdout' -- A string containing the data written to the standard output
757  stream.
758  'stderr' -- A string containing the data written to the standard error
759  stream.
760  'result' -- A 'Result' object. It may be used to annotate
761  the outcome according to the content of stderr.
762  returns -- A list of strings giving causes of failure."""
763 
764  causes = []
765  # Check the output
766  if not self.__CompareText(out, self.ref):
767  causes.append(self.cause)
768  result[self.result_key] = result.Quote(self.ref)
769 
770  return causes
771 
772  def __CompareText(self, s1, s2):
773  """Compare 's1' and 's2', ignoring line endings.
774  's1' -- A string.
775  's2' -- A string.
776  returns -- True if 's1' and 's2' are the same, ignoring
777  differences in line endings."""
778  if ROOT6WorkAroundEnabled('ReadRootmapCheck'):
779  # FIXME: (MCl) Hide warnings from new rootmap sanity check until we
780  # can fix them
781  to_ignore = re.compile(
782  r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*'
783  )
784 
785  def keep_line(l):
786  return not to_ignore.match(l)
787 
788  return list(filter(keep_line, s1.splitlines())) == list(
789  filter(keep_line, s2.splitlines()))
790  else:
791  return s1.splitlines() == s2.splitlines()
792 
793 
794 # ------------------------ Preprocessor elements ------------------------#
796  """ Base class for a callable that takes a file and returns a modified
797  version of it."""
798 
799  def __processLine__(self, line):
800  return line
801 
802  def __processFile__(self, lines):
803  output = []
804  for l in lines:
805  l = self.__processLine__(l)
806  if l:
807  output.append(l)
808  return output
809 
810  def __call__(self, input):
811  if not isinstance(input, six.string_types):
812  lines = input
813  mergeback = False
814  else:
815  lines = input.splitlines()
816  mergeback = True
817  output = self.__processFile__(lines)
818  if mergeback:
819  output = '\n'.join(output)
820  return output
821 
822  def __add__(self, rhs):
823  return FilePreprocessorSequence([self, rhs])
824 
825 
827  def __init__(self, members=[]):
828  self.members = members
829 
830  def __add__(self, rhs):
831  return FilePreprocessorSequence(self.members + [rhs])
832 
833  def __call__(self, input):
834  output = input
835  for pp in self.members:
836  output = pp(output)
837  return output
838 
839 
841  def __init__(self, strings=[], regexps=[]):
842  import re
843  self.strings = strings
844  self.regexps = list(map(re.compile, regexps))
845 
846  def __processLine__(self, line):
847  for s in self.strings:
848  if line.find(s) >= 0:
849  return None
850  for r in self.regexps:
851  if r.search(line):
852  return None
853  return line
854 
855 
857  def __init__(self, start, end):
858  self.start = start
859  self.end = end
860  self._skipping = False
861 
862  def __processLine__(self, line):
863  if self.start in line:
864  self._skipping = True
865  return None
866  elif self.end in line:
867  self._skipping = False
868  elif self._skipping:
869  return None
870  return line
871 
872 
874  def __init__(self, orig, repl="", when=None):
875  if when:
876  when = re.compile(when)
877  self._operations = [(when, re.compile(orig), repl)]
878 
879  def __add__(self, rhs):
880  if isinstance(rhs, RegexpReplacer):
881  res = RegexpReplacer("", "", None)
882  res._operations = self._operations + rhs._operations
883  else:
884  res = FilePreprocessor.__add__(self, rhs)
885  return res
886 
887  def __processLine__(self, line):
888  for w, o, r in self._operations:
889  if w is None or w.search(line):
890  line = o.sub(r, line)
891  return line
892 
893 
894 # Common preprocessors
895 maskPointers = RegexpReplacer("0x[0-9a-fA-F]{4,16}", "0x########")
896 normalizeDate = RegexpReplacer(
897  "[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9] [0-9]{4}[-/][01][0-9][-/][0-3][0-9][ A-Z]*",
898  "00:00:00 1970-01-01")
899 normalizeEOL = FilePreprocessor()
900 normalizeEOL.__processLine__ = lambda line: str(line).rstrip() + '\n'
901 
902 skipEmptyLines = FilePreprocessor()
903 # FIXME: that's ugly
904 skipEmptyLines.__processLine__ = lambda line: (line.strip() and line) or None
905 
906 # Special preprocessor sorting the list of strings (whitespace separated)
907 # that follow a signature on a single line
908 
909 
911  def __init__(self, signature):
912  self.signature = signature
913  self.siglen = len(signature)
914 
915  def __processLine__(self, line):
916  pos = line.find(self.signature)
917  if pos >= 0:
918  line = line[:(pos + self.siglen)]
919  lst = line[(pos + self.siglen):].split()
920  lst.sort()
921  line += " ".join(lst)
922  return line
923 
924 
926  '''
927  Sort group of lines matching a regular expression
928  '''
929 
930  def __init__(self, exp):
931  self.exp = exp if hasattr(exp, 'match') else re.compile(exp)
932 
933  def __processFile__(self, lines):
934  match = self.exp.match
935  output = []
936  group = []
937  for l in lines:
938  if match(l):
939  group.append(l)
940  else:
941  if group:
942  group.sort()
943  output.extend(group)
944  group = []
945  output.append(l)
946  return output
947 
948 
949 # Preprocessors for GaudiExamples
950 normalizeExamples = maskPointers + normalizeDate
951 for w, o, r in [
952  # ("TIMER.TIMER",r"[0-9]", "0"), # Normalize time output
953  ("TIMER.TIMER", r"\s+[+-]?[0-9]+[0-9.]*", " 0"), # Normalize time output
954  ("release all pending", r"^.*/([^/]*:.*)", r"\1"),
955  ("^#.*file", r"file '.*[/\\]([^/\\]*)$", r"file '\1"),
956  ("^JobOptionsSvc.*options successfully read in from",
957  r"read in from .*[/\\]([^/\\]*)$",
958  r"file \1"), # normalize path to options
959  # Normalize UUID, except those ending with all 0s (i.e. the class IDs)
960  (None,
961  r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}(?!-0{12})-[0-9A-Fa-f]{12}",
962  "00000000-0000-0000-0000-000000000000"),
963  # Absorb a change in ServiceLocatorHelper
964  ("ServiceLocatorHelper::", "ServiceLocatorHelper::(create|locate)Service",
965  "ServiceLocatorHelper::service"),
966  # Remove the leading 0 in Windows' exponential format
967  (None, r"e([-+])0([0-9][0-9])", r"e\1\2"),
968  # Output line changed in Gaudi v24
969  (None, r'Service reference count check:',
970  r'Looping over all active services...'),
971  # Ignore count of declared properties (anyway they are all printed)
972  (None,
973  r"^(.*(DEBUG|SUCCESS) List of ALL properties of .*#properties = )\d+",
974  r"\1NN"),
975  ('ApplicationMgr', r'(declareMultiSvcType|addMultiSvc): ', ''),
976  (r"Property 'Name': Value
"
, r"( = '[^']+':)'(.*)'", r'\1\2'),
977  ('TimelineSvc', "to file 'TimelineFile':", "to file "),
978  ('DataObjectHandleBase', r'DataObjectHandleBase\‍("([^"]*)"\‍)', r"'\1'"),
979 ]: # [ ("TIMER.TIMER","[0-9]+[0-9.]*", "") ]
980  normalizeExamples += RegexpReplacer(o, r, w)
981 
982 lineSkipper = LineSkipper(
983  [
984  "//GP:",
985  "JobOptionsSvc INFO # ",
986  "JobOptionsSvc WARNING # ",
987  "Time User",
988  "Welcome to",
989  "This machine has a speed",
990  "TIME:",
991  "running on",
992  "ToolSvc.Sequenc... INFO",
993  "DataListenerSvc INFO XML written to file:",
994  "[INFO]",
995  "[WARNING]",
996  "DEBUG No writable file catalog found which contains FID:",
997  "DEBUG Service base class initialized successfully",
998  # changed between v20 and v21
999  "DEBUG Incident timing:",
1000  # introduced with patch #3487
1001  # changed the level of the message from INFO to
1002  # DEBUG
1003  "INFO 'CnvServices':[",
1004  # message removed because could be printed in constructor
1005  "DEBUG 'CnvServices':[",
1006  # The signal handler complains about SIGXCPU not
1007  # defined on some platforms
1008  'SIGXCPU',
1009  # Message removed with redesing of JobOptionsSvc
1010  'ServiceLocatorHelper::service: found service JobOptionsSvc',
1011  # Ignore warnings for properties case mismatch
1012  'mismatching case for property name:',
1013  # Message demoted to DEBUG in gaudi/Gaudi!992
1014  'Histograms saving not required.',
1015  # Message added in gaudi/Gaudi!577
1016  'Properties are dumped into',
1017  ],
1018  regexps=[
1019  r"^JobOptionsSvc INFO *$",
1020  r"^# ", # Ignore python comments
1021  # skip the message reporting the version of the root file
1022  r"(Always|SUCCESS)\s*(Root f|[^ ]* F)ile version:",
1023  r"File '.*.xml' does not exist",
1024  r"INFO Refer to dataset .* by its file ID:",
1025  r"INFO Referring to dataset .* by its file ID:",
1026  r"INFO Disconnect from dataset",
1027  r"INFO Disconnected from dataset",
1028  r"INFO Disconnected data IO:",
1029  r"IncidentSvc\s*(DEBUG (Adding|Removing)|VERBOSE Calling)",
1030  # Ignore StatusCodeSvc related messages
1031  r".*StatusCodeSvc.*",
1032  r"Num\s*\|\s*Function\s*\|\s*Source Library",
1033  r"^[-+]*\s*$",
1034  # Hide the fake error message coming from POOL/ROOT (ROOT 5.21)
1035  r"ERROR Failed to modify file: .* Errno=2 No such file or directory",
1036  # Hide unchecked StatusCodes from dictionaries
1037  r"^ +[0-9]+ \|.*ROOT",
1038  r"^ +[0-9]+ \|.*\|.*Dict",
1039  # Hide EventLoopMgr total timing report
1040  r"EventLoopMgr.*---> Loop Finished",
1041  r"HiveSlimEventLo.*---> Loop Finished",
1042  # Remove ROOT TTree summary table, which changes from one version to the
1043  # other
1044  r"^\*.*\*$",
1045  # Remove Histos Summaries
1046  r"SUCCESS\s*Booked \d+ Histogram\‍(s\‍)",
1047  r"^ \|",
1048  r"^ ID=",
1049  # Ignore added/removed properties
1050  r"Property(.*)'Audit(Algorithm|Tool|Service)s':",
1051  r"Property(.*)'Audit(Begin|End)Run':",
1052  # these were missing in tools
1053  r"Property(.*)'AuditRe(start|initialize)':",
1054  r"Property(.*)'Blocking':",
1055  # removed with gaudi/Gaudi!273
1056  r"Property(.*)'ErrorCount(er)?':",
1057  # added with gaudi/Gaudi!306
1058  r"Property(.*)'Sequential':",
1059  # added with gaudi/Gaudi!314
1060  r"Property(.*)'FilterCircularDependencies':",
1061  # removed with gaudi/Gaudi!316
1062  r"Property(.*)'IsClonable':",
1063  # ignore uninteresting/obsolete messages
1064  r"Property update for OutputLevel : new value =",
1065  r"EventLoopMgr\s*DEBUG Creating OutputStream",
1066  ])
1067 
1068 if ROOT6WorkAroundEnabled('ReadRootmapCheck'):
1069  # FIXME: (MCl) Hide warnings from new rootmap sanity check until we can
1070  # fix them
1071  lineSkipper += LineSkipper(regexps=[
1072  r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*',
1073  ])
1074 
1075 normalizeExamples = (
1076  lineSkipper + normalizeExamples + skipEmptyLines + normalizeEOL +
1077  LineSorter("Services to release : ") +
1078  SortGroupOfLines(r'^\S+\s+(DEBUG|SUCCESS) Property \[\'Name\':'))
1079 
1080 # --------------------- Validation functions/classes ---------------------#
1081 
1082 
1084  def __init__(self, reffile, cause, result_key, preproc=normalizeExamples):
1085  self.reffile = os.path.expandvars(reffile)
1086  self.cause = cause
1087  self.result_key = result_key
1088  self.preproc = preproc
1089 
1090  def __call__(self, stdout, result):
1091  causes = []
1092  if os.path.isfile(self.reffile):
1093  orig = open(self.reffile).readlines()
1094  if self.preproc:
1095  orig = self.preproc(orig)
1096  result[self.result_key + '.preproc.orig'] = \
1097  result.Quote('\n'.join(map(str.strip, orig)))
1098  else:
1099  orig = []
1100  new = stdout.splitlines()
1101  if self.preproc:
1102  new = self.preproc(new)
1103 
1104  diffs = difflib.ndiff(orig, new, charjunk=difflib.IS_CHARACTER_JUNK)
1105  filterdiffs = list(
1106  map(lambda x: x.strip(), filter(lambda x: x[0] != " ", diffs)))
1107  if filterdiffs:
1108  result[self.result_key] = result.Quote("\n".join(filterdiffs))
1109  result[self.result_key] += result.Quote("""
1110  Legend:
1111  -) reference file
1112  +) standard output of the test""")
1113  result[self.result_key + '.preproc.new'] = \
1114  result.Quote('\n'.join(map(str.strip, new)))
1115  causes.append(self.cause)
1116  return causes
1117 
1118 
1120  """
1121  Scan stdout to find ROOT TTree summaries and digest them.
1122  """
1123  stars = re.compile(r"^\*+$")
1124  outlines = stdout.splitlines()
1125  nlines = len(outlines)
1126  trees = {}
1127 
1128  i = 0
1129  while i < nlines: # loop over the output
1130  # look for
1131  while i < nlines and not stars.match(outlines[i]):
1132  i += 1
1133  if i < nlines:
1134  tree, i = _parseTTreeSummary(outlines, i)
1135  if tree:
1136  trees[tree["Name"]] = tree
1137 
1138  return trees
1139 
1140 
1141 def cmpTreesDicts(reference, to_check, ignore=None):
1142  """
1143  Check that all the keys in reference are in to_check too, with the same value.
1144  If the value is a dict, the function is called recursively. to_check can
1145  contain more keys than reference, that will not be tested.
1146  The function returns at the first difference found.
1147  """
1148  fail_keys = []
1149  # filter the keys in the reference dictionary
1150  if ignore:
1151  ignore_re = re.compile(ignore)
1152  keys = [key for key in reference if not ignore_re.match(key)]
1153  else:
1154  keys = reference.keys()
1155  # loop over the keys (not ignored) in the reference dictionary
1156  for k in keys:
1157  if k in to_check: # the key must be in the dictionary to_check
1158  if (type(reference[k]) is dict) and (type(to_check[k]) is dict):
1159  # if both reference and to_check values are dictionaries,
1160  # recurse
1161  failed = fail_keys = cmpTreesDicts(reference[k], to_check[k],
1162  ignore)
1163  else:
1164  # compare the two values
1165  failed = to_check[k] != reference[k]
1166  else: # handle missing keys in the dictionary to check (i.e. failure)
1167  to_check[k] = None
1168  failed = True
1169  if failed:
1170  fail_keys.insert(0, k)
1171  break # exit from the loop at the first failure
1172  return fail_keys # return the list of keys bringing to the different values
1173 
1174 
1175 def getCmpFailingValues(reference, to_check, fail_path):
1176  c = to_check
1177  r = reference
1178  for k in fail_path:
1179  c = c.get(k, None)
1180  r = r.get(k, None)
1181  if c is None or r is None:
1182  break # one of the dictionaries is not deep enough
1183  return (fail_path, r, c)
1184 
1185 
1186 # signature of the print-out of the histograms
1187 h_count_re = re.compile(
1188  r"^(.*)SUCCESS\s+Booked (\d+) Histogram\‍(s\‍) :\s+([\s\w=-]*)")
1189 
1190 
1191 def _parseTTreeSummary(lines, pos):
1192  """
1193  Parse the TTree summary table in lines, starting from pos.
1194  Returns a tuple with the dictionary with the digested informations and the
1195  position of the first line after the summary.
1196  """
1197  result = {}
1198  i = pos + 1 # first line is a sequence of '*'
1199  count = len(lines)
1200 
1201  def splitcols(l):
1202  return [f.strip() for f in l.strip("*\n").split(':', 2)]
1203 
1204  def parseblock(ll):
1205  r = {}
1206  cols = splitcols(ll[0])
1207  r["Name"], r["Title"] = cols[1:]
1208 
1209  cols = splitcols(ll[1])
1210  r["Entries"] = int(cols[1])
1211 
1212  sizes = cols[2].split()
1213  r["Total size"] = int(sizes[2])
1214  if sizes[-1] == "memory":
1215  r["File size"] = 0
1216  else:
1217  r["File size"] = int(sizes[-1])
1218 
1219  cols = splitcols(ll[2])
1220  sizes = cols[2].split()
1221  if cols[0] == "Baskets":
1222  r["Baskets"] = int(cols[1])
1223  r["Basket size"] = int(sizes[2])
1224  r["Compression"] = float(sizes[-1])
1225  return r
1226 
1227  if i < (count - 3) and lines[i].startswith("*Tree"):
1228  result = parseblock(lines[i:i + 3])
1229  result["Branches"] = {}
1230  i += 4
1231  while i < (count - 3) and lines[i].startswith("*Br"):
1232  if i < (count - 2) and lines[i].startswith("*Branch "):
1233  # skip branch header
1234  i += 3
1235  continue
1236  branch = parseblock(lines[i:i + 3])
1237  result["Branches"][branch["Name"]] = branch
1238  i += 4
1239 
1240  return (result, i)
1241 
1242 
1243 def parseHistosSummary(lines, pos):
1244  """
1245  Extract the histograms infos from the lines starting at pos.
1246  Returns the position of the first line after the summary block.
1247  """
1248  global h_count_re
1249  h_table_head = re.compile(
1250  r'SUCCESS\s+(1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"'
1251  )
1252  h_short_summ = re.compile(r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
1253 
1254  nlines = len(lines)
1255 
1256  # decode header
1257  m = h_count_re.search(lines[pos])
1258  name = m.group(1).strip()
1259  total = int(m.group(2))
1260  header = {}
1261  for k, v in [x.split("=") for x in m.group(3).split()]:
1262  header[k] = int(v)
1263  pos += 1
1264  header["Total"] = total
1265 
1266  summ = {}
1267  while pos < nlines:
1268  m = h_table_head.search(lines[pos])
1269  if m:
1270  t, d = m.groups(1) # type and directory
1271  t = t.replace(" profile", "Prof")
1272  pos += 1
1273  if pos < nlines:
1274  l = lines[pos]
1275  else:
1276  l = ""
1277  cont = {}
1278  if l.startswith(" | ID"):
1279  # table format
1280  titles = [x.strip() for x in l.split("|")][1:]
1281  pos += 1
1282  while pos < nlines and lines[pos].startswith(" |"):
1283  l = lines[pos]
1284  values = [x.strip() for x in l.split("|")][1:]
1285  hcont = {}
1286  for i in range(len(titles)):
1287  hcont[titles[i]] = values[i]
1288  cont[hcont["ID"]] = hcont
1289  pos += 1
1290  elif l.startswith(" ID="):
1291  while pos < nlines and lines[pos].startswith(" ID="):
1292  values = [
1293  x.strip()
1294  for x in h_short_summ.search(lines[pos]).groups()
1295  ]
1296  cont[values[0]] = values
1297  pos += 1
1298  else: # not interpreted
1299  raise RuntimeError(
1300  "Cannot understand line %d: '%s'" % (pos, l))
1301  if not d in summ:
1302  summ[d] = {}
1303  summ[d][t] = cont
1304  summ[d]["header"] = header
1305  else:
1306  break
1307  if not summ:
1308  # If the full table is not present, we use only the header
1309  summ[name] = {"header": header}
1310  return summ, pos
1311 
1312 
1314  """
1315  Scan stdout to find ROOT TTree summaries and digest them.
1316  """
1317  outlines = stdout.splitlines()
1318  nlines = len(outlines) - 1
1319  summaries = {}
1320  global h_count_re
1321 
1322  pos = 0
1323  while pos < nlines:
1324  summ = {}
1325  # find first line of block:
1326  match = h_count_re.search(outlines[pos])
1327  while pos < nlines and not match:
1328  pos += 1
1329  match = h_count_re.search(outlines[pos])
1330  if match:
1331  summ, pos = parseHistosSummary(outlines, pos)
1332  summaries.update(summ)
1333  return summaries
1334 
1335 
1336 def GetPlatform(self):
1337  """
1338  Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.
1339  """
1340  arch = "None"
1341  # check architecture name
1342  if "BINARY_TAG" in os.environ:
1343  arch = os.environ["BINARY_TAG"]
1344  elif "CMTCONFIG" in os.environ:
1345  arch = os.environ["CMTCONFIG"]
1346  elif "SCRAM_ARCH" in os.environ:
1347  arch = os.environ["SCRAM_ARCH"]
1348  elif os.environ.get("ENV_CMAKE_BUILD_TYPE", "") in ("Debug", "FastDebug",
1349  "Developer"):
1350  arch = "dummy-dbg"
1351  elif os.environ.get("ENV_CMAKE_BUILD_TYPE",
1352  "") in ("Release", "MinSizeRel", "RelWithDebInfo",
1353  ""): # RelWithDebInfo == -O2 -g -DNDEBUG
1354  arch = "dummy-opt"
1355  return arch
1356 
1357 
1358 def isWinPlatform(self):
1359  """
1360  Return True if the current platform is Windows.
1361 
1362  This function was needed because of the change in the CMTCONFIG format,
1363  from win32_vc71_dbg to i686-winxp-vc9-dbg.
1364  """
1365  platform = GetPlatform(self)
1366  return "winxp" in platform or platform.startswith("win")
GaudiTesting.BaseTest.ReferenceFileValidator.reffile
reffile
Definition: BaseTest.py:1085
GaudiTesting.BaseTest.BaseTest.causes
causes
Definition: BaseTest.py:122
GaudiTesting.BaseTest.SortGroupOfLines.__init__
def __init__(self, exp)
Definition: BaseTest.py:930
GaudiTesting.BaseTest.BaseTest.options
options
Definition: BaseTest.py:110
GaudiTesting.BaseTest.FilePreprocessor
Definition: BaseTest.py:795
MSG::hex
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:282
GaudiTesting.BaseTest.Result.__getitem__
def __getitem__(self, key)
Definition: BaseTest.py:722
GaudiTesting.BaseTest.BasicOutputValidator.ref
ref
Definition: BaseTest.py:750
GaudiTesting.BaseTest.dumpProcs
def dumpProcs(name)
Definition: BaseTest.py:69
GaudiTesting.BaseTest.LineSorter.siglen
siglen
Definition: BaseTest.py:913
GaudiTesting.BaseTest.FilePreprocessor.__call__
def __call__(self, input)
Definition: BaseTest.py:810
GaudiTesting.BaseTest.LineSorter
Definition: BaseTest.py:910
GaudiTesting.BaseTest._parseTTreeSummary
def _parseTTreeSummary(lines, pos)
Definition: BaseTest.py:1191
GaudiTesting.BaseTest.LineSorter.__processLine__
def __processLine__(self, line)
Definition: BaseTest.py:915
GaudiTesting.BaseTest.BaseTest.out
out
Definition: BaseTest.py:125
GaudiTesting.BaseTest.BaseTest.CheckHistosSummaries
def CheckHistosSummaries(self, stdout=None, result=None, causes=None, dict=None, ignore=None)
Definition: BaseTest.py:472
GaudiTesting.BaseTest.sanitize_for_xml
def sanitize_for_xml(data)
Definition: BaseTest.py:51
GaudiTesting.BaseTest.BaseTest._common_tmpdir
_common_tmpdir
Definition: BaseTest.py:103
GaudiTesting.BaseTest.BaseTest.reference
reference
Definition: BaseTest.py:108
GaudiTesting.BaseTest.BasicOutputValidator.__init__
def __init__(self, ref, cause, result_key)
Definition: BaseTest.py:749
GaudiTesting.BaseTest.BaseTest.timeout
timeout
Definition: BaseTest.py:112
GaudiTesting.BaseTest.ReferenceFileValidator.preproc
preproc
Definition: BaseTest.py:1088
GaudiTesting.BaseTest.BaseTest.validateWithReference
def validateWithReference(self, stdout=None, stderr=None, result=None, causes=None, preproc=None)
Definition: BaseTest.py:517
GaudiTesting.BaseTest.getCmpFailingValues
def getCmpFailingValues(reference, to_check, fail_path)
Definition: BaseTest.py:1175
GaudiTesting.BaseTest.BasicOutputValidator.result_key
result_key
Definition: BaseTest.py:752
GaudiTesting.BaseTest.BaseTest.proc
proc
Definition: BaseTest.py:127
GaudiTesting.BaseTest._new_backslashreplace_errors
def _new_backslashreplace_errors(exc)
Definition: BaseTest.py:38
hivetimeline.read
def read(f, regex='.*', skipevents=0)
Definition: hivetimeline.py:33
GaudiTesting.BaseTest.BaseTest.stack_trace
stack_trace
Definition: BaseTest.py:128
GaudiTesting.BaseTest.FilePreprocessor.__processFile__
def __processFile__(self, lines)
Definition: BaseTest.py:802
GaudiTesting.BaseTest.BaseTest.environment
environment
Definition: BaseTest.py:114
GaudiTesting.BaseTest.LineSorter.signature
signature
Definition: BaseTest.py:912
GaudiTesting.BaseTest.BaseTest.exit_code
exit_code
Definition: BaseTest.py:113
GaudiTesting.BaseTest.BlockSkipper.start
start
Definition: BaseTest.py:858
GaudiTesting.BaseTest.kill_tree
def kill_tree(ppid, sig)
Definition: BaseTest.py:78
GaudiTesting.BaseTest.Result.Quote
def Quote(self, text)
Definition: BaseTest.py:732
GaudiTesting.BaseTest.FilePreprocessorSequence.__add__
def __add__(self, rhs)
Definition: BaseTest.py:830
Containers::map
struct GAUDI_API map
Parametrisation class for map-like implementation.
Definition: KeyedObjectManager.h:35
GaudiTesting.BaseTest.FilePreprocessorSequence
Definition: BaseTest.py:826
GaudiTesting.BaseTest.BaseTest.__init__
def __init__(self)
Definition: BaseTest.py:105
GaudiTesting.BaseTest.RegexpReplacer._operations
_operations
Definition: BaseTest.py:877
GaudiTesting.BaseTest.BaseTest.err
err
Definition: BaseTest.py:126
GaudiTesting.BaseTest.SortGroupOfLines.__processFile__
def __processFile__(self, lines)
Definition: BaseTest.py:933
GaudiTesting.BaseTest.BlockSkipper
Definition: BaseTest.py:856
GaudiTesting.BaseTest.BaseTest.args
args
Definition: BaseTest.py:107
GaudiTesting.BaseTest.BaseTest.result
result
Definition: BaseTest.py:123
GaudiTesting.BaseTest.FilePreprocessor.__processLine__
def __processLine__(self, line)
Definition: BaseTest.py:799
GaudiTesting.BaseTest.FilePreprocessorSequence.__call__
def __call__(self, input)
Definition: BaseTest.py:833
GaudiTesting.BaseTest.BaseTest.workdir
workdir
Definition: BaseTest.py:117
Gaudi::Functional::details::get
auto get(const Handle &handle, const Algo &, const EventContext &) -> decltype(details::deref(handle.get()))
Definition: FunctionalDetails.h:391
GaudiTesting.BaseTest.BlockSkipper._skipping
_skipping
Definition: BaseTest.py:860
GaudiTesting.BaseTest.ReferenceFileValidator.cause
cause
Definition: BaseTest.py:1086
GaudiTesting.BaseTest.parseHistosSummary
def parseHistosSummary(lines, pos)
Definition: BaseTest.py:1243
GaudiTesting.BaseTest.RegexpReplacer
Definition: BaseTest.py:873
GaudiTesting.BaseTest.isWinPlatform
def isWinPlatform(self)
Definition: BaseTest.py:1358
GaudiTesting.BaseTest.LineSkipper.regexps
regexps
Definition: BaseTest.py:844
GaudiTesting.BaseTest.BaseTest.basedir
basedir
Definition: BaseTest.py:129
GaudiTesting.BaseTest.which
def which(executable)
Definition: BaseTest.py:674
GaudiTesting.BaseTest.SortGroupOfLines.exp
exp
Definition: BaseTest.py:931
GaudiTesting.BaseTest.BaseTest.unsupported_platforms
unsupported_platforms
Definition: BaseTest.py:115
GaudiTesting.BaseTest.Result.__init__
def __init__(self, kind=None, id=None, outcome=PASS, annotations={})
Definition: BaseTest.py:719
GaudiTesting.BaseTest.BaseTest.countErrorLines
def countErrorLines(self, expected={ 'ERROR':0, 'FATAL':0 }, stdout=None, result=None, causes=None)
Definition: BaseTest.py:379
GaudiTesting.BaseTest.BlockSkipper.end
end
Definition: BaseTest.py:859
GaudiTesting.BaseTest.BaseTest.returnedCode
returnedCode
Definition: BaseTest.py:124
GaudiTesting.BaseTest.LineSkipper.strings
strings
Definition: BaseTest.py:843
GaudiTesting.BaseTest.BasicOutputValidator.__call__
def __call__(self, out, result)
Definition: BaseTest.py:754
GaudiTesting.BaseTest.cmpTreesDicts
def cmpTreesDicts(reference, to_check, ignore=None)
Definition: BaseTest.py:1141
GaudiTesting.BaseTest.Result.annotations
annotations
Definition: BaseTest.py:720
GaudiTesting.BaseTest.BaseTest.name
name
Definition: BaseTest.py:121
format
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
GaudiTesting.BaseTest.RegexpReplacer.__processLine__
def __processLine__(self, line)
Definition: BaseTest.py:887
GaudiTesting.BaseTest.ReferenceFileValidator.__init__
def __init__(self, reffile, cause, result_key, preproc=normalizeExamples)
Definition: BaseTest.py:1084
GaudiTesting.BaseTest.FilePreprocessorSequence.members
members
Definition: BaseTest.py:828
GaudiTesting.BaseTest.BaseTest._expandReferenceFileName
def _expandReferenceFileName(self, reffile)
Definition: BaseTest.py:603
GaudiTesting.BaseTest.BaseTest.signal
signal
Definition: BaseTest.py:116
GaudiTesting.BaseTest.SortGroupOfLines
Definition: BaseTest.py:925
GaudiTesting.BaseTest.BaseTest.findReferenceBlock
def findReferenceBlock(self, reference=None, stdout=None, result=None, causes=None, signature_offset=0, signature=None, id=None)
Definition: BaseTest.py:322
GaudiTesting.BaseTest.RationalizePath
def RationalizePath(p)
Definition: BaseTest.py:664
GaudiTesting.BaseTest.LineSkipper
Definition: BaseTest.py:840
GaudiTesting.BaseTest.ReferenceFileValidator
Definition: BaseTest.py:1083
gaudirun.type
type
Definition: gaudirun.py:154
GaudiTesting.BaseTest.BaseTest.program
program
Definition: BaseTest.py:106
GaudiTesting.BaseTest.FilePreprocessorSequence.__init__
def __init__(self, members=[])
Definition: BaseTest.py:827
GaudiTesting.BaseTest.ReferenceFileValidator.__call__
def __call__(self, stdout, result)
Definition: BaseTest.py:1090
GaudiTesting.BaseTest.BasicOutputValidator.cause
cause
Definition: BaseTest.py:751
GaudiTesting.BaseTest.FilePreprocessor.__add__
def __add__(self, rhs)
Definition: BaseTest.py:822
GaudiTesting.BaseTest.ReferenceFileValidator.result_key
result_key
Definition: BaseTest.py:1087
GaudiTesting.BaseTest.BlockSkipper.__init__
def __init__(self, start, end)
Definition: BaseTest.py:857
GaudiTesting.BaseTest.RegexpReplacer.__init__
def __init__(self, orig, repl="", when=None)
Definition: BaseTest.py:874
GaudiTesting.BaseTest.findHistosSummaries
def findHistosSummaries(stdout)
Definition: BaseTest.py:1313
GaudiTesting.BaseTest.Result.__setitem__
def __setitem__(self, key, value)
Definition: BaseTest.py:726
GaudiTesting.BaseTest.BaseTest.CheckTTreesSummaries
def CheckTTreesSummaries(self, stdout=None, result=None, causes=None, trees_dict=None, ignore=r"Basket|.*size|Compression")
Definition: BaseTest.py:427
GaudiTesting.BaseTest.BaseTest
Definition: BaseTest.py:101
GaudiTesting.BaseTest.BaseTest.error_reference
error_reference
Definition: BaseTest.py:109
GaudiTesting.BaseTest.BaseTest.ValidateOutput
def ValidateOutput(self, stdout, stderr, result)
Definition: BaseTest.py:315
GaudiTesting.BaseTest.BasicOutputValidator
Definition: BaseTest.py:748
GaudiTesting.BaseTest.LineSkipper.__init__
def __init__(self, strings=[], regexps=[])
Definition: BaseTest.py:841
GaudiTesting.BaseTest.Result
Definition: BaseTest.py:704
GaudiTesting.BaseTest.BaseTest.run
def run(self)
Definition: BaseTest.py:131
GaudiTesting.BaseTest.findTTreeSummaries
def findTTreeSummaries(stdout)
Definition: BaseTest.py:1119
GaudiTesting.BaseTest.BasicOutputValidator.__CompareText
def __CompareText(self, s1, s2)
Definition: BaseTest.py:772
GaudiTesting.BaseTest.RegexpReplacer.__add__
def __add__(self, rhs)
Definition: BaseTest.py:879
compareOutputFiles.pp
pp
Definition: compareOutputFiles.py:507
GaudiTesting.BaseTest.BaseTest.stderr
stderr
Definition: BaseTest.py:111
GaudiTesting.BaseTest.LineSorter.__init__
def __init__(self, signature)
Definition: BaseTest.py:911
GaudiTesting.BaseTest.LineSkipper.__processLine__
def __processLine__(self, line)
Definition: BaseTest.py:846
GaudiTesting.BaseTest.ROOT6WorkAroundEnabled
def ROOT6WorkAroundEnabled(id=None)
Definition: BaseTest.py:656
GaudiTesting.BaseTest.BaseTest.use_temp_dir
use_temp_dir
Definition: BaseTest.py:118
GaudiTesting.BaseTest.GetPlatform
def GetPlatform(self)
Definition: BaseTest.py:1336
GaudiTesting.BaseTest.BaseTest.status
status
Definition: BaseTest.py:120
Gaudi::Functional::details::zip::range
decltype(auto) range(Args &&... args)
Zips multiple containers together to form a single range.
Definition: FunctionalDetails.h:97
GaudiTesting.BaseTest.BlockSkipper.__processLine__
def __processLine__(self, line)
Definition: BaseTest.py:862