| The Gaudi Framework v32r1 (f65d50dc)

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