5 __author__ =
'Marco Clemencic CERN/PH-LBC'
20 from subprocess
import Popen, PIPE, STDOUT
23 from GaudiKernel
import ROOT6WorkAroundEnabled
30 os.environ[
'LC_ALL'] =
'C'
34 import xml.etree.cElementTree
as ET
36 import xml.etree.ElementTree
as ET
40 return timedelta.days*86400 + timedelta.seconds + timedelta.microseconds/1000000
44 from qm.test.classes.command
import ExecTestBase
45 from qm.test.result_stream
import ResultStream
52 if sys.platform ==
"win32":
55 from threading
import *
73 Class to changes the environment temporarily.
75 def __init__(self, orig = os.environ, keep_same = False):
77 Create a temporary environment on top of the one specified
78 (it can be another TemporaryEnvironment instance).
87 Set an environment variable recording the previous value.
99 Get an environment variable.
100 Needed to provide the same interface as os.environ.
106 Unset an environment variable.
107 Needed to provide the same interface as os.environ.
109 if key
not in self.
env :
116 Return the list of defined environment variables.
117 Needed to provide the same interface as os.environ.
119 return self.env.keys()
123 Return the list of (name,value) pairs for the defined environment variables.
124 Needed to provide the same interface as os.environ.
126 return self.env.items()
131 Needed to provide the same interface as os.environ.
133 return key
in self.
env
137 Revert all the changes done to the original environment.
139 for key,value
in self.old_values.items():
143 self.
env[key] = value
148 Revert the changes on destruction.
155 Generate a shell script to reproduce the changes in the environment.
157 shells = [
'csh',
'sh',
'bat' ]
158 if shell_type
not in shells:
159 raise RuntimeError(
"Shell type '%s' unknown. Available: %s"%(shell_type,shells))
161 for key,value
in self.old_values.items():
162 if key
not in self.
env:
164 if shell_type ==
'csh':
165 out +=
'unsetenv %s\n'%key
166 elif shell_type ==
'sh':
167 out +=
'unset %s\n'%key
168 elif shell_type ==
'bat':
169 out +=
'set %s=\n'%key
172 if shell_type ==
'csh':
173 out +=
'setenv %s "%s"\n'%(key,self.
env[key])
174 elif shell_type ==
'sh':
175 out +=
'export %s="%s"\n'%(key,self.
env[key])
176 elif shell_type ==
'bat':
177 out +=
'set %s=%s\n'%(key,self.
env[key])
181 """Small class for temporary directories.
182 When instantiated, it creates a temporary directory and the instance
183 behaves as the string containing the directory name.
184 When the instance goes out of scope, it removes all the content of
185 the temporary directory (automatic clean-up).
202 shutil.rmtree(self.
name)
205 return getattr(self.
name,attr)
208 """Small class for temporary files.
209 When instantiated, it creates a temporary directory and the instance
210 behaves as the string containing the directory name.
211 When the instance goes out of scope, it removes all the content of
212 the temporary directory (automatic clean-up).
214 def __init__(self, suffix='', prefix='tmp', dir=None, text=False, keep = False):
219 self._fd, self.
name = tempfile.mkstemp(suffix,prefix,dir,text)
220 self.
file = os.fdopen(self._fd,
"r+")
232 return getattr(self.
file,attr)
235 """Small wrapper to call CMT.
244 if type(args)
is str:
246 cmd =
"cmt %s"%command
254 result = os.popen4(cmd)[1].read()
260 return lambda args=[]: self.
_run_cmt(attr, args)
263 """Returns a dictionary containing the runtime environment produced by CMT.
264 If a dictionary is passed a modified instance of it is returned.
268 for l
in self.setup(
"-csh").splitlines():
270 if l.startswith(
"setenv"):
271 dummy,name,value = l.split(
None,3)
272 env[name] = value.strip(
'"')
273 elif l.startswith(
"unsetenv"):
274 dummy,name = l.split(
None,2)
279 r = self.show([
"macro",k])
280 if r.find(
"CMT> Error: symbol not found") >= 0:
283 return self.show([
"macro_value",k]).strip()
291 Locates an executable in the executables path ($PATH) and returns the full
292 path to it. An application is looked for with or without the '.exe' suffix.
293 If the executable cannot be found, None is returned
295 if os.path.isabs(executable):
296 if not os.path.exists(executable):
297 if executable.endswith(
'.exe'):
298 if os.path.exists(executable[:-4]):
299 return executable[:-4]
301 for d
in os.environ.get(
"PATH").split(os.pathsep):
302 fullpath = os.path.join(d, executable)
303 if os.path.exists(fullpath):
305 if executable.endswith(
'.exe'):
306 return which(executable[:-4])
310 np = os.path.normpath(os.path.expandvars(p))
311 if os.path.exists(np):
312 p = os.path.realpath(np)
329 _illegal_xml_chars_RE = re.compile(
u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')
332 "Return the hex string "
333 return "".join(
map(hexConvert,match.group()))
336 return hex(ord(char))
338 return _illegal_xml_chars_RE.sub(hexreplace, val)
341 """Filter out characters that are illegal in XML.
342 Looks for any character in val that is not allowed in XML
343 and replaces it with replacement ('?' by default).
346 return _illegal_xml_chars_RE.sub(replacement, val)
352 """Basic implementation of an option validator for Gaudi tests.
353 This implementation is based on the standard (LCG) validation functions
362 """Validate the output of the program.
364 'stdout' -- A string containing the data written to the standard output
367 'stderr' -- A string containing the data written to the standard error
370 'result' -- A 'Result' object. It may be used to annotate
371 the outcome according to the content of stderr.
373 returns -- A list of strings giving causes of failure."""
378 causes.append(self.
cause)
384 """Compare 's1' and 's2', ignoring line endings.
390 returns -- True if 's1' and 's2' are the same, ignoring
391 differences in line endings."""
397 to_ignore = re.compile(
r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*')
398 keep_line =
lambda l:
not to_ignore.match(l)
399 return filter(keep_line, s1.splitlines()) == filter(keep_line, s2.splitlines())
401 return s1.splitlines() == s2.splitlines()
404 """ Base class for a callable that takes a file and returns a modified
409 if hasattr(input,
"__iter__"):
413 lines = input.splitlines()
418 if l: output.append(l)
419 if mergeback: output =
'\n'.join(output)
443 if line.find(s) >= 0:
return None
445 if r.search(line):
return None
455 if self.
start in line:
458 elif self.
end in line:
467 when = re.compile(when)
470 if isinstance(rhs, RegexpReplacer):
472 res._operations = self.
_operations + rhs._operations
474 res = FilePreprocessor.__add__(self, rhs)
478 if w
is None or w.search(line):
479 line = o.sub(r, line)
484 normalizeDate =
RegexpReplacer(
"[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9] [0-9]{4}[-/][01][0-9][-/][0-3][0-9] *(CES?T)?",
485 "00:00:00 1970-01-01")
487 normalizeEOL.__processLine__ =
lambda line: str(line).rstrip() +
'\n'
491 skipEmptyLines.__processLine__ =
lambda line: (line.strip()
and line)
or None
502 line = line[:(pos+self.
siglen)]
503 lst = line[(pos+self.
siglen):].split()
505 line +=
" ".join(lst)
509 normalizeExamples = maskPointers + normalizeDate
512 (
"TIMER.TIMER",
r"\s+[+-]?[0-9]+[0-9.]*",
" 0"),
513 (
"release all pending",
r"^.*/([^/]*:.*)",
r"\1"),
514 (
"0x########",
r"\[.*/([^/]*.*)\]",
r"[\1]"),
515 (
"^#.*file",
r"file '.*[/\\]([^/\\]*)$",
r"file '\1"),
516 (
"^JobOptionsSvc.*options successfully read in from",
r"read in from .*[/\\]([^/\\]*)$",
r"file \1"),
518 (
None,
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}",
"00000000-0000-0000-0000-000000000000"),
520 (
"ServiceLocatorHelper::",
"ServiceLocatorHelper::(create|locate)Service",
"ServiceLocatorHelper::service"),
522 (
None,
r"e([-+])0([0-9][0-9])",
r"e\1\2"),
524 (
None,
r'Service reference count check:',
r'Looping over all active services...'),
526 (
None,
r"Property(.*)'ErrorCount':",
r"Property\1'ErrorCounter':"),
531 "JobOptionsSvc INFO # ",
532 "JobOptionsSvc WARNING # ",
535 "This machine has a speed",
538 "ToolSvc.Sequenc... INFO",
539 "DataListenerSvc INFO XML written to file:",
540 "[INFO]",
"[WARNING]",
541 "DEBUG No writable file catalog found which contains FID:",
543 "DEBUG Service base class initialized successfully",
544 "DEBUG Incident timing:",
545 "INFO 'CnvServices':[",
549 r"^JobOptionsSvc INFO *$",
551 r"(Always|SUCCESS)\s*(Root f|[^ ]* F)ile version:",
552 r"0x[0-9a-fA-F#]+ *Algorithm::sysInitialize\(\) *\[",
553 r"0x[0-9a-fA-F#]* *__gxx_personality_v0 *\[",
554 r"File '.*.xml' does not exist",
555 r"INFO Refer to dataset .* by its file ID:",
556 r"INFO Referring to dataset .* by its file ID:",
557 r"INFO Disconnect from dataset",
558 r"INFO Disconnected from dataset",
559 r"INFO Disconnected data IO:",
560 r"IncidentSvc\s*(DEBUG (Adding|Removing)|VERBOSE Calling)",
562 r"^StatusCodeSvc.*listing all unchecked return codes:",
563 r"^StatusCodeSvc\s*INFO\s*$",
564 r"Num\s*\|\s*Function\s*\|\s*Source Library",
567 r"ERROR Failed to modify file: .* Errno=2 No such file or directory",
569 r"^ +[0-9]+ \|.*ROOT",
570 r"^ +[0-9]+ \|.*\|.*Dict",
572 r"StatusCodeSvc.*all StatusCode instances where checked",
576 r"SUCCESS\s*Booked \d+ Histogram\(s\)",
583 r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*',
586 normalizeExamples = (lineSkipper + normalizeExamples + skipEmptyLines +
587 normalizeEOL +
LineSorter(
"Services to release : "))
590 def __init__(self, reffile, cause, result_key, preproc = normalizeExamples):
597 if os.path.isfile(self.
reffile):
598 orig = open(self.
reffile).xreadlines()
604 new = stdout.splitlines()
608 diffs = difflib.ndiff(orig,new,charjunk=difflib.IS_CHARACTER_JUNK)
609 filterdiffs =
map(
lambda x: x.strip(),filter(
lambda x: x[0] !=
" ",diffs))
612 result[self.
result_key] = result.Quote(
"\n".join(filterdiffs))
616 +) standard output of the test""")
617 causes.append(self.
cause)
624 def findReferenceBlock(reference, stdout, result, causes, signature_offset=0, signature=None,
627 Given a block of text, tries to find it in the output.
628 The block had to be identified by a signature line. By default, the first
629 line is used as signature, or the line pointed to by signature_offset. If
630 signature_offset points outside the block, a signature line can be passed as
631 signature argument. Note: if 'signature' is None (the default), a negative
632 signature_offset is interpreted as index in a list (e.g. -1 means the last
633 line), otherwise the it is interpreted as the number of lines before the
634 first one of the block the signature must appear.
635 The parameter 'id' allow to distinguish between different calls to this
636 function in the same validation code.
639 reflines = filter(
None,
map(
lambda s: s.rstrip(), reference.splitlines()))
641 raise RuntimeError(
"Empty (or null) reference")
643 outlines = filter(
None,
map(
lambda s: s.rstrip(), stdout.splitlines()))
645 res_field =
"GaudiTest.RefBlock"
647 res_field +=
"_%s" % id
649 if signature
is None:
650 if signature_offset < 0:
651 signature_offset = len(reference)+signature_offset
652 signature = reflines[signature_offset]
655 pos = outlines.index(signature)
656 outlines = outlines[pos-signature_offset:pos+len(reflines)-signature_offset]
657 if reflines != outlines:
658 msg =
"standard output"
660 if not msg
in causes:
662 result[res_field +
".observed"] = result.Quote(
"\n".join(outlines))
664 causes.append(
"missing signature")
665 result[res_field +
".signature"] = result.Quote(signature)
666 if len(reflines) > 1
or signature != reflines[0]:
667 result[res_field +
".expected"] = result.Quote(
"\n".join(reflines))
673 Count the number of messages with required severity (by default ERROR and FATAL)
674 and check if their numbers match the expected ones (0 by default).
675 The dictionary "expected" can be used to tune the number of errors and fatals
676 allowed, or to limit the number of expected warnings etc.
678 stdout = kwargs[
"stdout"]
679 result = kwargs[
"result"]
680 causes = kwargs[
"causes"]
687 outlines = stdout.splitlines()
688 from math
import log10
689 fmt =
"%%%dd - %%s" % (int(log10(len(outlines))+1))
695 if len(words) >= 2
and words[1]
in errors:
696 errors[words[1]].append(fmt%(linecount,l.rstrip()))
699 if len(errors[e]) != expected[e]:
700 causes.append(
'%s(%d)'%(e,len(errors[e])))
701 result[
"GaudiTest.lines.%s"%e] = result.Quote(
'\n'.join(errors[e]))
702 result[
"GaudiTest.lines.%s.expected#"%e] = result.Quote(str(expected[e]))
709 Parse the TTree summary table in lines, starting from pos.
710 Returns a tuple with the dictionary with the digested informations and the
711 position of the first line after the summary.
717 splitcols =
lambda l: [ f.strip()
for f
in l.strip(
"*\n").split(
':',2) ]
720 cols = splitcols(ll[0])
721 r[
"Name"], r[
"Title"] = cols[1:]
723 cols = splitcols(ll[1])
724 r[
"Entries"] = int(cols[1])
726 sizes = cols[2].split()
727 r[
"Total size"] = int(sizes[2])
728 if sizes[-1] ==
"memory":
731 r[
"File size"] = int(sizes[-1])
733 cols = splitcols(ll[2])
734 sizes = cols[2].split()
735 if cols[0] ==
"Baskets":
736 r[
"Baskets"] = int(cols[1])
737 r[
"Basket size"] = int(sizes[2])
738 r[
"Compression"] = float(sizes[-1])
741 if i < (count - 3)
and lines[i].startswith(
"*Tree"):
742 result = parseblock(lines[i:i+3])
743 result[
"Branches"] = {}
745 while i < (count - 3)
and lines[i].startswith(
"*Br"):
746 if i < (count - 2)
and lines[i].startswith(
"*Branch "):
750 branch = parseblock(lines[i:i+3])
751 result[
"Branches"][branch[
"Name"]] = branch
758 Scan stdout to find ROOT TTree summaries and digest them.
760 stars = re.compile(
r"^\*+$")
761 outlines = stdout.splitlines()
762 nlines = len(outlines)
768 while i < nlines
and not stars.match(outlines[i]):
773 trees[tree[
"Name"]] = tree
779 Check that all the keys in reference are in to_check too, with the same value.
780 If the value is a dict, the function is called recursively. to_check can
781 contain more keys than reference, that will not be tested.
782 The function returns at the first difference found.
787 ignore_re = re.compile(ignore)
788 keys = [ key
for key
in reference
if not ignore_re.match(key) ]
790 keys = reference.keys()
794 if (
type(reference[k])
is dict)
and (
type(to_check[k])
is dict):
796 failed = fail_keys =
cmpTreesDicts(reference[k], to_check[k], ignore)
799 failed = to_check[k] != reference[k]
804 fail_keys.insert(0, k)
814 if c
is None or r
is None:
816 return (fail_path, r, c)
819 h_count_re = re.compile(
r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)")
823 Extract the histograms infos from the lines starting at pos.
824 Returns the position of the first line after the summary block.
827 h_table_head = re.compile(
r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"')
828 h_short_summ = re.compile(
r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
833 m = h_count_re.search(lines[pos])
834 name = m.group(1).strip()
835 total = int(m.group(2))
837 for k, v
in [ x.split(
"=")
for x
in m.group(3).split() ]:
840 header[
"Total"] = total
844 m = h_table_head.search(lines[pos])
847 t = t.replace(
" profile",
"Prof")
854 if l.startswith(
" | ID"):
856 titles = [ x.strip()
for x
in l.split(
"|")][1:]
858 while pos < nlines
and lines[pos].startswith(
" |"):
860 values = [ x.strip()
for x
in l.split(
"|")][1:]
862 for i
in range(len(titles)):
863 hcont[titles[i]] = values[i]
864 cont[hcont[
"ID"]] = hcont
866 elif l.startswith(
" ID="):
867 while pos < nlines
and lines[pos].startswith(
" ID="):
868 values = [ x.strip()
for x
in h_short_summ.search(lines[pos]).groups() ]
869 cont[values[0]] = values
872 raise RuntimeError(
"Cannot understand line %d: '%s'" % (pos, l))
876 summ[d][
"header"] = header
881 summ[name] = {
"header": header}
886 Scan stdout to find ROOT TTree summaries and digest them.
888 outlines = stdout.splitlines()
889 nlines = len(outlines) - 1
897 match = h_count_re.search(outlines[pos])
898 while pos < nlines
and not match:
900 match = h_count_re.search(outlines[pos])
903 summaries.update(summ)
908 """Create a new 'Filter'.
910 'input' -- The string containing the input to provide to the
913 'timeout' -- As for 'TimeoutExecutable.__init__'."""
915 super(GaudiFilterExecutable, self).
__init__(input, timeout)
922 tmpf = tempfile.mkstemp()
927 """Copied from TimeoutExecutable to allow the re-implementation of
930 if sys.platform ==
"win32":
941 """Code copied from both FilterExecutable and TimeoutExecutable.
945 self._ClosePipeEnd(self._stdin_pipe[0])
946 if self._stdout_pipe:
947 self._ClosePipeEnd(self._stdout_pipe[1])
948 if self._stderr_pipe:
949 self._ClosePipeEnd(self._stderr_pipe[1])
957 super(qm.executable.TimeoutExecutable, self).
_HandleChild()
964 child_pid = self._GetChildPID()
966 os.setpgid(child_pid, child_pid)
994 os.setpgid(0, child_pid)
1003 max_fds = os.sysconf(
"SC_OPEN_MAX")
1006 for fd
in xrange(max_fds):
1017 if sys.platform ==
"linux2":
1019 os.path.join(
"/proc", str(child_pid),
"exe"),
1021 "-batch",
"-n",
"-x",
1022 "'%s'" % os.path.join(os.path.dirname(__file__),
"stack-trace.gdb")]
1025 o = os.popen(
" ".join(cmd)).read()
1030 os.kill(0, signal.SIGKILL)
1033 select.select ([], [], [])
1038 elif self.
__timeout >= 0
and sys.platform ==
"win32":
1041 self.__monitor_thread.start()
1043 if sys.platform ==
"win32":
1046 """Code copied from FilterExecutable.
1047 Kill the child if the timeout expires.
1049 This function is run in the monitoring thread."""
1057 result = win32event.WaitForSingleObject(self._GetChildPID(),
1060 if result == win32con.WAIT_TIMEOUT:
1067 """Standard Gaudi test.
1070 qm.fields.TextField(
1074 description=
"""The path to the program.
1076 This field indicates the path to the program. If it is not
1077 an absolute path, the value of the 'PATH' environment
1078 variable will be used to search for the program.
1079 If not specified, $GAUDIEXE or Gaudi.exe are used.
1082 qm.fields.SetField(qm.fields.TextField(
1084 title=
"Argument List",
1085 description=
"""The command-line arguments.
1087 If this field is left blank, the program is run without any
1090 Use this field to specify the option files.
1092 An implicit 0th argument (the path to the program) is added
1095 qm.fields.TextField(
1098 description=
"""Options to be passed to the application.
1100 This field allows to pass a list of options to the main program
1101 without the need of a separate option file.
1103 The content of the field is written to a temporary file which name
1104 is passed the the application as last argument (appended to the
1105 field "Argument List".
1111 qm.fields.TextField(
1113 title=
"Working Directory",
1114 description=
"""Path to the working directory.
1116 If this field is left blank, the program will be run from the qmtest
1117 directory, otherwise from the directory specified.""",
1120 qm.fields.TextField(
1122 title=
"Reference Output",
1123 description=
"""Path to the file containing the reference output.
1125 If this field is left blank, any standard output will be considered
1128 If the reference file is specified, any output on standard error is
1131 qm.fields.TextField(
1132 name=
"error_reference",
1133 title=
"Reference for standard error",
1134 description=
"""Path to the file containing the reference for the standard error.
1136 If this field is left blank, any standard output will be considered
1139 If the reference file is specified, any output on standard error is
1142 qm.fields.SetField(qm.fields.TextField(
1143 name =
"unsupported_platforms",
1144 title =
"Unsupported Platforms",
1145 description =
"""Platform on which the test must not be run.
1147 List of regular expressions identifying the platforms on which the
1148 test is not run and the result is set to UNTESTED."""
1151 qm.fields.TextField(
1153 title =
"Validator",
1154 description =
"""Function to validate the output of the test.
1156 If defined, the function is used to validate the products of the
1158 The function is called passing as arguments:
1159 self: the test class instance
1160 stdout: the standard output of the executed test
1161 stderr: the standard error of the executed test
1162 result: the Result objects to fill with messages
1163 The function must return a list of causes for the failure.
1164 If specified, overrides standard output, standard error and
1172 qm.fields.BooleanField(
1173 name =
"use_temp_dir",
1174 title =
"Use temporary directory",
1175 description =
"""Use temporary directory.
1177 If set to true, use a temporary directory as working directory.
1179 default_value=
"false"
1182 qm.fields.IntegerField(
1184 title =
"Expected signal",
1185 description =
"""Expect termination by signal.""",
1192 unsupported = [ re.compile(x)
1193 for x
in [ str(y).strip()
1194 for y
in self.unsupported_platforms ]
1197 for p_re
in unsupported:
1198 if p_re.search(platform):
1199 result.SetOutcome(result.UNTESTED)
1200 result[result.CAUSE] =
'Platform not supported.'
1206 Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.
1210 if "CMTCONFIG" in os.environ:
1211 arch = os.environ[
"CMTCONFIG"]
1212 elif "SCRAM_ARCH" in os.environ:
1213 arch = os.environ[
"SCRAM_ARCH"]
1218 Return True if the current platform is Windows.
1220 This function was needed because of the change in the CMTCONFIG format,
1221 from win32_vc71_dbg to i686-winxp-vc9-dbg.
1224 return "winxp" in platform
or platform.startswith(
"win")
1232 platformSplit =
lambda p: set(p.split(
'-' in p
and '-' or '_'))
1234 reference = os.path.normpath(os.path.expandvars(reffile))
1236 spec_ref = reference[:-3] + self.
GetPlatform()[0:3] + reference[-3:]
1237 if os.path.isfile(spec_ref):
1238 reference = spec_ref
1241 dirname, basename = os.path.split(reference)
1242 if not dirname: dirname =
'.'
1243 head = basename +
"."
1244 head_len = len(head)
1246 if 'do0' in platform:
1249 for f
in os.listdir(dirname):
1250 if f.startswith(head):
1251 req_plat = platformSplit(f[head_len:])
1252 if platform.issuperset(req_plat):
1253 candidates.append( (len(req_plat), f) )
1258 reference = os.path.join(dirname, candidates[-1][1])
1263 ignore =
r"Basket|.*size|Compression"):
1265 Compare the TTree summaries in stdout with the ones in trees_dict or in
1266 the reference file. By default ignore the size, compression and basket
1268 The presence of TTree summaries when none is expected is not a failure.
1270 if trees_dict
is None:
1273 if reference
and os.path.isfile(reference):
1278 from pprint
import PrettyPrinter
1279 pp = PrettyPrinter()
1281 result[
"GaudiTest.TTrees.expected"] = result.Quote(pp.pformat(trees_dict))
1283 result[
"GaudiTest.TTrees.ignore"] = result.Quote(ignore)
1288 causes.append(
"trees summaries")
1290 result[
"GaudiTest.TTrees.failure_on"] = result.Quote(msg)
1291 result[
"GaudiTest.TTrees.found"] = result.Quote(pp.pformat(trees))
1299 Compare the TTree summaries in stdout with the ones in trees_dict or in
1300 the reference file. By default ignore the size, compression and basket
1302 The presence of TTree summaries when none is expected is not a failure.
1307 if reference
and os.path.isfile(reference):
1312 from pprint
import PrettyPrinter
1313 pp = PrettyPrinter()
1315 result[
"GaudiTest.Histos.expected"] = result.Quote(pp.pformat(dict))
1317 result[
"GaudiTest.Histos.ignore"] = result.Quote(ignore)
1322 causes.append(
"histos summaries")
1324 result[
"GaudiTest.Histos.failure_on"] = result.Quote(msg)
1325 result[
"GaudiTest.Histos.found"] = result.Quote(pp.pformat(histos))
1331 Default validation action: compare standard output and error to the
1336 preproc = normalizeExamples
1340 if reference
and os.path.isfile(reference):
1341 result[
"GaudiTest.output_reference"] = reference
1344 "GaudiTest.output_diff",
1345 preproc = preproc)(stdout, result)
1353 newref = open(reference +
".new",
"w")
1355 for l
in stdout.splitlines():
1356 newref.write(l.rstrip() +
'\n')
1366 if reference
and os.path.isfile(reference):
1367 result[
"GaudiTest.error_reference"] = reference
1370 "GaudiTest.error_diff",
1371 preproc = preproc)(stderr, result)
1374 newref = open(reference +
".new",
"w")
1376 for l
in stderr.splitlines():
1377 newref.write(l.rstrip() +
'\n')
1382 "ExecTest.expected_stderr")(stderr, result)
1389 if self.validator.strip() !=
"":
1390 class CallWrapper(object):
1392 Small wrapper class to dynamically bind some default arguments
1395 def __init__(self, callable, extra_args = {}):
1399 from inspect
import getargspec
1405 def __call__(self, *args, **kwargs):
1409 kwargs = dict(kwargs)
1413 if a
not in positional
and a
not in kwargs:
1415 return apply(self.
callable, args, kwargs)
1417 exported_symbols = {
"self":self,
1422 "findReferenceBlock":
1423 CallWrapper(findReferenceBlock, {
"stdout":stdout,
1426 "validateWithReference":
1432 CallWrapper(countErrorLines, {
"stdout":stdout,
1435 "checkTTreesSummaries":
1439 "checkHistosSummaries":
1445 exec self.validator
in globals(), exported_symbols
1453 Add the content of the environment to the result object.
1455 Copied from the QMTest class of COOL.
1457 vars = os.environ.keys()
1459 result[
'GaudiTest.environment'] = \
1460 result.Quote(
'\n'.join([
"%s=%s"%(v,os.environ[v])
for v
in vars]))
1462 def Run(self, context, result):
1465 'context' -- A 'Context' giving run-time parameters to the
1468 'result' -- A 'Result' object. The outcome will be
1469 'Result.PASS' when this method is called. The 'result' may be
1470 modified by this method to indicate outcomes other than
1471 'Result.PASS' or to add annotations."""
1480 elif "GAUDIEXE" in os.environ:
1481 prog = os.environ[
"GAUDIEXE"]
1486 dummy, prog_ext = os.path.splitext(prog)
1487 if prog_ext
not in [
".exe",
".py",
".bat" ]
and self.
isWinPlatform():
1491 prog =
which(prog)
or prog
1494 args =
map(rationalizepath, self.args)
1501 if self.options.strip():
1503 if re.search(
r"from\s+Gaudi.Configuration\s+import\s+\*|from\s+Configurables\s+import", self.options):
1506 tmpfile.writelines(
"\n".join(self.options.splitlines()))
1508 args.append(tmpfile.name)
1509 result[
"GaudiTest.options"] = result.Quote(self.options)
1512 if prog_ext ==
".py":
1515 prog =
which(
"python.exe")
or "python.exe"
1517 prog =
which(
"python")
or "python"
1520 origdir = os.getcwd()
1522 os.chdir(str(os.path.normpath(os.path.expandvars(self.workdir))))
1524 if "QMTEST_TMPDIR" in os.environ:
1525 qmtest_tmpdir = os.environ[
"QMTEST_TMPDIR"]
1526 if not os.path.exists(qmtest_tmpdir):
1527 os.makedirs(qmtest_tmpdir)
1528 os.chdir(qmtest_tmpdir)
1529 elif "qmtest.tmpdir" in context:
1530 os.chdir(context[
"qmtest.tmpdir"])
1532 if "QMTEST_IGNORE_TIMEOUT" not in os.environ:
1545 if result.GetOutcome()
not in [ result.PASS ]:
1552 """Run the 'program'.
1554 'program' -- The path to the program to run.
1556 'arguments' -- A list of the arguments to the program. This
1557 list must contain a first argument corresponding to 'argv[0]'.
1559 'context' -- A 'Context' giving run-time parameters to the
1562 'result' -- A 'Result' object. The outcome will be
1563 'Result.PASS' when this method is called. The 'result' may be
1564 modified by this method to indicate outcomes other than
1565 'Result.PASS' or to add annotations.
1567 @attention: This method has been copied from command.ExecTestBase
1568 (QMTest 2.3.0) and modified to keep stdout and stderr
1569 for tests that have been terminated by a signal.
1570 (Fundamental for debugging in the Application Area)
1574 environment = self.MakeEnvironment(context)
1576 if "slc6" in environment.get(
'CMTCONFIG',
''):
1577 environment[
'TERM'] =
'dumb'
1590 exit_status = e.Run(arguments, environment, path = program)
1592 if e.stack_trace_file
and os.path.exists(e.stack_trace_file):
1593 stack_trace = open(e.stack_trace_file).read()
1594 os.remove(e.stack_trace_file)
1598 result[
"ExecTest.stack_trace"] = result.Quote(stack_trace)
1601 if (sys.platform ==
"win32" or os.WIFEXITED(exit_status)
1607 if self.exit_code
is None:
1609 elif sys.platform ==
"win32":
1610 exit_code = exit_status
1612 exit_code = os.WEXITSTATUS(exit_status)
1617 result[
"ExecTest.exit_code"] = str(exit_code)
1618 result[
"ExecTest.stdout"] = result.Quote(stdout)
1619 result[
"ExecTest.stderr"] = result.Quote(stderr)
1621 if exit_code != self.exit_code:
1622 causes.append(
"exit_code")
1623 result[
"ExecTest.expected_exit_code"] \
1624 = str(self.exit_code)
1629 result.Fail(
"Unexpected %s." % string.join(causes,
", "))
1630 elif os.WIFSIGNALED(exit_status):
1633 signal_number = str(os.WTERMSIG(exit_status))
1635 result.Fail(
"Program terminated by signal.")
1639 result.Fail(
"Exceeded time limit (%ds), terminated." % timeout)
1640 result[
"ExecTest.signal_number"] = signal_number
1641 result[
"ExecTest.stdout"] = result.Quote(e.stdout)
1642 result[
"ExecTest.stderr"] = result.Quote(e.stderr)
1644 result[
"ExecTest.expected_signal_number"] = str(self.
signal)
1645 elif os.WIFSTOPPED(exit_status):
1648 signal_number = str(os.WSTOPSIG(exit_status))
1650 result.Fail(
"Program stopped by signal.")
1654 result.Fail(
"Exceeded time limit (%ds), stopped." % timeout)
1655 result[
"ExecTest.signal_number"] = signal_number
1656 result[
"ExecTest.stdout"] = result.Quote(e.stdout)
1657 result[
"ExecTest.stderr"] = result.Quote(e.stderr)
1661 result.Fail(
"Program did not terminate normally.")
1667 result[
"ExecTest.stdout"] = result[
"ExecTest.stdout"].replace(esc,repr_esc)
1672 if 'NO_ECLIPSE_LAUNCHERS' in os.environ:
1677 projbasedir = os.path.normpath(destdir)
1678 while not os.path.exists(os.path.join(projbasedir,
".project")):
1679 oldprojdir = projbasedir
1680 projbasedir = os.path.normpath(os.path.join(projbasedir, os.pardir))
1683 if oldprojdir == projbasedir:
1687 if not os.path.exists(destdir):
1688 os.makedirs(destdir)
1690 from xml.etree
import ElementTree
as ET
1691 t = ET.parse(os.path.join(projbasedir,
".project"))
1692 projectName = t.find(
"name").text
1695 destfile =
"%s.launch" % self._Runnable__id
1697 destfile = os.path.join(destdir, destfile)
1699 if self.options.strip():
1703 tempfile = args.pop()
1704 optsfile = destfile + os.path.splitext(tempfile)[1]
1705 shutil.copyfile(tempfile, optsfile)
1706 args.append(optsfile)
1709 from xml.sax.saxutils
import quoteattr
1713 data[
"environment"] =
"\n".join([
'<mapEntry key=%s value=%s/>' % (quoteattr(k), quoteattr(v))
1714 for k, v
in os.environ.iteritems()
1715 if k
not in (
'MAKEOVERRIDES',
'MAKEFLAGS',
'MAKELEVEL')])
1717 data[
"exec"] =
which(prog)
or prog
1718 if os.path.basename(data[
"exec"]).lower().startswith(
"python"):
1719 data[
"stopAtMain"] =
"false"
1721 data[
"stopAtMain"] =
"true"
1723 data[
"args"] =
" ".join(
map(rationalizepath, args))
1725 data[
"args"] =
" ".join([
"/debugexe"] +
map(rationalizepath, [data[
"exec"]] + args))
1726 data[
"exec"] =
which(
"vcexpress.exe")
1729 data[
"workdir"] = os.getcwd()
1733 data[
"workdir"] = destdir
1735 data[
"project"] = projectName.strip()
1738 xml_template =
u"""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1739 <launchConfiguration type="org.eclipse.cdt.launch.applicationLaunchType">
1740 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.AUTO_SOLIB" value="true"/>
1741 <listAttribute key="org.eclipse.cdt.debug.mi.core.AUTO_SOLIB_LIST"/>
1742 <stringAttribute key="org.eclipse.cdt.debug.mi.core.DEBUG_NAME" value="gdb"/>
1743 <stringAttribute key="org.eclipse.cdt.debug.mi.core.GDB_INIT" value=".gdbinit"/>
1744 <listAttribute key="org.eclipse.cdt.debug.mi.core.SOLIB_PATH"/>
1745 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.STOP_ON_SOLIB_EVENTS" value="false"/>
1746 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.breakpointsFullPath" value="false"/>
1747 <stringAttribute key="org.eclipse.cdt.debug.mi.core.commandFactory" value="org.eclipse.cdt.debug.mi.core.standardCommandFactory"/>
1748 <stringAttribute key="org.eclipse.cdt.debug.mi.core.protocol" value="mi"/>
1749 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.verboseMode" value="false"/>
1750 <intAttribute key="org.eclipse.cdt.launch.ATTR_BUILD_BEFORE_LAUNCH_ATTR" value="0"/>
1751 <stringAttribute key="org.eclipse.cdt.launch.COREFILE_PATH" value=""/>
1752 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_ID" value="org.eclipse.cdt.debug.mi.core.CDebuggerNew"/>
1753 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_REGISTER_GROUPS" value=""/>
1754 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_START_MODE" value="run"/>
1755 <booleanAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN" value="%(stopAtMain)s"/>
1756 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN_SYMBOL" value="main"/>
1757 <booleanAttribute key="org.eclipse.cdt.launch.ENABLE_REGISTER_BOOKKEEPING" value="false"/>
1758 <booleanAttribute key="org.eclipse.cdt.launch.ENABLE_VARIABLE_BOOKKEEPING" value="false"/>
1759 <stringAttribute key="org.eclipse.cdt.launch.FORMAT" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?><contentList/>"/>
1760 <stringAttribute key="org.eclipse.cdt.launch.GLOBAL_VARIABLES" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <globalVariableList/> "/>
1761 <stringAttribute key="org.eclipse.cdt.launch.MEMORY_BLOCKS" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <memoryBlockExpressionList/> "/>
1762 <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_ARGUMENTS" value="%(args)s"/>
1763 <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="%(exec)s"/>
1764 <stringAttribute key="org.eclipse.cdt.launch.PROJECT_ATTR" value="%(project)s"/>
1765 <stringAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_ID_ATTR" value=""/>
1766 <stringAttribute key="org.eclipse.cdt.launch.WORKING_DIRECTORY" value="%(workdir)s"/>
1767 <booleanAttribute key="org.eclipse.cdt.launch.ui.ApplicationCDebuggerTab.DEFAULTS_SET" value="true"/>
1768 <booleanAttribute key="org.eclipse.cdt.launch.use_terminal" value="true"/>
1769 <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
1770 <listEntry value="/%(project)s"/>
1772 <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
1773 <listEntry value="4"/>
1775 <booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="false"/>
1776 <mapAttribute key="org.eclipse.debug.core.environmentVariables">
1779 <mapAttribute key="org.eclipse.debug.core.preferred_launchers">
1780 <mapEntry key="[debug]" value="org.eclipse.cdt.cdi.launch.localCLaunch"/>
1782 <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
1783 <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
1785 </launchConfiguration>
1790 data[k] = codecs.decode(data[k],
'utf-8')
1791 xml = xml_template % data
1794 codecs.open(destfile,
"w", encoding=
'utf-8').write(xml)
1796 print 'WARNING: problem generating Eclipse launcher'
1803 import simplejson
as json
1806 """An 'HTMLResultStream' writes its output to a set of HTML files.
1808 The argument 'dir' is used to select the destination directory for the HTML
1810 The destination directory may already contain the report from a previous run
1811 (for example of a different package), in which case it will be extended to
1812 include the new data.
1815 qm.fields.TextField(
1817 title =
"Destination Directory",
1818 description =
"""The name of the directory.
1820 All results will be written to the directory indicated.""",
1822 default_value =
""),
1826 """Prepare the destination directory.
1828 Creates the destination directory and store in it some preliminary
1829 annotations and the static files found in the template directory
1832 ResultStream.__init__(self, arguments, **args)
1837 templateDir = os.path.join(os.path.dirname(__file__),
"html_report")
1838 if not os.path.isdir(self.dir):
1839 os.makedirs(self.dir)
1841 for f
in os.listdir(templateDir):
1842 src = os.path.join(templateDir, f)
1843 dst = os.path.join(self.dir, f)
1844 if not os.path.isdir(src)
and not os.path.exists(dst):
1845 shutil.copy(src, dst)
1847 if "CMTCONFIG" in os.environ:
1853 """Helper function to extend the global summary file in the destination
1860 ids = set([ i[
"id"]
for i
in self.
_summary ])
1861 newSummary = [ i
for i
in oldSummary
if i[
"id"]
not in ids ]
1867 """Writes the annotation to the annotation file.
1868 If the key is already present with a different value, the value becomes
1869 a list and the new value is appended to it, except for start_time and
1878 key, value =
map(str, [key, value])
1879 if key ==
"qmtest.run.start_time":
1884 if key
not in annotations:
1885 annotations[key] = value
1886 if "qmtest.run.end_time" in annotations:
1887 del annotations[
"qmtest.run.end_time"]
1890 if key
in annotations:
1891 old = annotations[key]
1892 if type(old)
is list:
1893 if value
not in old:
1894 annotations[key].append(value)
1896 annotations[key] = [old, value]
1898 annotations[key] = value
1904 """Prepare the test result directory in the destination directory storing
1905 into it the result fields.
1906 A summary of the test result is stored both in a file in the test directory
1907 and in the global summary file.
1910 summary[
"id"] = result.GetId()
1911 summary[
"outcome"] = result.GetOutcome()
1912 summary[
"cause"] = result.GetCause()
1913 summary[
"fields"] = result.keys()
1914 summary[
"fields"].sort()
1917 for f
in [
"id",
"outcome",
"cause"]:
1918 summary[f] = str(summary[f])
1919 summary[
"fields"] =
map(str, summary[
"fields"])
1921 self._summary.append(summary)
1927 testOutDir = os.path.join(self.dir, summary[
"id"])
1928 if not os.path.isdir(testOutDir):
1929 os.makedirs(testOutDir)
1930 json.dump(summary, open(os.path.join(testOutDir,
"summary.json"),
"w"),
1932 for f
in summary[
"fields"]:
1933 open(os.path.join(testOutDir, f),
"w").write(result[f])
1944 class XMLResultStream(ResultStream):
1945 """An 'XMLResultStream' writes its output to a Ctest XML file.
1947 The argument 'dir' is used to select the destination file for the XML
1949 The destination directory may already contain the report from a previous run
1950 (for example of a different package), in which case it will be overrided to
1954 qm.fields.TextField(
1956 title =
"Destination Directory",
1957 description =
"""The name of the directory.
1959 All results will be written to the directory indicated.""",
1961 default_value =
""),
1962 qm.fields.TextField(
1964 title =
"Output File Prefix",
1965 description =
"""The output file name will be the specified prefix
1966 followed by 'Test.xml' (CTest convention).""",
1968 default_value =
""),
1972 """Prepare the destination directory.
1974 Creates the destination directory and store in it some preliminary
1977 ResultStream.__init__(self, arguments, **args)
1979 self.
_xmlFile = os.path.join(self.dir, self.prefix +
'Test.xml')
1985 if not os.path.isfile(self.
_xmlFile):
1987 if not os.path.exists(os.path.dirname(self.
_xmlFile)):
1988 os.makedirs(os.path.dirname(self.
_xmlFile))
1990 newdataset = ET.Element(
"newdataset")
1996 newdataset = self._tree.getroot()
2003 for site
in newdataset.getiterator() :
2004 if site.get(
"OSPlatform") == os.uname()[4]:
2014 import multiprocessing
2016 "BuildName" : os.getenv(
"CMTCONFIG"),
2017 "Name" : os.uname()[1] ,
2018 "Generator" :
"QMTest "+qm.version ,
2019 "OSName" : os.uname()[0] ,
2020 "Hostname" : socket.gethostname() ,
2021 "OSRelease" : os.uname()[2] ,
2022 "OSVersion" :os.uname()[3] ,
2023 "OSPlatform" :os.uname()[4] ,
2024 "Is64Bits" :
"unknown" ,
2025 "VendorString" :
"unknown" ,
2026 "VendorID" :
"unknown" ,
2027 "FamilyID" :
"unknown" ,
2028 "ModelID" :
"unknown" ,
2029 "ProcessorCacheSize" :
"unknown" ,
2030 "NumberOfLogicalCPU" : str(multiprocessing.cpu_count()) ,
2031 "NumberOfPhysicalCPU" :
"0" ,
2032 "TotalVirtualMemory" :
"0" ,
2033 "TotalPhysicalMemory" :
"0" ,
2034 "LogicalProcessorsPerPhysical" :
"0" ,
2035 "ProcessorClockFrequency" :
"0" ,
2037 self.
_site = ET.SubElement(newdataset,
"Site", attrib)
2060 self.
_Testing = self._site.find(
"Testing")
2063 self.
_TestList = self._Testing.find(
"TestList")
2069 # Add some non-QMTest attributes
2070 if "CMTCONFIG" in os.environ:
2071 self.WriteAnnotation("cmt.cmtconfig", os.environ["CMTCONFIG"])
2073 self.WriteAnnotation("hostname", socket.gethostname())
2078 if key ==
"qmtest.run.start_time":
2079 if self._site.get(
"qmtest.run.start_time")
is not None :
2081 self._site.set(str(key),str(value))
2083 """Prepare the test result directory in the destination directory storing
2084 into it the result fields.
2085 A summary of the test result is stored both in a file in the test directory
2086 and in the global summary file.
2089 summary[
"id"] = result.GetId()
2090 summary[
"outcome"] = result.GetOutcome()
2091 summary[
"cause"] = result.GetCause()
2092 summary[
"fields"] = result.keys()
2093 summary[
"fields"].sort()
2097 for f
in [
"id",
"outcome",
"cause"]:
2098 summary[f] = str(summary[f])
2099 summary[
"fields"] =
map(str, summary[
"fields"])
2105 if "qmtest.start_time" in summary[
"fields"]:
2106 haveStartDate =
True
2108 haveStartDate =
False
2109 if "qmtest.end_time" in summary[
"fields"]:
2116 self.
_startTime = calendar.timegm(time.strptime(result[
"qmtest.start_time"],
"%Y-%m-%dT%H:%M:%SZ"))
2117 if self._StartTestTime.text
is None:
2118 self._StartDateTime.text = time.strftime(
"%b %d %H:%M %Z", time.localtime(self.
_startTime))
2119 self._StartTestTime.text = str(self.
_startTime)
2120 self._site.set(
"BuildStamp" , result[
"qmtest.start_time"] )
2124 self.
_endTime = calendar.timegm(time.strptime(result[
"qmtest.end_time"],
"%Y-%m-%dT%H:%M:%SZ"))
2128 tl = ET.Element(
"Test")
2129 tl.text = summary[
"id"]
2130 self._TestList.insert(0,tl)
2133 Test = ET.Element(
"Test")
2134 if summary[
"outcome"] ==
"PASS":
2135 Test.set(
"Status",
"passed")
2136 elif summary[
"outcome"] ==
"FAIL":
2137 Test.set(
"Status",
"failed")
2138 elif summary[
"outcome"] ==
"SKIPPED" or summary[
"outcome"] ==
"UNTESTED":
2139 Test.set(
"Status",
"skipped")
2140 elif summary[
"outcome"] ==
"ERROR":
2141 Test.set(
"Status",
"failed")
2142 Name = ET.SubElement(Test,
"Name",)
2143 Name.text = summary[
"id"]
2144 Results = ET.SubElement(Test,
"Results")
2147 self._Testing.insert(3,Test)
2149 if haveStartDate
and haveEndDate:
2152 testduration = str(delta)
2153 Testduration= ET.SubElement(Results,
"NamedMeasurement")
2154 Testduration.set(
"name",
"Execution Time")
2155 Testduration.set(
"type",
"numeric/float" )
2156 value = ET.SubElement(Testduration,
"Value")
2157 value.text = testduration
2160 for n
in (
"qmtest.end_time",
"qmtest.start_time",
"qmtest.cause",
"ExecTest.stdout"):
2161 if n
in summary[
"fields"]:
2162 summary[
"fields"].
remove(n)
2166 if "ExecTest.exit_code" in summary[
"fields"] :
2167 summary[
"fields"].
remove(
"ExecTest.exit_code")
2168 ExitCode= ET.SubElement(Results,
"NamedMeasurement")
2169 ExitCode.set(
"name",
"exit_code")
2170 ExitCode.set(
"type",
"numeric/integer" )
2171 value = ET.SubElement(ExitCode,
"Value")
2174 TestStartTime= ET.SubElement(Results,
"NamedMeasurement")
2175 TestStartTime.set(
"name",
"Start_Time")
2176 TestStartTime.set(
"type",
"String" )
2177 value = ET.SubElement(TestStartTime,
"Value")
2183 TestEndTime= ET.SubElement(Results,
"NamedMeasurement")
2184 TestEndTime.set(
"name",
"End_Time")
2185 TestEndTime.set(
"type",
"String" )
2186 value = ET.SubElement(TestEndTime,
"Value")
2192 if summary[
"cause"]:
2193 FailureCause= ET.SubElement(Results,
"NamedMeasurement")
2194 FailureCause.set(
"name",
"Cause")
2195 FailureCause.set(
"type",
"String" )
2196 value = ET.SubElement(FailureCause,
"Value")
2201 for field
in summary[
"fields"] :
2202 fields[field] = ET.SubElement(Results,
"NamedMeasurement")
2203 fields[field].set(
"type",
"String")
2204 fields[field].set(
"name",field)
2205 value = ET.SubElement(fields[field],
"Value")
2207 if "<pre>" in result[field][0:6] :
2213 if result.has_key(
"ExecTest.stdout" ) :
2214 Measurement = ET.SubElement(Results,
"Measurement")
2215 value = ET.SubElement(Measurement,
"Value")
2216 if "<pre>" in result[
"ExecTest.stdout"][0:6] :
2223 self._tree.write(self.
_xmlFile,
"utf-8")
2229 self._EndTestTime.text = str(self.
_endTime)
2230 self._EndDateTime.text = time.strftime(
"%b %d %H:%M %Z", time.localtime(self.
_endTime))
2237 self._ElapsedMinutes.text = str(delta/60)
2240 self._tree.write(self.
_xmlFile,
"utf-8")
def __init__(self, ref, cause, result_key)
def parseHistosSummary(lines, pos)
def __init__(self, arguments=None, args)
def __init__(self, signature)
def PlatformIsNotSupported(self, context, result)
def __processLine__(self, line)
def escape_xml_illegal_chars
def DumpEnvironment(self, result)
def __delitem__(self, key)
def _HandleChild(self)
Needs to replace the ones from RedirectedExecutable and TimeoutExecutable.
def __contains__(self, key)
def _expandReferenceFileName(self, reffile)
def WriteAnnotation(self, key, value)
def __setitem__(self, key, value)
def which(executable)
Locates an executable in the executables path ($PATH) and returns the full path to it...
def RunProgram(self, program, arguments, context, result)
def __processLine__(self, line)
__monitor_thread
This is the interesting part: dump the stack trace to a file.
def __UseSeparateProcessGroupForChild(self)
def WriteResult(self, result)
_EndDateTime
End time elements.
struct GAUDI_API map
Parametrisation class for map-like implementation.
def convert_xml_illegal_chars(val)
def _parseTTreeSummary(lines, pos)
def __call__(self, stdout, result)
def gen_script(self, shell_type)
def getCmpFailingValues(reference, to_check, fail_path)
def Run(self, context, result)
def __processLine__(self, line)
Output Validation Classes.
def __processLine__(self, line)
def total_seconds_replacement(timedelta)
def _run_cmt(self, command, args)
def WriteAnnotation(self, key, value)
def ValidateWithReference
def __call__(self, input)
def ROOT6WorkAroundEnabled
def __getattr__(self, attr)
def findHistosSummaries(stdout)
def ValidateOutput(self, stdout, stderr, result)
def __getitem__(self, key)
def __processLine__(self, line)
def findTTreeSummaries(stdout)
def __init__(self, arguments=None, args)
def __getattr__(self, attr)
def __init__(self, start, end)
Special preprocessor sorting the list of strings (whitespace separated) that follow a signature on a ...
def __call__(self, out, result)
def __getattr__(self, attr)
def __call__(self, input)
NamedRange_< CONTAINER > range(const CONTAINER &cnt, const std::string &name)
simple function to create the named range form arbitrary container
def WriteResult(self, result)
def __CompareText(self, s1, s2)