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...'),
529 "JobOptionsSvc INFO # ",
530 "JobOptionsSvc WARNING # ",
533 "This machine has a speed",
536 "ToolSvc.Sequenc... INFO",
537 "DataListenerSvc INFO XML written to file:",
538 "[INFO]",
"[WARNING]",
539 "DEBUG No writable file catalog found which contains FID:",
541 "DEBUG Service base class initialized successfully",
542 "DEBUG Incident timing:",
543 "INFO 'CnvServices':[",
547 r"^JobOptionsSvc INFO *$",
549 r"(Always|SUCCESS)\s*(Root f|[^ ]* F)ile version:",
550 r"0x[0-9a-fA-F#]+ *Algorithm::sysInitialize\(\) *\[",
551 r"0x[0-9a-fA-F#]* *__gxx_personality_v0 *\[",
552 r"File '.*.xml' does not exist",
553 r"INFO Refer to dataset .* by its file ID:",
554 r"INFO Referring to dataset .* by its file ID:",
555 r"INFO Disconnect from dataset",
556 r"INFO Disconnected from dataset",
557 r"INFO Disconnected data IO:",
558 r"IncidentSvc\s*(DEBUG (Adding|Removing)|VERBOSE Calling)",
560 r"^StatusCodeSvc.*listing all unchecked return codes:",
561 r"^StatusCodeSvc\s*INFO\s*$",
562 r"Num\s*\|\s*Function\s*\|\s*Source Library",
565 r"ERROR Failed to modify file: .* Errno=2 No such file or directory",
567 r"^ +[0-9]+ \|.*ROOT",
568 r"^ +[0-9]+ \|.*\|.*Dict",
572 r"SUCCESS\s*Booked \d+ Histogram\(s\)",
579 r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*',
582 normalizeExamples = (lineSkipper + normalizeExamples + skipEmptyLines +
583 normalizeEOL +
LineSorter(
"Services to release : "))
586 def __init__(self, reffile, cause, result_key, preproc = normalizeExamples):
593 if os.path.isfile(self.
reffile):
594 orig = open(self.
reffile).xreadlines()
600 new = stdout.splitlines()
604 diffs = difflib.ndiff(orig,new,charjunk=difflib.IS_CHARACTER_JUNK)
605 filterdiffs =
map(
lambda x: x.strip(),filter(
lambda x: x[0] !=
" ",diffs))
608 result[self.
result_key] = result.Quote(
"\n".join(filterdiffs))
612 +) standard output of the test""")
613 causes.append(self.
cause)
620 def findReferenceBlock(reference, stdout, result, causes, signature_offset=0, signature=None,
623 Given a block of text, tries to find it in the output.
624 The block had to be identified by a signature line. By default, the first
625 line is used as signature, or the line pointed to by signature_offset. If
626 signature_offset points outside the block, a signature line can be passed as
627 signature argument. Note: if 'signature' is None (the default), a negative
628 signature_offset is interpreted as index in a list (e.g. -1 means the last
629 line), otherwise the it is interpreted as the number of lines before the
630 first one of the block the signature must appear.
631 The parameter 'id' allow to distinguish between different calls to this
632 function in the same validation code.
635 reflines = filter(
None,
map(
lambda s: s.rstrip(), reference.splitlines()))
637 raise RuntimeError(
"Empty (or null) reference")
639 outlines = filter(
None,
map(
lambda s: s.rstrip(), stdout.splitlines()))
641 res_field =
"GaudiTest.RefBlock"
643 res_field +=
"_%s" % id
645 if signature
is None:
646 if signature_offset < 0:
647 signature_offset = len(reference)+signature_offset
648 signature = reflines[signature_offset]
651 pos = outlines.index(signature)
652 outlines = outlines[pos-signature_offset:pos+len(reflines)-signature_offset]
653 if reflines != outlines:
654 msg =
"standard output"
656 if not msg
in causes:
658 result[res_field +
".observed"] = result.Quote(
"\n".join(outlines))
660 causes.append(
"missing signature")
661 result[res_field +
".signature"] = result.Quote(signature)
662 if len(reflines) > 1
or signature != reflines[0]:
663 result[res_field +
".expected"] = result.Quote(
"\n".join(reflines))
669 Count the number of messages with required severity (by default ERROR and FATAL)
670 and check if their numbers match the expected ones (0 by default).
671 The dictionary "expected" can be used to tune the number of errors and fatals
672 allowed, or to limit the number of expected warnings etc.
674 stdout = kwargs[
"stdout"]
675 result = kwargs[
"result"]
676 causes = kwargs[
"causes"]
683 outlines = stdout.splitlines()
684 from math
import log10
685 fmt =
"%%%dd - %%s" % (int(log10(len(outlines))+1))
691 if len(words) >= 2
and words[1]
in errors:
692 errors[words[1]].append(fmt%(linecount,l.rstrip()))
695 if len(errors[e]) != expected[e]:
696 causes.append(
'%s(%d)'%(e,len(errors[e])))
697 result[
"GaudiTest.lines.%s"%e] = result.Quote(
'\n'.join(errors[e]))
698 result[
"GaudiTest.lines.%s.expected#"%e] = result.Quote(str(expected[e]))
705 Parse the TTree summary table in lines, starting from pos.
706 Returns a tuple with the dictionary with the digested informations and the
707 position of the first line after the summary.
713 splitcols =
lambda l: [ f.strip()
for f
in l.strip(
"*\n").split(
':',2) ]
716 cols = splitcols(ll[0])
717 r[
"Name"], r[
"Title"] = cols[1:]
719 cols = splitcols(ll[1])
720 r[
"Entries"] = int(cols[1])
722 sizes = cols[2].split()
723 r[
"Total size"] = int(sizes[2])
724 if sizes[-1] ==
"memory":
727 r[
"File size"] = int(sizes[-1])
729 cols = splitcols(ll[2])
730 sizes = cols[2].split()
731 if cols[0] ==
"Baskets":
732 r[
"Baskets"] = int(cols[1])
733 r[
"Basket size"] = int(sizes[2])
734 r[
"Compression"] = float(sizes[-1])
737 if i < (count - 3)
and lines[i].startswith(
"*Tree"):
738 result = parseblock(lines[i:i+3])
739 result[
"Branches"] = {}
741 while i < (count - 3)
and lines[i].startswith(
"*Br"):
742 if i < (count - 2)
and lines[i].startswith(
"*Branch "):
746 branch = parseblock(lines[i:i+3])
747 result[
"Branches"][branch[
"Name"]] = branch
754 Scan stdout to find ROOT TTree summaries and digest them.
756 stars = re.compile(
r"^\*+$")
757 outlines = stdout.splitlines()
758 nlines = len(outlines)
764 while i < nlines
and not stars.match(outlines[i]):
769 trees[tree[
"Name"]] = tree
775 Check that all the keys in reference are in to_check too, with the same value.
776 If the value is a dict, the function is called recursively. to_check can
777 contain more keys than reference, that will not be tested.
778 The function returns at the first difference found.
783 ignore_re = re.compile(ignore)
784 keys = [ key
for key
in reference
if not ignore_re.match(key) ]
786 keys = reference.keys()
790 if (
type(reference[k])
is dict)
and (
type(to_check[k])
is dict):
792 failed = fail_keys =
cmpTreesDicts(reference[k], to_check[k], ignore)
795 failed = to_check[k] != reference[k]
800 fail_keys.insert(0, k)
810 if c
is None or r
is None:
812 return (fail_path, r, c)
815 h_count_re = re.compile(
r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)")
819 Extract the histograms infos from the lines starting at pos.
820 Returns the position of the first line after the summary block.
823 h_table_head = re.compile(
r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"')
824 h_short_summ = re.compile(
r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
829 m = h_count_re.search(lines[pos])
830 name = m.group(1).strip()
831 total = int(m.group(2))
833 for k, v
in [ x.split(
"=")
for x
in m.group(3).split() ]:
836 header[
"Total"] = total
840 m = h_table_head.search(lines[pos])
843 t = t.replace(
" profile",
"Prof")
850 if l.startswith(
" | ID"):
852 titles = [ x.strip()
for x
in l.split(
"|")][1:]
854 while pos < nlines
and lines[pos].startswith(
" |"):
856 values = [ x.strip()
for x
in l.split(
"|")][1:]
858 for i
in range(len(titles)):
859 hcont[titles[i]] = values[i]
860 cont[hcont[
"ID"]] = hcont
862 elif l.startswith(
" ID="):
863 while pos < nlines
and lines[pos].startswith(
" ID="):
864 values = [ x.strip()
for x
in h_short_summ.search(lines[pos]).groups() ]
865 cont[values[0]] = values
868 raise RuntimeError(
"Cannot understand line %d: '%s'" % (pos, l))
872 summ[d][
"header"] = header
877 summ[name] = {
"header": header}
882 Scan stdout to find ROOT TTree summaries and digest them.
884 outlines = stdout.splitlines()
885 nlines = len(outlines) - 1
893 match = h_count_re.search(outlines[pos])
894 while pos < nlines
and not match:
896 match = h_count_re.search(outlines[pos])
899 summaries.update(summ)
904 """Create a new 'Filter'.
906 'input' -- The string containing the input to provide to the
909 'timeout' -- As for 'TimeoutExecutable.__init__'."""
911 super(GaudiFilterExecutable, self).
__init__(input, timeout)
918 tmpf = tempfile.mkstemp()
923 """Copied from TimeoutExecutable to allow the re-implementation of
926 if sys.platform ==
"win32":
937 """Code copied from both FilterExecutable and TimeoutExecutable.
941 self._ClosePipeEnd(self._stdin_pipe[0])
942 if self._stdout_pipe:
943 self._ClosePipeEnd(self._stdout_pipe[1])
944 if self._stderr_pipe:
945 self._ClosePipeEnd(self._stderr_pipe[1])
953 super(qm.executable.TimeoutExecutable, self).
_HandleChild()
960 child_pid = self._GetChildPID()
962 os.setpgid(child_pid, child_pid)
990 os.setpgid(0, child_pid)
999 max_fds = os.sysconf(
"SC_OPEN_MAX")
1002 for fd
in xrange(max_fds):
1013 if sys.platform ==
"linux2":
1015 os.path.join(
"/proc", str(child_pid),
"exe"),
1017 "-batch",
"-n",
"-x",
1018 "'%s'" % os.path.join(os.path.dirname(__file__),
"stack-trace.gdb")]
1021 o = os.popen(
" ".join(cmd)).read()
1026 os.kill(0, signal.SIGKILL)
1029 select.select ([], [], [])
1034 elif self.
__timeout >= 0
and sys.platform ==
"win32":
1037 self.__monitor_thread.start()
1039 if sys.platform ==
"win32":
1042 """Code copied from FilterExecutable.
1043 Kill the child if the timeout expires.
1045 This function is run in the monitoring thread."""
1053 result = win32event.WaitForSingleObject(self._GetChildPID(),
1056 if result == win32con.WAIT_TIMEOUT:
1063 """Standard Gaudi test.
1066 qm.fields.TextField(
1070 description=
"""The path to the program.
1072 This field indicates the path to the program. If it is not
1073 an absolute path, the value of the 'PATH' environment
1074 variable will be used to search for the program.
1075 If not specified, $GAUDIEXE or Gaudi.exe are used.
1078 qm.fields.SetField(qm.fields.TextField(
1080 title=
"Argument List",
1081 description=
"""The command-line arguments.
1083 If this field is left blank, the program is run without any
1086 Use this field to specify the option files.
1088 An implicit 0th argument (the path to the program) is added
1091 qm.fields.TextField(
1094 description=
"""Options to be passed to the application.
1096 This field allows to pass a list of options to the main program
1097 without the need of a separate option file.
1099 The content of the field is written to a temporary file which name
1100 is passed the the application as last argument (appended to the
1101 field "Argument List".
1107 qm.fields.TextField(
1109 title=
"Working Directory",
1110 description=
"""Path to the working directory.
1112 If this field is left blank, the program will be run from the qmtest
1113 directory, otherwise from the directory specified.""",
1116 qm.fields.TextField(
1118 title=
"Reference Output",
1119 description=
"""Path to the file containing the reference output.
1121 If this field is left blank, any standard output will be considered
1124 If the reference file is specified, any output on standard error is
1127 qm.fields.TextField(
1128 name=
"error_reference",
1129 title=
"Reference for standard error",
1130 description=
"""Path to the file containing the reference for the standard error.
1132 If this field is left blank, any standard output will be considered
1135 If the reference file is specified, any output on standard error is
1138 qm.fields.SetField(qm.fields.TextField(
1139 name =
"unsupported_platforms",
1140 title =
"Unsupported Platforms",
1141 description =
"""Platform on which the test must not be run.
1143 List of regular expressions identifying the platforms on which the
1144 test is not run and the result is set to UNTESTED."""
1147 qm.fields.TextField(
1149 title =
"Validator",
1150 description =
"""Function to validate the output of the test.
1152 If defined, the function is used to validate the products of the
1154 The function is called passing as arguments:
1155 self: the test class instance
1156 stdout: the standard output of the executed test
1157 stderr: the standard error of the executed test
1158 result: the Result objects to fill with messages
1159 The function must return a list of causes for the failure.
1160 If specified, overrides standard output, standard error and
1168 qm.fields.BooleanField(
1169 name =
"use_temp_dir",
1170 title =
"Use temporary directory",
1171 description =
"""Use temporary directory.
1173 If set to true, use a temporary directory as working directory.
1175 default_value=
"false"
1178 qm.fields.IntegerField(
1180 title =
"Expected signal",
1181 description =
"""Expect termination by signal.""",
1188 unsupported = [ re.compile(x)
1189 for x
in [ str(y).strip()
1190 for y
in self.unsupported_platforms ]
1193 for p_re
in unsupported:
1194 if p_re.search(platform):
1195 result.SetOutcome(result.UNTESTED)
1196 result[result.CAUSE] =
'Platform not supported.'
1202 Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.
1206 if "CMTCONFIG" in os.environ:
1207 arch = os.environ[
"CMTCONFIG"]
1208 elif "SCRAM_ARCH" in os.environ:
1209 arch = os.environ[
"SCRAM_ARCH"]
1214 Return True if the current platform is Windows.
1216 This function was needed because of the change in the CMTCONFIG format,
1217 from win32_vc71_dbg to i686-winxp-vc9-dbg.
1220 return "winxp" in platform
or platform.startswith(
"win")
1228 platformSplit =
lambda p: set(p.split(
'-' in p
and '-' or '_'))
1230 reference = os.path.normpath(os.path.expandvars(reffile))
1232 spec_ref = reference[:-3] + self.
GetPlatform()[0:3] + reference[-3:]
1233 if os.path.isfile(spec_ref):
1234 reference = spec_ref
1237 dirname, basename = os.path.split(reference)
1238 if not dirname: dirname =
'.'
1239 head = basename +
"."
1240 head_len = len(head)
1243 for f
in os.listdir(dirname):
1244 if f.startswith(head):
1245 req_plat = platformSplit(f[head_len:])
1246 if platform.issuperset(req_plat):
1247 candidates.append( (len(req_plat), f) )
1252 reference = os.path.join(dirname, candidates[-1][1])
1257 ignore =
r"Basket|.*size|Compression"):
1259 Compare the TTree summaries in stdout with the ones in trees_dict or in
1260 the reference file. By default ignore the size, compression and basket
1262 The presence of TTree summaries when none is expected is not a failure.
1264 if trees_dict
is None:
1267 if reference
and os.path.isfile(reference):
1272 from pprint
import PrettyPrinter
1273 pp = PrettyPrinter()
1275 result[
"GaudiTest.TTrees.expected"] = result.Quote(pp.pformat(trees_dict))
1277 result[
"GaudiTest.TTrees.ignore"] = result.Quote(ignore)
1282 causes.append(
"trees summaries")
1284 result[
"GaudiTest.TTrees.failure_on"] = result.Quote(msg)
1285 result[
"GaudiTest.TTrees.found"] = result.Quote(pp.pformat(trees))
1293 Compare the TTree summaries in stdout with the ones in trees_dict or in
1294 the reference file. By default ignore the size, compression and basket
1296 The presence of TTree summaries when none is expected is not a failure.
1301 if reference
and os.path.isfile(reference):
1306 from pprint
import PrettyPrinter
1307 pp = PrettyPrinter()
1309 result[
"GaudiTest.Histos.expected"] = result.Quote(pp.pformat(dict))
1311 result[
"GaudiTest.Histos.ignore"] = result.Quote(ignore)
1316 causes.append(
"histos summaries")
1318 result[
"GaudiTest.Histos.failure_on"] = result.Quote(msg)
1319 result[
"GaudiTest.Histos.found"] = result.Quote(pp.pformat(histos))
1325 Default validation action: compare standard output and error to the
1330 preproc = normalizeExamples
1334 if reference
and os.path.isfile(reference):
1335 result[
"GaudiTest.output_reference"] = reference
1338 "GaudiTest.output_diff",
1339 preproc = preproc)(stdout, result)
1347 newref = open(reference +
".new",
"w")
1349 for l
in stdout.splitlines():
1350 newref.write(l.rstrip() +
'\n')
1360 if reference
and os.path.isfile(reference):
1361 result[
"GaudiTest.error_reference"] = reference
1364 "GaudiTest.error_diff",
1365 preproc = preproc)(stderr, result)
1368 newref = open(reference +
".new",
"w")
1370 for l
in stderr.splitlines():
1371 newref.write(l.rstrip() +
'\n')
1376 "ExecTest.expected_stderr")(stderr, result)
1383 if self.validator.strip() !=
"":
1384 class CallWrapper(object):
1386 Small wrapper class to dynamically bind some default arguments
1389 def __init__(self, callable, extra_args = {}):
1393 from inspect
import getargspec
1399 def __call__(self, *args, **kwargs):
1403 kwargs = dict(kwargs)
1407 if a
not in positional
and a
not in kwargs:
1409 return apply(self.
callable, args, kwargs)
1411 exported_symbols = {
"self":self,
1416 "findReferenceBlock":
1417 CallWrapper(findReferenceBlock, {
"stdout":stdout,
1420 "validateWithReference":
1426 CallWrapper(countErrorLines, {
"stdout":stdout,
1429 "checkTTreesSummaries":
1433 "checkHistosSummaries":
1439 exec self.validator
in globals(), exported_symbols
1447 Add the content of the environment to the result object.
1449 Copied from the QMTest class of COOL.
1451 vars = os.environ.keys()
1453 result[
'GaudiTest.environment'] = \
1454 result.Quote(
'\n'.join([
"%s=%s"%(v,os.environ[v])
for v
in vars]))
1456 def Run(self, context, result):
1459 'context' -- A 'Context' giving run-time parameters to the
1462 'result' -- A 'Result' object. The outcome will be
1463 'Result.PASS' when this method is called. The 'result' may be
1464 modified by this method to indicate outcomes other than
1465 'Result.PASS' or to add annotations."""
1474 elif "GAUDIEXE" in os.environ:
1475 prog = os.environ[
"GAUDIEXE"]
1480 dummy, prog_ext = os.path.splitext(prog)
1481 if prog_ext
not in [
".exe",
".py",
".bat" ]
and self.
isWinPlatform():
1485 prog =
which(prog)
or prog
1488 args =
map(rationalizepath, self.args)
1495 if self.options.strip():
1497 if re.search(
r"from\s+Gaudi.Configuration\s+import\s+\*|from\s+Configurables\s+import", self.options):
1500 tmpfile.writelines(
"\n".join(self.options.splitlines()))
1502 args.append(tmpfile.name)
1503 result[
"GaudiTest.options"] = result.Quote(self.options)
1506 if prog_ext ==
".py":
1509 prog =
which(
"python.exe")
or "python.exe"
1511 prog =
which(
"python")
or "python"
1514 origdir = os.getcwd()
1516 os.chdir(str(os.path.normpath(os.path.expandvars(self.workdir))))
1518 if "QMTEST_TMPDIR" in os.environ:
1519 qmtest_tmpdir = os.environ[
"QMTEST_TMPDIR"]
1520 if not os.path.exists(qmtest_tmpdir):
1521 os.makedirs(qmtest_tmpdir)
1522 os.chdir(qmtest_tmpdir)
1523 elif "qmtest.tmpdir" in context:
1524 os.chdir(context[
"qmtest.tmpdir"])
1526 if "QMTEST_IGNORE_TIMEOUT" not in os.environ:
1539 if result.GetOutcome()
not in [ result.PASS ]:
1546 """Run the 'program'.
1548 'program' -- The path to the program to run.
1550 'arguments' -- A list of the arguments to the program. This
1551 list must contain a first argument corresponding to 'argv[0]'.
1553 'context' -- A 'Context' giving run-time parameters to the
1556 'result' -- A 'Result' object. The outcome will be
1557 'Result.PASS' when this method is called. The 'result' may be
1558 modified by this method to indicate outcomes other than
1559 'Result.PASS' or to add annotations.
1561 @attention: This method has been copied from command.ExecTestBase
1562 (QMTest 2.3.0) and modified to keep stdout and stderr
1563 for tests that have been terminated by a signal.
1564 (Fundamental for debugging in the Application Area)
1568 environment = self.MakeEnvironment(context)
1570 if "slc6" in environment.get(
'CMTCONFIG',
''):
1571 environment[
'TERM'] =
'dumb'
1584 exit_status = e.Run(arguments, environment, path = program)
1586 if e.stack_trace_file
and os.path.exists(e.stack_trace_file):
1587 stack_trace = open(e.stack_trace_file).read()
1588 os.remove(e.stack_trace_file)
1592 result[
"ExecTest.stack_trace"] = result.Quote(stack_trace)
1595 if (sys.platform ==
"win32" or os.WIFEXITED(exit_status)
1601 if self.exit_code
is None:
1603 elif sys.platform ==
"win32":
1604 exit_code = exit_status
1606 exit_code = os.WEXITSTATUS(exit_status)
1611 result[
"ExecTest.exit_code"] = str(exit_code)
1612 result[
"ExecTest.stdout"] = result.Quote(stdout)
1613 result[
"ExecTest.stderr"] = result.Quote(stderr)
1615 if exit_code != self.exit_code:
1616 causes.append(
"exit_code")
1617 result[
"ExecTest.expected_exit_code"] \
1618 = str(self.exit_code)
1623 result.Fail(
"Unexpected %s." % string.join(causes,
", "))
1624 elif os.WIFSIGNALED(exit_status):
1627 signal_number = str(os.WTERMSIG(exit_status))
1629 result.Fail(
"Program terminated by signal.")
1633 result.Fail(
"Exceeded time limit (%ds), terminated." % timeout)
1634 result[
"ExecTest.signal_number"] = signal_number
1635 result[
"ExecTest.stdout"] = result.Quote(e.stdout)
1636 result[
"ExecTest.stderr"] = result.Quote(e.stderr)
1638 result[
"ExecTest.expected_signal_number"] = str(self.
signal)
1639 elif os.WIFSTOPPED(exit_status):
1642 signal_number = str(os.WSTOPSIG(exit_status))
1644 result.Fail(
"Program stopped by signal.")
1648 result.Fail(
"Exceeded time limit (%ds), stopped." % timeout)
1649 result[
"ExecTest.signal_number"] = signal_number
1650 result[
"ExecTest.stdout"] = result.Quote(e.stdout)
1651 result[
"ExecTest.stderr"] = result.Quote(e.stderr)
1655 result.Fail(
"Program did not terminate normally.")
1661 result[
"ExecTest.stdout"] = result[
"ExecTest.stdout"].replace(esc,repr_esc)
1666 if 'NO_ECLIPSE_LAUNCHERS' in os.environ:
1671 projbasedir = os.path.normpath(destdir)
1672 while not os.path.exists(os.path.join(projbasedir,
".project")):
1673 oldprojdir = projbasedir
1674 projbasedir = os.path.normpath(os.path.join(projbasedir, os.pardir))
1677 if oldprojdir == projbasedir:
1681 if not os.path.exists(destdir):
1682 os.makedirs(destdir)
1684 from xml.etree
import ElementTree
as ET
1685 t = ET.parse(os.path.join(projbasedir,
".project"))
1686 projectName = t.find(
"name").text
1689 destfile =
"%s.launch" % self._Runnable__id
1691 destfile = os.path.join(destdir, destfile)
1693 if self.options.strip():
1697 tempfile = args.pop()
1698 optsfile = destfile + os.path.splitext(tempfile)[1]
1699 shutil.copyfile(tempfile, optsfile)
1700 args.append(optsfile)
1703 from xml.sax.saxutils
import quoteattr
1707 data[
"environment"] =
"\n".join([
'<mapEntry key=%s value=%s/>' % (quoteattr(k), quoteattr(v))
1708 for k, v
in os.environ.iteritems()
1709 if k
not in (
'MAKEOVERRIDES',
'MAKEFLAGS',
'MAKELEVEL')])
1711 data[
"exec"] =
which(prog)
or prog
1712 if os.path.basename(data[
"exec"]).lower().startswith(
"python"):
1713 data[
"stopAtMain"] =
"false"
1715 data[
"stopAtMain"] =
"true"
1717 data[
"args"] =
" ".join(
map(rationalizepath, args))
1719 data[
"args"] =
" ".join([
"/debugexe"] +
map(rationalizepath, [data[
"exec"]] + args))
1720 data[
"exec"] =
which(
"vcexpress.exe")
1723 data[
"workdir"] = os.getcwd()
1727 data[
"workdir"] = destdir
1729 data[
"project"] = projectName.strip()
1732 xml_template =
u"""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1733 <launchConfiguration type="org.eclipse.cdt.launch.applicationLaunchType">
1734 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.AUTO_SOLIB" value="true"/>
1735 <listAttribute key="org.eclipse.cdt.debug.mi.core.AUTO_SOLIB_LIST"/>
1736 <stringAttribute key="org.eclipse.cdt.debug.mi.core.DEBUG_NAME" value="gdb"/>
1737 <stringAttribute key="org.eclipse.cdt.debug.mi.core.GDB_INIT" value=".gdbinit"/>
1738 <listAttribute key="org.eclipse.cdt.debug.mi.core.SOLIB_PATH"/>
1739 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.STOP_ON_SOLIB_EVENTS" value="false"/>
1740 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.breakpointsFullPath" value="false"/>
1741 <stringAttribute key="org.eclipse.cdt.debug.mi.core.commandFactory" value="org.eclipse.cdt.debug.mi.core.standardCommandFactory"/>
1742 <stringAttribute key="org.eclipse.cdt.debug.mi.core.protocol" value="mi"/>
1743 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.verboseMode" value="false"/>
1744 <intAttribute key="org.eclipse.cdt.launch.ATTR_BUILD_BEFORE_LAUNCH_ATTR" value="0"/>
1745 <stringAttribute key="org.eclipse.cdt.launch.COREFILE_PATH" value=""/>
1746 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_ID" value="org.eclipse.cdt.debug.mi.core.CDebuggerNew"/>
1747 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_REGISTER_GROUPS" value=""/>
1748 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_START_MODE" value="run"/>
1749 <booleanAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN" value="%(stopAtMain)s"/>
1750 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN_SYMBOL" value="main"/>
1751 <booleanAttribute key="org.eclipse.cdt.launch.ENABLE_REGISTER_BOOKKEEPING" value="false"/>
1752 <booleanAttribute key="org.eclipse.cdt.launch.ENABLE_VARIABLE_BOOKKEEPING" value="false"/>
1753 <stringAttribute key="org.eclipse.cdt.launch.FORMAT" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?><contentList/>"/>
1754 <stringAttribute key="org.eclipse.cdt.launch.GLOBAL_VARIABLES" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <globalVariableList/> "/>
1755 <stringAttribute key="org.eclipse.cdt.launch.MEMORY_BLOCKS" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <memoryBlockExpressionList/> "/>
1756 <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_ARGUMENTS" value="%(args)s"/>
1757 <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="%(exec)s"/>
1758 <stringAttribute key="org.eclipse.cdt.launch.PROJECT_ATTR" value="%(project)s"/>
1759 <stringAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_ID_ATTR" value=""/>
1760 <stringAttribute key="org.eclipse.cdt.launch.WORKING_DIRECTORY" value="%(workdir)s"/>
1761 <booleanAttribute key="org.eclipse.cdt.launch.ui.ApplicationCDebuggerTab.DEFAULTS_SET" value="true"/>
1762 <booleanAttribute key="org.eclipse.cdt.launch.use_terminal" value="true"/>
1763 <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
1764 <listEntry value="/%(project)s"/>
1766 <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
1767 <listEntry value="4"/>
1769 <booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="false"/>
1770 <mapAttribute key="org.eclipse.debug.core.environmentVariables">
1773 <mapAttribute key="org.eclipse.debug.core.preferred_launchers">
1774 <mapEntry key="[debug]" value="org.eclipse.cdt.cdi.launch.localCLaunch"/>
1776 <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
1777 <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
1779 </launchConfiguration>
1784 data[k] = codecs.decode(data[k],
'utf-8')
1785 xml = xml_template % data
1788 codecs.open(destfile,
"w", encoding=
'utf-8').write(xml)
1790 print 'WARNING: problem generating Eclipse launcher'
1797 import simplejson
as json
1800 """An 'HTMLResultStream' writes its output to a set of HTML files.
1802 The argument 'dir' is used to select the destination directory for the HTML
1804 The destination directory may already contain the report from a previous run
1805 (for example of a different package), in which case it will be extended to
1806 include the new data.
1809 qm.fields.TextField(
1811 title =
"Destination Directory",
1812 description =
"""The name of the directory.
1814 All results will be written to the directory indicated.""",
1816 default_value =
""),
1820 """Prepare the destination directory.
1822 Creates the destination directory and store in it some preliminary
1823 annotations and the static files found in the template directory
1826 ResultStream.__init__(self, arguments, **args)
1831 templateDir = os.path.join(os.path.dirname(__file__),
"html_report")
1832 if not os.path.isdir(self.dir):
1833 os.makedirs(self.dir)
1835 for f
in os.listdir(templateDir):
1836 src = os.path.join(templateDir, f)
1837 dst = os.path.join(self.dir, f)
1838 if not os.path.isdir(src)
and not os.path.exists(dst):
1839 shutil.copy(src, dst)
1841 if "CMTCONFIG" in os.environ:
1847 """Helper function to extend the global summary file in the destination
1854 ids = set([ i[
"id"]
for i
in self.
_summary ])
1855 newSummary = [ i
for i
in oldSummary
if i[
"id"]
not in ids ]
1861 """Writes the annotation to the annotation file.
1862 If the key is already present with a different value, the value becomes
1863 a list and the new value is appended to it, except for start_time and
1872 key, value =
map(str, [key, value])
1873 if key ==
"qmtest.run.start_time":
1878 if key
not in annotations:
1879 annotations[key] = value
1880 if "qmtest.run.end_time" in annotations:
1881 del annotations[
"qmtest.run.end_time"]
1884 if key
in annotations:
1885 old = annotations[key]
1886 if type(old)
is list:
1887 if value
not in old:
1888 annotations[key].append(value)
1890 annotations[key] = [old, value]
1892 annotations[key] = value
1898 """Prepare the test result directory in the destination directory storing
1899 into it the result fields.
1900 A summary of the test result is stored both in a file in the test directory
1901 and in the global summary file.
1904 summary[
"id"] = result.GetId()
1905 summary[
"outcome"] = result.GetOutcome()
1906 summary[
"cause"] = result.GetCause()
1907 summary[
"fields"] = result.keys()
1908 summary[
"fields"].sort()
1911 for f
in [
"id",
"outcome",
"cause"]:
1912 summary[f] = str(summary[f])
1913 summary[
"fields"] =
map(str, summary[
"fields"])
1915 self._summary.append(summary)
1921 testOutDir = os.path.join(self.dir, summary[
"id"])
1922 if not os.path.isdir(testOutDir):
1923 os.makedirs(testOutDir)
1924 json.dump(summary, open(os.path.join(testOutDir,
"summary.json"),
"w"),
1926 for f
in summary[
"fields"]:
1927 open(os.path.join(testOutDir, f),
"w").write(result[f])
1938 class XMLResultStream(ResultStream):
1939 """An 'XMLResultStream' writes its output to a Ctest XML file.
1941 The argument 'dir' is used to select the destination file for the XML
1943 The destination directory may already contain the report from a previous run
1944 (for example of a different package), in which case it will be overrided to
1948 qm.fields.TextField(
1950 title =
"Destination Directory",
1951 description =
"""The name of the directory.
1953 All results will be written to the directory indicated.""",
1955 default_value =
""),
1956 qm.fields.TextField(
1958 title =
"Output File Prefix",
1959 description =
"""The output file name will be the specified prefix
1960 followed by 'Test.xml' (CTest convention).""",
1962 default_value =
""),
1966 """Prepare the destination directory.
1968 Creates the destination directory and store in it some preliminary
1971 ResultStream.__init__(self, arguments, **args)
1973 self.
_xmlFile = os.path.join(self.dir, self.prefix +
'Test.xml')
1979 if not os.path.isfile(self.
_xmlFile):
1981 if not os.path.exists(os.path.dirname(self.
_xmlFile)):
1982 os.makedirs(os.path.dirname(self.
_xmlFile))
1984 newdataset = ET.Element(
"newdataset")
1990 newdataset = self._tree.getroot()
1997 for site
in newdataset.getiterator() :
1998 if site.get(
"OSPlatform") == os.uname()[4]:
2008 import multiprocessing
2010 "BuildName" : os.getenv(
"CMTCONFIG"),
2011 "Name" : os.uname()[1] ,
2012 "Generator" :
"QMTest "+qm.version ,
2013 "OSName" : os.uname()[0] ,
2014 "Hostname" : socket.gethostname() ,
2015 "OSRelease" : os.uname()[2] ,
2016 "OSVersion" :os.uname()[3] ,
2017 "OSPlatform" :os.uname()[4] ,
2018 "Is64Bits" :
"unknown" ,
2019 "VendorString" :
"unknown" ,
2020 "VendorID" :
"unknown" ,
2021 "FamilyID" :
"unknown" ,
2022 "ModelID" :
"unknown" ,
2023 "ProcessorCacheSize" :
"unknown" ,
2024 "NumberOfLogicalCPU" : str(multiprocessing.cpu_count()) ,
2025 "NumberOfPhysicalCPU" :
"0" ,
2026 "TotalVirtualMemory" :
"0" ,
2027 "TotalPhysicalMemory" :
"0" ,
2028 "LogicalProcessorsPerPhysical" :
"0" ,
2029 "ProcessorClockFrequency" :
"0" ,
2031 self.
_site = ET.SubElement(newdataset,
"site", attrib)
2054 self.
_Testing = self._site.find(
"Testing")
2057 self.
_TestList = self._Testing.find(
"TestList")
2063 # Add some non-QMTest attributes
2064 if "CMTCONFIG" in os.environ:
2065 self.WriteAnnotation("cmt.cmtconfig", os.environ["CMTCONFIG"])
2067 self.WriteAnnotation("hostname", socket.gethostname())
2072 if key ==
"qmtest.run.start_time":
2073 if self._site.get(
"qmtest.run.start_time")
is not None :
2075 self._site.set(str(key),str(value))
2077 """Prepare the test result directory in the destination directory storing
2078 into it the result fields.
2079 A summary of the test result is stored both in a file in the test directory
2080 and in the global summary file.
2083 summary[
"id"] = result.GetId()
2084 summary[
"outcome"] = result.GetOutcome()
2085 summary[
"cause"] = result.GetCause()
2086 summary[
"fields"] = result.keys()
2087 summary[
"fields"].sort()
2091 for f
in [
"id",
"outcome",
"cause"]:
2092 summary[f] = str(summary[f])
2093 summary[
"fields"] =
map(str, summary[
"fields"])
2099 if "qmtest.start_time" in summary[
"fields"]:
2100 haveStartDate =
True
2102 haveStartDate =
False
2103 if "qmtest.end_time" in summary[
"fields"]:
2110 self.
_startTime = calendar.timegm(time.strptime(result[
"qmtest.start_time"],
"%Y-%m-%dT%H:%M:%SZ"))
2111 if self._StartTestTime.text
is None:
2112 self._StartDateTime.text = time.strftime(
"%b %d %H:%M %Z", time.localtime(self.
_startTime))
2113 self._StartTestTime.text = str(self.
_startTime)
2114 self._site.set(
"BuildStamp" , result[
"qmtest.start_time"] )
2118 self.
_endTime = calendar.timegm(time.strptime(result[
"qmtest.end_time"],
"%Y-%m-%dT%H:%M:%SZ"))
2122 tl = ET.Element(
"Test")
2123 tl.text = summary[
"id"]
2124 self._TestList.insert(0,tl)
2127 Test = ET.Element(
"Test")
2128 if summary[
"outcome"] ==
"PASS":
2129 Test.set(
"Status",
"passed")
2130 elif summary[
"outcome"] ==
"FAIL":
2131 Test.set(
"Status",
"failed")
2132 elif summary[
"outcome"] ==
"SKIPPED" or summary[
"outcome"] ==
"UNTESTED":
2133 Test.set(
"Status",
"skipped")
2134 elif summary[
"outcome"] ==
"ERROR":
2135 Test.set(
"Status",
"failed")
2136 Name = ET.SubElement(Test,
"Name",)
2137 Name.text = summary[
"id"]
2138 Results = ET.SubElement(Test,
"Results")
2141 self._Testing.insert(3,Test)
2143 if haveStartDate
and haveEndDate:
2146 testduration = str(delta)
2147 Testduration= ET.SubElement(Results,
"NamedMeasurement")
2148 Testduration.set(
"name",
"Execution Time")
2149 Testduration.set(
"type",
"numeric/float" )
2150 value = ET.SubElement(Testduration,
"Value")
2151 value.text = testduration
2154 for n
in (
"qmtest.end_time",
"qmtest.start_time",
"qmtest.cause",
"ExecTest.stdout"):
2155 if n
in summary[
"fields"]:
2156 summary[
"fields"].
remove(n)
2160 if "ExecTest.exit_code" in summary[
"fields"] :
2161 summary[
"fields"].
remove(
"ExecTest.exit_code")
2162 ExitCode= ET.SubElement(Results,
"NamedMeasurement")
2163 ExitCode.set(
"name",
"exit_code")
2164 ExitCode.set(
"type",
"numeric/integer" )
2165 value = ET.SubElement(ExitCode,
"Value")
2168 TestStartTime= ET.SubElement(Results,
"NamedMeasurement")
2169 TestStartTime.set(
"name",
"Start_Time")
2170 TestStartTime.set(
"type",
"String" )
2171 value = ET.SubElement(TestStartTime,
"Value")
2177 TestEndTime= ET.SubElement(Results,
"NamedMeasurement")
2178 TestEndTime.set(
"name",
"End_Time")
2179 TestEndTime.set(
"type",
"String" )
2180 value = ET.SubElement(TestEndTime,
"Value")
2186 if summary[
"cause"]:
2187 FailureCause= ET.SubElement(Results,
"NamedMeasurement")
2188 FailureCause.set(
"name",
"Cause")
2189 FailureCause.set(
"type",
"String" )
2190 value = ET.SubElement(FailureCause,
"Value")
2195 for field
in summary[
"fields"] :
2196 fields[field] = ET.SubElement(Results,
"NamedMeasurement")
2197 fields[field].set(
"type",
"String")
2198 fields[field].set(
"name",field)
2199 value = ET.SubElement(fields[field],
"Value")
2201 if "<pre>" in result[field][0:6] :
2207 if result.has_key(
"ExecTest.stdout" ) :
2208 Measurement = ET.SubElement(Results,
"Measurement")
2209 value = ET.SubElement(Measurement,
"Value")
2210 if "<pre>" in result[
"ExecTest.stdout"][0:6] :
2217 self._tree.write(self.
_xmlFile,
"utf-8")
2223 self._EndTestTime.text = str(self.
_endTime)
2224 self._EndDateTime.text = time.strftime(
"%b %d %H:%M %Z", time.localtime(self.
_endTime))
2231 self._ElapsedMinutes.text = str(delta/60)
2234 self._tree.write(self.
_xmlFile,
"utf-8")
def escape_xml_illegal_chars
__monitor_thread
This is the interesting part: dump the stack trace to a file.
_EndDateTime
End time elements.
struct GAUDI_API map
Parametrisation class for map-like implementation.
def which
Locates an executable in the executables path ($PATH) and returns the full path to it...
Output Validation Classes.
def ValidateWithReference
def PlatformIsNotSupported
def ROOT6WorkAroundEnabled
def _HandleChild
Needs to replace the ones from RedirectedExecutable and TimeoutExecutable.
def total_seconds_replacement
def convert_xml_illegal_chars
def __UseSeparateProcessGroupForChild
Special preprocessor sorting the list of strings (whitespace separated) that follow a signature on a ...
NamedRange_< CONTAINER > range(const CONTAINER &cnt, const std::string &name)
simple function to create the named range form arbitrary container
def _expandReferenceFileName