14 from subprocess
import Popen, PIPE, STDOUT
18 if sys.version_info < (3, 5):
21 from codecs
import register_error, backslashreplace_errors
24 if isinstance(exc, UnicodeDecodeError):
25 code =
hex(ord(exc.object[exc.start]))
26 return (
u'\\' + code[1:], exc.start + 1)
28 return backslashreplace_errors(exc)
30 register_error(
'backslashreplace', _new_backslashreplace_errors)
32 del backslashreplace_errors
33 del _new_backslashreplace_errors
38 Take a string with invalid ASCII/UTF characters and quote them so that the 39 string can be used in an XML text. 41 >>> sanitize_for_xml('this is \x1b') 42 'this is [NON-XML-CHAR-0x1B]' 44 bad_chars = re.compile(
45 u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')
49 return ''.join(
'[NON-XML-CHAR-0x%2X]' % ord(c)
for c
in match.group())
51 return bad_chars.sub(quote, data)
55 '''helper to debug GAUDI-1084, dump the list of processes''' 56 from getpass
import getuser
57 if 'WORKSPACE' in os.environ:
58 p = Popen([
'ps',
'-fH',
'-U', getuser()], stdout=PIPE)
59 with open(os.path.join(os.environ[
'WORKSPACE'], name),
'wb')
as f:
60 f.write(p.communicate()[0])
65 Send a signal to a process and all its child processes (starting from the 68 log = logging.getLogger(
'kill_tree')
69 ps_cmd = [
'ps',
'--no-headers',
'-o',
'pid',
'--ppid', str(ppid)]
70 get_children = Popen(ps_cmd, stdout=PIPE, stderr=PIPE)
71 children =
map(int, get_children.communicate()[0].split())
72 for child
in children:
75 log.debug(
'killing process %d', ppid)
77 except OSError
as err:
80 log.debug(
'no such process %d', ppid)
117 logging.debug(
'running test %s', self.
name)
121 r'from\s+Gaudi.Configuration\s+import\s+\*|' 122 'from\s+Configurables\s+import', self.
options):
123 optionFile = tempfile.NamedTemporaryFile(suffix=
'.py')
125 optionFile = tempfile.NamedTemporaryFile(suffix=
'.opts')
126 optionFile.file.write(self.
options.encode(
'utf-8'))
137 platform_id = (os.environ.get(
'BINARY_TAG')
138 or os.environ.get(
'CMTCONFIG')
or platform.platform())
142 if re.search(prex, platform_id)
152 workdir = tempfile.mkdtemp()
158 elif "GAUDIEXE" in os.environ:
159 prog = os.environ[
"GAUDIEXE"]
163 dummy, prog_ext = os.path.splitext(prog)
164 if prog_ext
not in [
".exe",
".py",
".bat"]:
168 prog =
which(prog)
or prog
170 args = list(
map(RationalizePath, self.
args))
172 if prog_ext ==
".py":
185 'TIMEOUT_DETAIL':
None 187 self.
result = validatorRes
195 logging.debug(
'executing %r in %s', params, workdir)
197 params, stdout=PIPE, stderr=PIPE, env=self.
environment)
198 logging.debug(
'(pid: %d)', self.
proc.pid)
199 out, err = self.
proc.communicate()
200 self.
out = out.decode(
'utf-8', errors=
'backslashreplace')
201 self.
err = err.decode(
'utf-8', errors=
'backslashreplace')
203 thread = threading.Thread(target=target)
208 if thread.is_alive():
209 logging.debug(
'time out in test %s (pid %d)', self.
name,
214 str(self.
proc.pid),
'--batch',
215 '--eval-command=thread apply all backtrace' 217 gdb = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
219 'utf-8', errors=
'backslashreplace')
223 if thread.is_alive():
225 self.
causes.append(
'timeout')
227 logging.debug(
'completed test %s', self.
name)
230 logging.debug(
'returnedCode = %s', self.
proc.returncode)
233 logging.debug(
'validating test...')
235 stdout=self.
out, stderr=self.
err, result=validatorRes)
239 shutil.rmtree(workdir,
True)
244 if self.
signal is not None:
246 self.
causes.append(
'exit code')
250 self.
causes.append(
'exit code')
253 self.
causes.append(
"exit code")
263 logging.debug(
'%s: %s', self.
name, self.
status)
265 'Exit Code':
'returnedCode',
268 'Environment':
'environment',
271 'Program Name':
'program',
273 'Validator':
'validator',
274 'Output Reference File':
'reference',
275 'Error Reference File':
'error_reference',
278 'Unsupported Platforms':
'unsupported_platforms',
279 'Stack Trace':
'stack_trace' 281 resultDict = [(key, getattr(self, attr))
282 for key, attr
in field_mapping.items()
283 if getattr(self, attr)]
284 resultDict.append((
'Working Directory',
286 os.path.join(os.getcwd(), self.
workdir))))
288 resultDict.extend(self.
result.annotations.items())
290 return dict(resultDict)
299 elif stderr.strip() != self.
stderr.strip():
300 self.
causes.append(
'standard error')
301 return result, self.
causes 312 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. 315 if reference
is None:
325 filter(
None,
map(
lambda s: s.rstrip(), reference.splitlines())))
327 raise RuntimeError(
"Empty (or null) reference")
330 filter(
None,
map(
lambda s: s.rstrip(), stdout.splitlines())))
332 res_field =
"GaudiTest.RefBlock" 334 res_field +=
"_%s" % id
336 if signature
is None:
337 if signature_offset < 0:
338 signature_offset = len(reference) + signature_offset
339 signature = reflines[signature_offset]
342 pos = outlines.index(signature)
343 outlines = outlines[pos - signature_offset:pos + len(reflines) -
345 if reflines != outlines:
346 msg =
"standard output" 349 if not msg
in causes:
351 result[res_field +
".observed"] = result.Quote(
354 causes.append(
"missing signature")
355 result[res_field +
".signature"] = result.Quote(signature)
356 if len(reflines) > 1
or signature != reflines[0]:
357 result[res_field +
".expected"] = result.Quote(
"\n".join(reflines))
369 Count the number of messages with required severity (by default ERROR and FATAL) 370 and check if their numbers match the expected ones (0 by default). 371 The dictionary "expected" can be used to tune the number of errors and fatals 372 allowed, or to limit the number of expected warnings etc. 387 outlines = stdout.splitlines()
388 from math
import log10
389 fmt =
"%%%dd - %%s" % (int(log10(len(outlines) + 1)))
395 if len(words) >= 2
and words[1]
in errors:
396 errors[words[1]].append(fmt % (linecount, l.rstrip()))
399 if len(errors[e]) != expected[e]:
400 causes.append(
'%s(%d)' % (e, len(errors[e])))
401 result[
"GaudiTest.lines.%s" % e] = result.Quote(
'\n'.join(
403 result[
"GaudiTest.lines.%s.expected#" % e] = result.Quote(
413 ignore=r"Basket|.*size|Compression"):
415 Compare the TTree summaries in stdout with the ones in trees_dict or in 416 the reference file. By default ignore the size, compression and basket 418 The presence of TTree summaries when none is expected is not a failure. 426 if trees_dict
is None:
429 if lreference
and os.path.isfile(lreference):
434 from pprint
import PrettyPrinter
437 result[
"GaudiTest.TTrees.expected"] = result.Quote(
438 pp.pformat(trees_dict))
440 result[
"GaudiTest.TTrees.ignore"] = result.Quote(ignore)
445 causes.append(
"trees summaries")
448 result[
"GaudiTest.TTrees.failure_on"] = result.Quote(msg)
449 result[
"GaudiTest.TTrees.found"] = result.Quote(pp.pformat(trees))
460 Compare the TTree summaries in stdout with the ones in trees_dict or in 461 the reference file. By default ignore the size, compression and basket 463 The presence of TTree summaries when none is expected is not a failure. 475 if lreference
and os.path.isfile(lreference):
480 from pprint
import PrettyPrinter
483 result[
"GaudiTest.Histos.expected"] = result.Quote(
486 result[
"GaudiTest.Histos.ignore"] = result.Quote(ignore)
491 causes.append(
"histos summaries")
493 result[
"GaudiTest.Histos.failure_on"] = result.Quote(msg)
494 result[
"GaudiTest.Histos.found"] = result.Quote(pp.pformat(histos))
505 Default validation acti*on: compare standard output and error to the 520 preproc = normalizeExamples
524 if lreference
and os.path.isfile(lreference):
526 lreference,
"standard output",
"Output Diff",
527 preproc=preproc)(stdout, result)
529 causes += [
"missing reference file"]
533 if causes
and lreference:
536 newrefname =
'.'.join([lreference,
'new'])
537 while os.path.exists(newrefname):
539 newrefname =
'.'.join([lreference,
'~%d~' % cnt,
'new'])
540 newref = open(newrefname,
"w")
542 for l
in stdout.splitlines():
543 newref.write(l.rstrip() +
'\n')
545 result[
'New Output Reference File'] = os.path.relpath(
556 if os.path.isfile(lreference):
561 preproc=preproc)(stderr, result)
563 newcauses += [
"missing error reference file"]
565 if newcauses
and lreference:
567 newrefname =
'.'.join([lreference,
'new'])
568 while os.path.exists(newrefname):
570 newrefname =
'.'.join([lreference,
'~%d~' % cnt,
'new'])
571 newref = open(newrefname,
"w")
573 for l
in stderr.splitlines():
574 newref.write(l.rstrip() +
'\n')
576 result[
'New Error Reference File'] = os.path.relpath(
580 "ExecTest.expected_stderr")(stderr,
590 def platformSplit(p):
592 delim = re.compile(
'-' in p
and r"[-+]" or r"_")
593 return set(delim.split(p))
595 reference = os.path.normpath(
596 os.path.join(self.
basedir, os.path.expandvars(reffile)))
599 spec_ref = reference[:-3] +
GetPlatform(self)[0:3] + reference[-3:]
600 if os.path.isfile(spec_ref):
604 dirname, basename = os.path.split(reference)
607 head = basename +
"." 610 if 'do0' in platform:
613 for f
in os.listdir(dirname):
614 if f.startswith(head):
615 req_plat = platformSplit(f[head_len:])
616 if platform.issuperset(req_plat):
617 candidates.append((len(req_plat), f))
622 reference = os.path.join(dirname, candidates[-1][1])
634 from GaudiKernel
import ROOT6WorkAroundEnabled
647 Function used to normalize the used path 649 newPath = os.path.normpath(os.path.expandvars(p))
650 if os.path.exists(newPath):
651 p = os.path.realpath(newPath)
657 Locates an executable in the executables path ($PATH) and returns the full 658 path to it. An application is looked for with or without the '.exe' suffix. 659 If the executable cannot be found, None is returned 661 if os.path.isabs(executable):
662 if not os.path.exists(executable):
663 if executable.endswith(
'.exe'):
664 if os.path.exists(executable[:-4]):
665 return executable[:-4]
667 head, executable = os.path.split(executable)
670 for d
in os.environ.get(
"PATH").split(os.pathsep):
671 fullpath = os.path.join(d, executable)
672 if os.path.exists(fullpath):
674 if executable.endswith(
'.exe'):
675 return which(executable[:-4])
690 UNTESTED =
'UNTESTED' 700 def __init__(self, kind=None, id=None, outcome=PASS, annotations={}):
704 assert isinstance(key, six.string_types)
708 assert isinstance(key, six.string_types)
710 value, six.string_types),
'{!r} is not a string'.
format(value)
733 """Validate the output of the program. 734 'stdout' -- A string containing the data written to the standard output 736 'stderr' -- A string containing the data written to the standard error 738 'result' -- A 'Result' object. It may be used to annotate 739 the outcome according to the content of stderr. 740 returns -- A list of strings giving causes of failure.""" 745 causes.append(self.
cause)
751 """Compare 's1' and 's2', ignoring line endings. 754 returns -- True if 's1' and 's2' are the same, ignoring 755 differences in line endings.""" 759 to_ignore = re.compile(
760 r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*' 764 return not to_ignore.match(l)
766 return list(filter(keep_line, s1.splitlines())) == list(
767 filter(keep_line, s2.splitlines()))
769 return s1.splitlines() == s2.splitlines()
774 """ Base class for a callable that takes a file and returns a modified 789 if not isinstance(input, six.string_types):
793 lines = input.splitlines()
797 output =
'\n'.join(output)
826 if line.find(s) >= 0:
841 if self.
start in line:
844 elif self.
end in line:
854 when = re.compile(when)
858 if isinstance(rhs, RegexpReplacer):
860 res._operations = self.
_operations + rhs._operations
862 res = FilePreprocessor.__add__(self, rhs)
867 if w
is None or w.search(line):
868 line = o.sub(r, line)
875 "[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9] [0-9]{4}[-/][01][0-9][-/][0-3][0-9][ A-Z]*",
876 "00:00:00 1970-01-01")
878 normalizeEOL.__processLine__ =
lambda line: str(line).rstrip() +
'\n' 882 skipEmptyLines.__processLine__ =
lambda line: (line.strip()
and line)
or None 896 line = line[:(pos + self.
siglen)]
897 lst = line[(pos + self.
siglen):].split()
899 line +=
" ".join(lst)
905 Sort group of lines matching a regular expression 909 self.
exp = exp
if hasattr(exp,
'match')
else re.compile(exp)
912 match = self.
exp.match
928 normalizeExamples = maskPointers + normalizeDate
931 (
"TIMER.TIMER",
r"\s+[+-]?[0-9]+[0-9.]*",
" 0"),
932 (
"release all pending",
r"^.*/([^/]*:.*)",
r"\1"),
933 (
"^#.*file",
r"file '.*[/\\]([^/\\]*)$",
r"file '\1"),
934 (
"^JobOptionsSvc.*options successfully read in from",
935 r"read in from .*[/\\]([^/\\]*)$",
939 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}",
940 "00000000-0000-0000-0000-000000000000"),
942 (
"ServiceLocatorHelper::",
"ServiceLocatorHelper::(create|locate)Service",
943 "ServiceLocatorHelper::service"),
945 (
None,
r"e([-+])0([0-9][0-9])",
r"e\1\2"),
947 (
None,
r'Service reference count check:',
948 r'Looping over all active services...'),
951 r"^(.*(DEBUG|SUCCESS) List of ALL properties of .*#properties = )\d+",
953 (
'ApplicationMgr',
r'(declareMultiSvcType|addMultiSvc): ',
''),
960 "JobOptionsSvc INFO # ",
961 "JobOptionsSvc WARNING # ",
964 "This machine has a speed",
967 "ToolSvc.Sequenc... INFO",
968 "DataListenerSvc INFO XML written to file:",
971 "DEBUG No writable file catalog found which contains FID:",
972 "DEBUG Service base class initialized successfully",
974 "DEBUG Incident timing:",
978 "INFO 'CnvServices':[",
980 "DEBUG 'CnvServices':[",
986 r"^JobOptionsSvc INFO *$",
989 r"(Always|SUCCESS)\s*(Root f|[^ ]* F)ile version:",
990 r"File '.*.xml' does not exist",
991 r"INFO Refer to dataset .* by its file ID:",
992 r"INFO Referring to dataset .* by its file ID:",
993 r"INFO Disconnect from dataset",
994 r"INFO Disconnected from dataset",
995 r"INFO Disconnected data IO:",
996 r"IncidentSvc\s*(DEBUG (Adding|Removing)|VERBOSE Calling)",
998 r"^StatusCodeSvc.*listing all unchecked return codes:",
999 r"^StatusCodeSvc\s*INFO\s*$",
1000 r"Num\s*\|\s*Function\s*\|\s*Source Library",
1003 r"ERROR Failed to modify file: .* Errno=2 No such file or directory",
1005 r"^ +[0-9]+ \|.*ROOT",
1006 r"^ +[0-9]+ \|.*\|.*Dict",
1008 r"StatusCodeSvc.*all StatusCode instances where checked",
1010 r"EventLoopMgr.*---> Loop Finished",
1011 r"HiveSlimEventLo.*---> Loop Finished",
1016 r"SUCCESS\s*Booked \d+ Histogram\(s\)",
1020 r"Property(.*)'Audit(Algorithm|Tool|Service)s':",
1022 r"Property(.*)'AuditRe(start|initialize)':",
1023 r"Property(.*)'IsIOBound':",
1025 r"Property(.*)'ErrorCount(er)?':",
1027 r"Property(.*)'Sequential':",
1029 r"Property(.*)'FilterCircularDependencies':",
1031 r"Property(.*)'IsClonable':",
1033 r"Property update for OutputLevel : new value =",
1034 r"EventLoopMgr\s*DEBUG Creating OutputStream",
1041 r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*',
1044 normalizeExamples = (
1045 lineSkipper + normalizeExamples + skipEmptyLines + normalizeEOL +
1053 def __init__(self, reffile, cause, result_key, preproc=normalizeExamples):
1061 if os.path.isfile(self.
reffile):
1062 orig = open(self.
reffile).readlines()
1065 result[self.
result_key +
'.preproc.orig'] = \
1066 result.Quote(
'\n'.join(
map(str.strip, orig)))
1069 new = stdout.splitlines()
1073 diffs = difflib.ndiff(orig, new, charjunk=difflib.IS_CHARACTER_JUNK)
1075 map(
lambda x: x.strip(), filter(
lambda x: x[0] !=
" ", diffs)))
1077 result[self.
result_key] = result.Quote(
"\n".join(filterdiffs))
1081 +) standard output of the test""")
1083 result.Quote(
'\n'.join(
map(str.strip, new)))
1084 causes.append(self.
cause)
1090 Scan stdout to find ROOT TTree summaries and digest them. 1092 stars = re.compile(
r"^\*+$")
1093 outlines = stdout.splitlines()
1094 nlines = len(outlines)
1100 while i < nlines
and not stars.match(outlines[i]):
1105 trees[tree[
"Name"]] = tree
1112 Check that all the keys in reference are in to_check too, with the same value. 1113 If the value is a dict, the function is called recursively. to_check can 1114 contain more keys than reference, that will not be tested. 1115 The function returns at the first difference found. 1120 ignore_re = re.compile(ignore)
1121 keys = [key
for key
in reference
if not ignore_re.match(key)]
1123 keys = reference.keys()
1127 if (
type(reference[k])
is dict)
and (
type(to_check[k])
is dict):
1130 failed = fail_keys =
cmpTreesDicts(reference[k], to_check[k],
1134 failed = to_check[k] != reference[k]
1139 fail_keys.insert(0, k)
1150 if c
is None or r
is None:
1152 return (fail_path, r, c)
1156 h_count_re = re.compile(
1157 r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+([\s\w=-]*)")
1162 Parse the TTree summary table in lines, starting from pos. 1163 Returns a tuple with the dictionary with the digested informations and the 1164 position of the first line after the summary. 1171 return [f.strip()
for f
in l.strip(
"*\n").split(
':', 2)]
1175 cols = splitcols(ll[0])
1176 r[
"Name"], r[
"Title"] = cols[1:]
1178 cols = splitcols(ll[1])
1179 r[
"Entries"] = int(cols[1])
1181 sizes = cols[2].split()
1182 r[
"Total size"] = int(sizes[2])
1183 if sizes[-1] ==
"memory":
1186 r[
"File size"] = int(sizes[-1])
1188 cols = splitcols(ll[2])
1189 sizes = cols[2].split()
1190 if cols[0] ==
"Baskets":
1191 r[
"Baskets"] = int(cols[1])
1192 r[
"Basket size"] = int(sizes[2])
1193 r[
"Compression"] = float(sizes[-1])
1196 if i < (count - 3)
and lines[i].startswith(
"*Tree"):
1197 result = parseblock(lines[i:i + 3])
1198 result[
"Branches"] = {}
1200 while i < (count - 3)
and lines[i].startswith(
"*Br"):
1201 if i < (count - 2)
and lines[i].startswith(
"*Branch "):
1205 branch = parseblock(lines[i:i + 3])
1206 result[
"Branches"][branch[
"Name"]] = branch
1214 Extract the histograms infos from the lines starting at pos. 1215 Returns the position of the first line after the summary block. 1218 h_table_head = re.compile(
1219 r'SUCCESS\s+(1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"' 1221 h_short_summ = re.compile(
r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
1226 m = h_count_re.search(lines[pos])
1227 name = m.group(1).strip()
1228 total = int(m.group(2))
1230 for k, v
in [x.split(
"=")
for x
in m.group(3).split()]:
1233 header[
"Total"] = total
1237 m = h_table_head.search(lines[pos])
1240 t = t.replace(
" profile",
"Prof")
1247 if l.startswith(
" | ID"):
1249 titles = [x.strip()
for x
in l.split(
"|")][1:]
1251 while pos < nlines
and lines[pos].startswith(
" |"):
1253 values = [x.strip()
for x
in l.split(
"|")][1:]
1255 for i
in range(len(titles)):
1256 hcont[titles[i]] = values[i]
1257 cont[hcont[
"ID"]] = hcont
1259 elif l.startswith(
" ID="):
1260 while pos < nlines
and lines[pos].startswith(
" ID="):
1263 for x
in h_short_summ.search(lines[pos]).groups()
1265 cont[values[0]] = values
1269 "Cannot understand line %d: '%s'" % (pos, l))
1273 summ[d][
"header"] = header
1278 summ[name] = {
"header": header}
1284 Scan stdout to find ROOT TTree summaries and digest them. 1286 outlines = stdout.splitlines()
1287 nlines = len(outlines) - 1
1295 match = h_count_re.search(outlines[pos])
1296 while pos < nlines
and not match:
1298 match = h_count_re.search(outlines[pos])
1301 summaries.update(summ)
1308 re.compile(x)
for x
in [str(y).strip()
for y
in unsupported_platforms]
1311 for p_re
in unsupported:
1312 if p_re.search(platform):
1313 result.SetOutcome(result.UNTESTED)
1314 result[result.CAUSE] =
'Platform not supported.' 1321 Return the platform Id defined in CMTCONFIG or SCRAM_ARCH. 1325 if "BINARY_TAG" in os.environ:
1326 arch = os.environ[
"BINARY_TAG"]
1327 elif "CMTCONFIG" in os.environ:
1328 arch = os.environ[
"CMTCONFIG"]
1329 elif "SCRAM_ARCH" in os.environ:
1330 arch = os.environ[
"SCRAM_ARCH"]
1336 Return True if the current platform is Windows. 1338 This function was needed because of the change in the CMTCONFIG format, 1339 from win32_vc71_dbg to i686-winxp-vc9-dbg. 1342 return "winxp" in platform
or platform.startswith(
"win")
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
def PlatformIsNotSupported(self, context, result)
def __processLine__(self, line)
def __init__(self, start, end)
def __call__(self, input)
def validateWithReference(self, stdout=None, stderr=None, result=None, causes=None, preproc=None)
def __processLine__(self, line)
def cmpTreesDicts(reference, to_check, ignore=None)
def __processFile__(self, lines)
def ValidateOutput(self, stdout, stderr, result)
def read(f, regex='.*', skipevents=0)
def __processLine__(self, line)
def __processFile__(self, lines)
def __call__(self, out, result)
def findHistosSummaries(stdout)
MsgStream & hex(MsgStream &log)
def _parseTTreeSummary(lines, pos)
struct GAUDI_API map
Parametrisation class for map-like implementation.
def __call__(self, stdout, result)
def __processLine__(self, line)
def __init__(self, orig, repl="", when=None)
def _new_backslashreplace_errors(exc)
def __init__(self, signature)
def __call__(self, input)
def sanitize_for_xml(data)
def countErrorLines(self, expected={ 'ERROR':0, 'FATAL':0 }, stdout=None, result=None, causes=None)
def getCmpFailingValues(reference, to_check, fail_path)
def __init__(self, members=[])
def __init__(self, strings=[], regexps=[])
def __setitem__(self, key, value)
def __init__(self, kind=None, id=None, outcome=PASS, annotations={})
def __processLine__(self, line)
def parseHistosSummary(lines, pos)
def _expandReferenceFileName(self, reffile)
def findReferenceBlock(self, reference=None, stdout=None, result=None, causes=None, signature_offset=0, signature=None, id=None)
def CheckHistosSummaries(self, stdout=None, result=None, causes=None, dict=None, ignore=None)
def __CompareText(self, s1, s2)
def __init__(self, reffile, cause, result_key, preproc=normalizeExamples)
decltype(auto) range(Args &&... args)
Zips multiple containers together to form a single range.
def __getitem__(self, key)
def findTTreeSummaries(stdout)
def __init__(self, ref, cause, result_key)
def ROOT6WorkAroundEnabled(id=None)
def CheckTTreesSummaries(self, stdout=None, result=None, causes=None, trees_dict=None, ignore=r"Basket|.*size|Compression")