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",
570 r"StatusCodeSvc.*all StatusCode instances where checked",
574 r"SUCCESS\s*Booked \d+ Histogram\(s\)",
581 r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*',
584 normalizeExamples = (lineSkipper + normalizeExamples + skipEmptyLines +
585 normalizeEOL +
LineSorter(
"Services to release : "))
588 def __init__(self, reffile, cause, result_key, preproc = normalizeExamples):
595 if os.path.isfile(self.
reffile):
596 orig = open(self.
reffile).xreadlines()
602 new = stdout.splitlines()
606 diffs = difflib.ndiff(orig,new,charjunk=difflib.IS_CHARACTER_JUNK)
607 filterdiffs =
map(
lambda x: x.strip(),filter(
lambda x: x[0] !=
" ",diffs))
610 result[self.
result_key] = result.Quote(
"\n".join(filterdiffs))
614 +) standard output of the test""")
615 causes.append(self.
cause)
622 def findReferenceBlock(reference, stdout, result, causes, signature_offset=0, signature=None,
625 Given a block of text, tries to find it in the output.
626 The block had to be identified by a signature line. By default, the first
627 line is used as signature, or the line pointed to by signature_offset. If
628 signature_offset points outside the block, a signature line can be passed as
629 signature argument. Note: if 'signature' is None (the default), a negative
630 signature_offset is interpreted as index in a list (e.g. -1 means the last
631 line), otherwise the it is interpreted as the number of lines before the
632 first one of the block the signature must appear.
633 The parameter 'id' allow to distinguish between different calls to this
634 function in the same validation code.
637 reflines = filter(
None,
map(
lambda s: s.rstrip(), reference.splitlines()))
639 raise RuntimeError(
"Empty (or null) reference")
641 outlines = filter(
None,
map(
lambda s: s.rstrip(), stdout.splitlines()))
643 res_field =
"GaudiTest.RefBlock"
645 res_field +=
"_%s" % id
647 if signature
is None:
648 if signature_offset < 0:
649 signature_offset = len(reference)+signature_offset
650 signature = reflines[signature_offset]
653 pos = outlines.index(signature)
654 outlines = outlines[pos-signature_offset:pos+len(reflines)-signature_offset]
655 if reflines != outlines:
656 msg =
"standard output"
658 if not msg
in causes:
660 result[res_field +
".observed"] = result.Quote(
"\n".join(outlines))
662 causes.append(
"missing signature")
663 result[res_field +
".signature"] = result.Quote(signature)
664 if len(reflines) > 1
or signature != reflines[0]:
665 result[res_field +
".expected"] = result.Quote(
"\n".join(reflines))
671 Count the number of messages with required severity (by default ERROR and FATAL)
672 and check if their numbers match the expected ones (0 by default).
673 The dictionary "expected" can be used to tune the number of errors and fatals
674 allowed, or to limit the number of expected warnings etc.
676 stdout = kwargs[
"stdout"]
677 result = kwargs[
"result"]
678 causes = kwargs[
"causes"]
685 outlines = stdout.splitlines()
686 from math
import log10
687 fmt =
"%%%dd - %%s" % (int(log10(len(outlines))+1))
693 if len(words) >= 2
and words[1]
in errors:
694 errors[words[1]].append(fmt%(linecount,l.rstrip()))
697 if len(errors[e]) != expected[e]:
698 causes.append(
'%s(%d)'%(e,len(errors[e])))
699 result[
"GaudiTest.lines.%s"%e] = result.Quote(
'\n'.join(errors[e]))
700 result[
"GaudiTest.lines.%s.expected#"%e] = result.Quote(str(expected[e]))
707 Parse the TTree summary table in lines, starting from pos.
708 Returns a tuple with the dictionary with the digested informations and the
709 position of the first line after the summary.
715 splitcols =
lambda l: [ f.strip()
for f
in l.strip(
"*\n").split(
':',2) ]
718 cols = splitcols(ll[0])
719 r[
"Name"], r[
"Title"] = cols[1:]
721 cols = splitcols(ll[1])
722 r[
"Entries"] = int(cols[1])
724 sizes = cols[2].split()
725 r[
"Total size"] = int(sizes[2])
726 if sizes[-1] ==
"memory":
729 r[
"File size"] = int(sizes[-1])
731 cols = splitcols(ll[2])
732 sizes = cols[2].split()
733 if cols[0] ==
"Baskets":
734 r[
"Baskets"] = int(cols[1])
735 r[
"Basket size"] = int(sizes[2])
736 r[
"Compression"] = float(sizes[-1])
739 if i < (count - 3)
and lines[i].startswith(
"*Tree"):
740 result = parseblock(lines[i:i+3])
741 result[
"Branches"] = {}
743 while i < (count - 3)
and lines[i].startswith(
"*Br"):
744 if i < (count - 2)
and lines[i].startswith(
"*Branch "):
748 branch = parseblock(lines[i:i+3])
749 result[
"Branches"][branch[
"Name"]] = branch
756 Scan stdout to find ROOT TTree summaries and digest them.
758 stars = re.compile(
r"^\*+$")
759 outlines = stdout.splitlines()
760 nlines = len(outlines)
766 while i < nlines
and not stars.match(outlines[i]):
771 trees[tree[
"Name"]] = tree
777 Check that all the keys in reference are in to_check too, with the same value.
778 If the value is a dict, the function is called recursively. to_check can
779 contain more keys than reference, that will not be tested.
780 The function returns at the first difference found.
785 ignore_re = re.compile(ignore)
786 keys = [ key
for key
in reference
if not ignore_re.match(key) ]
788 keys = reference.keys()
792 if (
type(reference[k])
is dict)
and (
type(to_check[k])
is dict):
794 failed = fail_keys =
cmpTreesDicts(reference[k], to_check[k], ignore)
797 failed = to_check[k] != reference[k]
802 fail_keys.insert(0, k)
812 if c
is None or r
is None:
814 return (fail_path, r, c)
817 h_count_re = re.compile(
r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+(.*)")
821 Extract the histograms infos from the lines starting at pos.
822 Returns the position of the first line after the summary block.
825 h_table_head = re.compile(
r'SUCCESS\s+List of booked (1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"')
826 h_short_summ = re.compile(
r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
831 m = h_count_re.search(lines[pos])
832 name = m.group(1).strip()
833 total = int(m.group(2))
835 for k, v
in [ x.split(
"=")
for x
in m.group(3).split() ]:
838 header[
"Total"] = total
842 m = h_table_head.search(lines[pos])
845 t = t.replace(
" profile",
"Prof")
852 if l.startswith(
" | ID"):
854 titles = [ x.strip()
for x
in l.split(
"|")][1:]
856 while pos < nlines
and lines[pos].startswith(
" |"):
858 values = [ x.strip()
for x
in l.split(
"|")][1:]
860 for i
in range(len(titles)):
861 hcont[titles[i]] = values[i]
862 cont[hcont[
"ID"]] = hcont
864 elif l.startswith(
" ID="):
865 while pos < nlines
and lines[pos].startswith(
" ID="):
866 values = [ x.strip()
for x
in h_short_summ.search(lines[pos]).groups() ]
867 cont[values[0]] = values
870 raise RuntimeError(
"Cannot understand line %d: '%s'" % (pos, l))
874 summ[d][
"header"] = header
879 summ[name] = {
"header": header}
884 Scan stdout to find ROOT TTree summaries and digest them.
886 outlines = stdout.splitlines()
887 nlines = len(outlines) - 1
895 match = h_count_re.search(outlines[pos])
896 while pos < nlines
and not match:
898 match = h_count_re.search(outlines[pos])
901 summaries.update(summ)
906 """Create a new 'Filter'.
908 'input' -- The string containing the input to provide to the
911 'timeout' -- As for 'TimeoutExecutable.__init__'."""
913 super(GaudiFilterExecutable, self).
__init__(input, timeout)
920 tmpf = tempfile.mkstemp()
925 """Copied from TimeoutExecutable to allow the re-implementation of
928 if sys.platform ==
"win32":
939 """Code copied from both FilterExecutable and TimeoutExecutable.
943 self._ClosePipeEnd(self._stdin_pipe[0])
944 if self._stdout_pipe:
945 self._ClosePipeEnd(self._stdout_pipe[1])
946 if self._stderr_pipe:
947 self._ClosePipeEnd(self._stderr_pipe[1])
955 super(qm.executable.TimeoutExecutable, self).
_HandleChild()
962 child_pid = self._GetChildPID()
964 os.setpgid(child_pid, child_pid)
992 os.setpgid(0, child_pid)
1001 max_fds = os.sysconf(
"SC_OPEN_MAX")
1004 for fd
in xrange(max_fds):
1015 if sys.platform ==
"linux2":
1017 os.path.join(
"/proc", str(child_pid),
"exe"),
1019 "-batch",
"-n",
"-x",
1020 "'%s'" % os.path.join(os.path.dirname(__file__),
"stack-trace.gdb")]
1023 o = os.popen(
" ".join(cmd)).read()
1028 os.kill(0, signal.SIGKILL)
1031 select.select ([], [], [])
1036 elif self.
__timeout >= 0
and sys.platform ==
"win32":
1039 self.__monitor_thread.start()
1041 if sys.platform ==
"win32":
1044 """Code copied from FilterExecutable.
1045 Kill the child if the timeout expires.
1047 This function is run in the monitoring thread."""
1055 result = win32event.WaitForSingleObject(self._GetChildPID(),
1058 if result == win32con.WAIT_TIMEOUT:
1065 """Standard Gaudi test.
1068 qm.fields.TextField(
1072 description=
"""The path to the program.
1074 This field indicates the path to the program. If it is not
1075 an absolute path, the value of the 'PATH' environment
1076 variable will be used to search for the program.
1077 If not specified, $GAUDIEXE or Gaudi.exe are used.
1080 qm.fields.SetField(qm.fields.TextField(
1082 title=
"Argument List",
1083 description=
"""The command-line arguments.
1085 If this field is left blank, the program is run without any
1088 Use this field to specify the option files.
1090 An implicit 0th argument (the path to the program) is added
1093 qm.fields.TextField(
1096 description=
"""Options to be passed to the application.
1098 This field allows to pass a list of options to the main program
1099 without the need of a separate option file.
1101 The content of the field is written to a temporary file which name
1102 is passed the the application as last argument (appended to the
1103 field "Argument List".
1109 qm.fields.TextField(
1111 title=
"Working Directory",
1112 description=
"""Path to the working directory.
1114 If this field is left blank, the program will be run from the qmtest
1115 directory, otherwise from the directory specified.""",
1118 qm.fields.TextField(
1120 title=
"Reference Output",
1121 description=
"""Path to the file containing the reference output.
1123 If this field is left blank, any standard output will be considered
1126 If the reference file is specified, any output on standard error is
1129 qm.fields.TextField(
1130 name=
"error_reference",
1131 title=
"Reference for standard error",
1132 description=
"""Path to the file containing the reference for the standard error.
1134 If this field is left blank, any standard output will be considered
1137 If the reference file is specified, any output on standard error is
1140 qm.fields.SetField(qm.fields.TextField(
1141 name =
"unsupported_platforms",
1142 title =
"Unsupported Platforms",
1143 description =
"""Platform on which the test must not be run.
1145 List of regular expressions identifying the platforms on which the
1146 test is not run and the result is set to UNTESTED."""
1149 qm.fields.TextField(
1151 title =
"Validator",
1152 description =
"""Function to validate the output of the test.
1154 If defined, the function is used to validate the products of the
1156 The function is called passing as arguments:
1157 self: the test class instance
1158 stdout: the standard output of the executed test
1159 stderr: the standard error of the executed test
1160 result: the Result objects to fill with messages
1161 The function must return a list of causes for the failure.
1162 If specified, overrides standard output, standard error and
1170 qm.fields.BooleanField(
1171 name =
"use_temp_dir",
1172 title =
"Use temporary directory",
1173 description =
"""Use temporary directory.
1175 If set to true, use a temporary directory as working directory.
1177 default_value=
"false"
1180 qm.fields.IntegerField(
1182 title =
"Expected signal",
1183 description =
"""Expect termination by signal.""",
1190 unsupported = [ re.compile(x)
1191 for x
in [ str(y).strip()
1192 for y
in self.unsupported_platforms ]
1195 for p_re
in unsupported:
1196 if p_re.search(platform):
1197 result.SetOutcome(result.UNTESTED)
1198 result[result.CAUSE] =
'Platform not supported.'
1204 Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.
1208 if "CMTCONFIG" in os.environ:
1209 arch = os.environ[
"CMTCONFIG"]
1210 elif "SCRAM_ARCH" in os.environ:
1211 arch = os.environ[
"SCRAM_ARCH"]
1216 Return True if the current platform is Windows.
1218 This function was needed because of the change in the CMTCONFIG format,
1219 from win32_vc71_dbg to i686-winxp-vc9-dbg.
1222 return "winxp" in platform
or platform.startswith(
"win")
1230 platformSplit =
lambda p: set(p.split(
'-' in p
and '-' or '_'))
1232 reference = os.path.normpath(os.path.expandvars(reffile))
1234 spec_ref = reference[:-3] + self.
GetPlatform()[0:3] + reference[-3:]
1235 if os.path.isfile(spec_ref):
1236 reference = spec_ref
1239 dirname, basename = os.path.split(reference)
1240 if not dirname: dirname =
'.'
1241 head = basename +
"."
1242 head_len = len(head)
1245 for f
in os.listdir(dirname):
1246 if f.startswith(head):
1247 req_plat = platformSplit(f[head_len:])
1248 if platform.issuperset(req_plat):
1249 candidates.append( (len(req_plat), f) )
1254 reference = os.path.join(dirname, candidates[-1][1])
1259 ignore =
r"Basket|.*size|Compression"):
1261 Compare the TTree summaries in stdout with the ones in trees_dict or in
1262 the reference file. By default ignore the size, compression and basket
1264 The presence of TTree summaries when none is expected is not a failure.
1266 if trees_dict
is None:
1269 if reference
and os.path.isfile(reference):
1274 from pprint
import PrettyPrinter
1275 pp = PrettyPrinter()
1277 result[
"GaudiTest.TTrees.expected"] = result.Quote(pp.pformat(trees_dict))
1279 result[
"GaudiTest.TTrees.ignore"] = result.Quote(ignore)
1284 causes.append(
"trees summaries")
1286 result[
"GaudiTest.TTrees.failure_on"] = result.Quote(msg)
1287 result[
"GaudiTest.TTrees.found"] = result.Quote(pp.pformat(trees))
1295 Compare the TTree summaries in stdout with the ones in trees_dict or in
1296 the reference file. By default ignore the size, compression and basket
1298 The presence of TTree summaries when none is expected is not a failure.
1303 if reference
and os.path.isfile(reference):
1308 from pprint
import PrettyPrinter
1309 pp = PrettyPrinter()
1311 result[
"GaudiTest.Histos.expected"] = result.Quote(pp.pformat(dict))
1313 result[
"GaudiTest.Histos.ignore"] = result.Quote(ignore)
1318 causes.append(
"histos summaries")
1320 result[
"GaudiTest.Histos.failure_on"] = result.Quote(msg)
1321 result[
"GaudiTest.Histos.found"] = result.Quote(pp.pformat(histos))
1327 Default validation action: compare standard output and error to the
1332 preproc = normalizeExamples
1336 if reference
and os.path.isfile(reference):
1337 result[
"GaudiTest.output_reference"] = reference
1340 "GaudiTest.output_diff",
1341 preproc = preproc)(stdout, result)
1349 newref = open(reference +
".new",
"w")
1351 for l
in stdout.splitlines():
1352 newref.write(l.rstrip() +
'\n')
1362 if reference
and os.path.isfile(reference):
1363 result[
"GaudiTest.error_reference"] = reference
1366 "GaudiTest.error_diff",
1367 preproc = preproc)(stderr, result)
1370 newref = open(reference +
".new",
"w")
1372 for l
in stderr.splitlines():
1373 newref.write(l.rstrip() +
'\n')
1378 "ExecTest.expected_stderr")(stderr, result)
1385 if self.validator.strip() !=
"":
1386 class CallWrapper(object):
1388 Small wrapper class to dynamically bind some default arguments
1391 def __init__(self, callable, extra_args = {}):
1395 from inspect
import getargspec
1401 def __call__(self, *args, **kwargs):
1405 kwargs = dict(kwargs)
1409 if a
not in positional
and a
not in kwargs:
1411 return apply(self.
callable, args, kwargs)
1413 exported_symbols = {
"self":self,
1418 "findReferenceBlock":
1419 CallWrapper(findReferenceBlock, {
"stdout":stdout,
1422 "validateWithReference":
1428 CallWrapper(countErrorLines, {
"stdout":stdout,
1431 "checkTTreesSummaries":
1435 "checkHistosSummaries":
1441 exec self.validator
in globals(), exported_symbols
1449 Add the content of the environment to the result object.
1451 Copied from the QMTest class of COOL.
1453 vars = os.environ.keys()
1455 result[
'GaudiTest.environment'] = \
1456 result.Quote(
'\n'.join([
"%s=%s"%(v,os.environ[v])
for v
in vars]))
1458 def Run(self, context, result):
1461 'context' -- A 'Context' giving run-time parameters to the
1464 'result' -- A 'Result' object. The outcome will be
1465 'Result.PASS' when this method is called. The 'result' may be
1466 modified by this method to indicate outcomes other than
1467 'Result.PASS' or to add annotations."""
1476 elif "GAUDIEXE" in os.environ:
1477 prog = os.environ[
"GAUDIEXE"]
1482 dummy, prog_ext = os.path.splitext(prog)
1483 if prog_ext
not in [
".exe",
".py",
".bat" ]
and self.
isWinPlatform():
1487 prog =
which(prog)
or prog
1490 args =
map(rationalizepath, self.args)
1497 if self.options.strip():
1499 if re.search(
r"from\s+Gaudi.Configuration\s+import\s+\*|from\s+Configurables\s+import", self.options):
1502 tmpfile.writelines(
"\n".join(self.options.splitlines()))
1504 args.append(tmpfile.name)
1505 result[
"GaudiTest.options"] = result.Quote(self.options)
1508 if prog_ext ==
".py":
1511 prog =
which(
"python.exe")
or "python.exe"
1513 prog =
which(
"python")
or "python"
1516 origdir = os.getcwd()
1518 os.chdir(str(os.path.normpath(os.path.expandvars(self.workdir))))
1520 if "QMTEST_TMPDIR" in os.environ:
1521 qmtest_tmpdir = os.environ[
"QMTEST_TMPDIR"]
1522 if not os.path.exists(qmtest_tmpdir):
1523 os.makedirs(qmtest_tmpdir)
1524 os.chdir(qmtest_tmpdir)
1525 elif "qmtest.tmpdir" in context:
1526 os.chdir(context[
"qmtest.tmpdir"])
1528 if "QMTEST_IGNORE_TIMEOUT" not in os.environ:
1541 if result.GetOutcome()
not in [ result.PASS ]:
1548 """Run the 'program'.
1550 'program' -- The path to the program to run.
1552 'arguments' -- A list of the arguments to the program. This
1553 list must contain a first argument corresponding to 'argv[0]'.
1555 'context' -- A 'Context' giving run-time parameters to the
1558 'result' -- A 'Result' object. The outcome will be
1559 'Result.PASS' when this method is called. The 'result' may be
1560 modified by this method to indicate outcomes other than
1561 'Result.PASS' or to add annotations.
1563 @attention: This method has been copied from command.ExecTestBase
1564 (QMTest 2.3.0) and modified to keep stdout and stderr
1565 for tests that have been terminated by a signal.
1566 (Fundamental for debugging in the Application Area)
1570 environment = self.MakeEnvironment(context)
1572 if "slc6" in environment.get(
'CMTCONFIG',
''):
1573 environment[
'TERM'] =
'dumb'
1586 exit_status = e.Run(arguments, environment, path = program)
1588 if e.stack_trace_file
and os.path.exists(e.stack_trace_file):
1589 stack_trace = open(e.stack_trace_file).read()
1590 os.remove(e.stack_trace_file)
1594 result[
"ExecTest.stack_trace"] = result.Quote(stack_trace)
1597 if (sys.platform ==
"win32" or os.WIFEXITED(exit_status)
1603 if self.exit_code
is None:
1605 elif sys.platform ==
"win32":
1606 exit_code = exit_status
1608 exit_code = os.WEXITSTATUS(exit_status)
1613 result[
"ExecTest.exit_code"] = str(exit_code)
1614 result[
"ExecTest.stdout"] = result.Quote(stdout)
1615 result[
"ExecTest.stderr"] = result.Quote(stderr)
1617 if exit_code != self.exit_code:
1618 causes.append(
"exit_code")
1619 result[
"ExecTest.expected_exit_code"] \
1620 = str(self.exit_code)
1625 result.Fail(
"Unexpected %s." % string.join(causes,
", "))
1626 elif os.WIFSIGNALED(exit_status):
1629 signal_number = str(os.WTERMSIG(exit_status))
1631 result.Fail(
"Program terminated by signal.")
1635 result.Fail(
"Exceeded time limit (%ds), terminated." % timeout)
1636 result[
"ExecTest.signal_number"] = signal_number
1637 result[
"ExecTest.stdout"] = result.Quote(e.stdout)
1638 result[
"ExecTest.stderr"] = result.Quote(e.stderr)
1640 result[
"ExecTest.expected_signal_number"] = str(self.
signal)
1641 elif os.WIFSTOPPED(exit_status):
1644 signal_number = str(os.WSTOPSIG(exit_status))
1646 result.Fail(
"Program stopped by signal.")
1650 result.Fail(
"Exceeded time limit (%ds), stopped." % timeout)
1651 result[
"ExecTest.signal_number"] = signal_number
1652 result[
"ExecTest.stdout"] = result.Quote(e.stdout)
1653 result[
"ExecTest.stderr"] = result.Quote(e.stderr)
1657 result.Fail(
"Program did not terminate normally.")
1663 result[
"ExecTest.stdout"] = result[
"ExecTest.stdout"].replace(esc,repr_esc)
1668 if 'NO_ECLIPSE_LAUNCHERS' in os.environ:
1673 projbasedir = os.path.normpath(destdir)
1674 while not os.path.exists(os.path.join(projbasedir,
".project")):
1675 oldprojdir = projbasedir
1676 projbasedir = os.path.normpath(os.path.join(projbasedir, os.pardir))
1679 if oldprojdir == projbasedir:
1683 if not os.path.exists(destdir):
1684 os.makedirs(destdir)
1686 from xml.etree
import ElementTree
as ET
1687 t = ET.parse(os.path.join(projbasedir,
".project"))
1688 projectName = t.find(
"name").text
1691 destfile =
"%s.launch" % self._Runnable__id
1693 destfile = os.path.join(destdir, destfile)
1695 if self.options.strip():
1699 tempfile = args.pop()
1700 optsfile = destfile + os.path.splitext(tempfile)[1]
1701 shutil.copyfile(tempfile, optsfile)
1702 args.append(optsfile)
1705 from xml.sax.saxutils
import quoteattr
1709 data[
"environment"] =
"\n".join([
'<mapEntry key=%s value=%s/>' % (quoteattr(k), quoteattr(v))
1710 for k, v
in os.environ.iteritems()
1711 if k
not in (
'MAKEOVERRIDES',
'MAKEFLAGS',
'MAKELEVEL')])
1713 data[
"exec"] =
which(prog)
or prog
1714 if os.path.basename(data[
"exec"]).lower().startswith(
"python"):
1715 data[
"stopAtMain"] =
"false"
1717 data[
"stopAtMain"] =
"true"
1719 data[
"args"] =
" ".join(
map(rationalizepath, args))
1721 data[
"args"] =
" ".join([
"/debugexe"] +
map(rationalizepath, [data[
"exec"]] + args))
1722 data[
"exec"] =
which(
"vcexpress.exe")
1725 data[
"workdir"] = os.getcwd()
1729 data[
"workdir"] = destdir
1731 data[
"project"] = projectName.strip()
1734 xml_template =
u"""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1735 <launchConfiguration type="org.eclipse.cdt.launch.applicationLaunchType">
1736 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.AUTO_SOLIB" value="true"/>
1737 <listAttribute key="org.eclipse.cdt.debug.mi.core.AUTO_SOLIB_LIST"/>
1738 <stringAttribute key="org.eclipse.cdt.debug.mi.core.DEBUG_NAME" value="gdb"/>
1739 <stringAttribute key="org.eclipse.cdt.debug.mi.core.GDB_INIT" value=".gdbinit"/>
1740 <listAttribute key="org.eclipse.cdt.debug.mi.core.SOLIB_PATH"/>
1741 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.STOP_ON_SOLIB_EVENTS" value="false"/>
1742 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.breakpointsFullPath" value="false"/>
1743 <stringAttribute key="org.eclipse.cdt.debug.mi.core.commandFactory" value="org.eclipse.cdt.debug.mi.core.standardCommandFactory"/>
1744 <stringAttribute key="org.eclipse.cdt.debug.mi.core.protocol" value="mi"/>
1745 <booleanAttribute key="org.eclipse.cdt.debug.mi.core.verboseMode" value="false"/>
1746 <intAttribute key="org.eclipse.cdt.launch.ATTR_BUILD_BEFORE_LAUNCH_ATTR" value="0"/>
1747 <stringAttribute key="org.eclipse.cdt.launch.COREFILE_PATH" value=""/>
1748 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_ID" value="org.eclipse.cdt.debug.mi.core.CDebuggerNew"/>
1749 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_REGISTER_GROUPS" value=""/>
1750 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_START_MODE" value="run"/>
1751 <booleanAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN" value="%(stopAtMain)s"/>
1752 <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN_SYMBOL" value="main"/>
1753 <booleanAttribute key="org.eclipse.cdt.launch.ENABLE_REGISTER_BOOKKEEPING" value="false"/>
1754 <booleanAttribute key="org.eclipse.cdt.launch.ENABLE_VARIABLE_BOOKKEEPING" value="false"/>
1755 <stringAttribute key="org.eclipse.cdt.launch.FORMAT" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?><contentList/>"/>
1756 <stringAttribute key="org.eclipse.cdt.launch.GLOBAL_VARIABLES" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <globalVariableList/> "/>
1757 <stringAttribute key="org.eclipse.cdt.launch.MEMORY_BLOCKS" value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <memoryBlockExpressionList/> "/>
1758 <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_ARGUMENTS" value="%(args)s"/>
1759 <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="%(exec)s"/>
1760 <stringAttribute key="org.eclipse.cdt.launch.PROJECT_ATTR" value="%(project)s"/>
1761 <stringAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_ID_ATTR" value=""/>
1762 <stringAttribute key="org.eclipse.cdt.launch.WORKING_DIRECTORY" value="%(workdir)s"/>
1763 <booleanAttribute key="org.eclipse.cdt.launch.ui.ApplicationCDebuggerTab.DEFAULTS_SET" value="true"/>
1764 <booleanAttribute key="org.eclipse.cdt.launch.use_terminal" value="true"/>
1765 <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
1766 <listEntry value="/%(project)s"/>
1768 <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
1769 <listEntry value="4"/>
1771 <booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="false"/>
1772 <mapAttribute key="org.eclipse.debug.core.environmentVariables">
1775 <mapAttribute key="org.eclipse.debug.core.preferred_launchers">
1776 <mapEntry key="[debug]" value="org.eclipse.cdt.cdi.launch.localCLaunch"/>
1778 <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
1779 <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
1781 </launchConfiguration>
1786 data[k] = codecs.decode(data[k],
'utf-8')
1787 xml = xml_template % data
1790 codecs.open(destfile,
"w", encoding=
'utf-8').write(xml)
1792 print 'WARNING: problem generating Eclipse launcher'
1799 import simplejson
as json
1802 """An 'HTMLResultStream' writes its output to a set of HTML files.
1804 The argument 'dir' is used to select the destination directory for the HTML
1806 The destination directory may already contain the report from a previous run
1807 (for example of a different package), in which case it will be extended to
1808 include the new data.
1811 qm.fields.TextField(
1813 title =
"Destination Directory",
1814 description =
"""The name of the directory.
1816 All results will be written to the directory indicated.""",
1818 default_value =
""),
1822 """Prepare the destination directory.
1824 Creates the destination directory and store in it some preliminary
1825 annotations and the static files found in the template directory
1828 ResultStream.__init__(self, arguments, **args)
1833 templateDir = os.path.join(os.path.dirname(__file__),
"html_report")
1834 if not os.path.isdir(self.dir):
1835 os.makedirs(self.dir)
1837 for f
in os.listdir(templateDir):
1838 src = os.path.join(templateDir, f)
1839 dst = os.path.join(self.dir, f)
1840 if not os.path.isdir(src)
and not os.path.exists(dst):
1841 shutil.copy(src, dst)
1843 if "CMTCONFIG" in os.environ:
1849 """Helper function to extend the global summary file in the destination
1856 ids = set([ i[
"id"]
for i
in self.
_summary ])
1857 newSummary = [ i
for i
in oldSummary
if i[
"id"]
not in ids ]
1863 """Writes the annotation to the annotation file.
1864 If the key is already present with a different value, the value becomes
1865 a list and the new value is appended to it, except for start_time and
1874 key, value =
map(str, [key, value])
1875 if key ==
"qmtest.run.start_time":
1880 if key
not in annotations:
1881 annotations[key] = value
1882 if "qmtest.run.end_time" in annotations:
1883 del annotations[
"qmtest.run.end_time"]
1886 if key
in annotations:
1887 old = annotations[key]
1888 if type(old)
is list:
1889 if value
not in old:
1890 annotations[key].append(value)
1892 annotations[key] = [old, value]
1894 annotations[key] = value
1900 """Prepare the test result directory in the destination directory storing
1901 into it the result fields.
1902 A summary of the test result is stored both in a file in the test directory
1903 and in the global summary file.
1906 summary[
"id"] = result.GetId()
1907 summary[
"outcome"] = result.GetOutcome()
1908 summary[
"cause"] = result.GetCause()
1909 summary[
"fields"] = result.keys()
1910 summary[
"fields"].sort()
1913 for f
in [
"id",
"outcome",
"cause"]:
1914 summary[f] = str(summary[f])
1915 summary[
"fields"] =
map(str, summary[
"fields"])
1917 self._summary.append(summary)
1923 testOutDir = os.path.join(self.dir, summary[
"id"])
1924 if not os.path.isdir(testOutDir):
1925 os.makedirs(testOutDir)
1926 json.dump(summary, open(os.path.join(testOutDir,
"summary.json"),
"w"),
1928 for f
in summary[
"fields"]:
1929 open(os.path.join(testOutDir, f),
"w").write(result[f])
1940 class XMLResultStream(ResultStream):
1941 """An 'XMLResultStream' writes its output to a Ctest XML file.
1943 The argument 'dir' is used to select the destination file for the XML
1945 The destination directory may already contain the report from a previous run
1946 (for example of a different package), in which case it will be overrided to
1950 qm.fields.TextField(
1952 title =
"Destination Directory",
1953 description =
"""The name of the directory.
1955 All results will be written to the directory indicated.""",
1957 default_value =
""),
1958 qm.fields.TextField(
1960 title =
"Output File Prefix",
1961 description =
"""The output file name will be the specified prefix
1962 followed by 'Test.xml' (CTest convention).""",
1964 default_value =
""),
1968 """Prepare the destination directory.
1970 Creates the destination directory and store in it some preliminary
1973 ResultStream.__init__(self, arguments, **args)
1975 self.
_xmlFile = os.path.join(self.dir, self.prefix +
'Test.xml')
1981 if not os.path.isfile(self.
_xmlFile):
1983 if not os.path.exists(os.path.dirname(self.
_xmlFile)):
1984 os.makedirs(os.path.dirname(self.
_xmlFile))
1986 newdataset = ET.Element(
"newdataset")
1992 newdataset = self._tree.getroot()
1999 for site
in newdataset.getiterator() :
2000 if site.get(
"OSPlatform") == os.uname()[4]:
2010 import multiprocessing
2012 "BuildName" : os.getenv(
"CMTCONFIG"),
2013 "Name" : os.uname()[1] ,
2014 "Generator" :
"QMTest "+qm.version ,
2015 "OSName" : os.uname()[0] ,
2016 "Hostname" : socket.gethostname() ,
2017 "OSRelease" : os.uname()[2] ,
2018 "OSVersion" :os.uname()[3] ,
2019 "OSPlatform" :os.uname()[4] ,
2020 "Is64Bits" :
"unknown" ,
2021 "VendorString" :
"unknown" ,
2022 "VendorID" :
"unknown" ,
2023 "FamilyID" :
"unknown" ,
2024 "ModelID" :
"unknown" ,
2025 "ProcessorCacheSize" :
"unknown" ,
2026 "NumberOfLogicalCPU" : str(multiprocessing.cpu_count()) ,
2027 "NumberOfPhysicalCPU" :
"0" ,
2028 "TotalVirtualMemory" :
"0" ,
2029 "TotalPhysicalMemory" :
"0" ,
2030 "LogicalProcessorsPerPhysical" :
"0" ,
2031 "ProcessorClockFrequency" :
"0" ,
2033 self.
_site = ET.SubElement(newdataset,
"Site", attrib)
2056 self.
_Testing = self._site.find(
"Testing")
2059 self.
_TestList = self._Testing.find(
"TestList")
2065 # Add some non-QMTest attributes
2066 if "CMTCONFIG" in os.environ:
2067 self.WriteAnnotation("cmt.cmtconfig", os.environ["CMTCONFIG"])
2069 self.WriteAnnotation("hostname", socket.gethostname())
2074 if key ==
"qmtest.run.start_time":
2075 if self._site.get(
"qmtest.run.start_time")
is not None :
2077 self._site.set(str(key),str(value))
2079 """Prepare the test result directory in the destination directory storing
2080 into it the result fields.
2081 A summary of the test result is stored both in a file in the test directory
2082 and in the global summary file.
2085 summary[
"id"] = result.GetId()
2086 summary[
"outcome"] = result.GetOutcome()
2087 summary[
"cause"] = result.GetCause()
2088 summary[
"fields"] = result.keys()
2089 summary[
"fields"].sort()
2093 for f
in [
"id",
"outcome",
"cause"]:
2094 summary[f] = str(summary[f])
2095 summary[
"fields"] =
map(str, summary[
"fields"])
2101 if "qmtest.start_time" in summary[
"fields"]:
2102 haveStartDate =
True
2104 haveStartDate =
False
2105 if "qmtest.end_time" in summary[
"fields"]:
2112 self.
_startTime = calendar.timegm(time.strptime(result[
"qmtest.start_time"],
"%Y-%m-%dT%H:%M:%SZ"))
2113 if self._StartTestTime.text
is None:
2114 self._StartDateTime.text = time.strftime(
"%b %d %H:%M %Z", time.localtime(self.
_startTime))
2115 self._StartTestTime.text = str(self.
_startTime)
2116 self._site.set(
"BuildStamp" , result[
"qmtest.start_time"] )
2120 self.
_endTime = calendar.timegm(time.strptime(result[
"qmtest.end_time"],
"%Y-%m-%dT%H:%M:%SZ"))
2124 tl = ET.Element(
"Test")
2125 tl.text = summary[
"id"]
2126 self._TestList.insert(0,tl)
2129 Test = ET.Element(
"Test")
2130 if summary[
"outcome"] ==
"PASS":
2131 Test.set(
"Status",
"passed")
2132 elif summary[
"outcome"] ==
"FAIL":
2133 Test.set(
"Status",
"failed")
2134 elif summary[
"outcome"] ==
"SKIPPED" or summary[
"outcome"] ==
"UNTESTED":
2135 Test.set(
"Status",
"skipped")
2136 elif summary[
"outcome"] ==
"ERROR":
2137 Test.set(
"Status",
"failed")
2138 Name = ET.SubElement(Test,
"Name",)
2139 Name.text = summary[
"id"]
2140 Results = ET.SubElement(Test,
"Results")
2143 self._Testing.insert(3,Test)
2145 if haveStartDate
and haveEndDate:
2148 testduration = str(delta)
2149 Testduration= ET.SubElement(Results,
"NamedMeasurement")
2150 Testduration.set(
"name",
"Execution Time")
2151 Testduration.set(
"type",
"numeric/float" )
2152 value = ET.SubElement(Testduration,
"Value")
2153 value.text = testduration
2156 for n
in (
"qmtest.end_time",
"qmtest.start_time",
"qmtest.cause",
"ExecTest.stdout"):
2157 if n
in summary[
"fields"]:
2158 summary[
"fields"].
remove(n)
2162 if "ExecTest.exit_code" in summary[
"fields"] :
2163 summary[
"fields"].
remove(
"ExecTest.exit_code")
2164 ExitCode= ET.SubElement(Results,
"NamedMeasurement")
2165 ExitCode.set(
"name",
"exit_code")
2166 ExitCode.set(
"type",
"numeric/integer" )
2167 value = ET.SubElement(ExitCode,
"Value")
2170 TestStartTime= ET.SubElement(Results,
"NamedMeasurement")
2171 TestStartTime.set(
"name",
"Start_Time")
2172 TestStartTime.set(
"type",
"String" )
2173 value = ET.SubElement(TestStartTime,
"Value")
2179 TestEndTime= ET.SubElement(Results,
"NamedMeasurement")
2180 TestEndTime.set(
"name",
"End_Time")
2181 TestEndTime.set(
"type",
"String" )
2182 value = ET.SubElement(TestEndTime,
"Value")
2188 if summary[
"cause"]:
2189 FailureCause= ET.SubElement(Results,
"NamedMeasurement")
2190 FailureCause.set(
"name",
"Cause")
2191 FailureCause.set(
"type",
"String" )
2192 value = ET.SubElement(FailureCause,
"Value")
2197 for field
in summary[
"fields"] :
2198 fields[field] = ET.SubElement(Results,
"NamedMeasurement")
2199 fields[field].set(
"type",
"String")
2200 fields[field].set(
"name",field)
2201 value = ET.SubElement(fields[field],
"Value")
2203 if "<pre>" in result[field][0:6] :
2209 if result.has_key(
"ExecTest.stdout" ) :
2210 Measurement = ET.SubElement(Results,
"Measurement")
2211 value = ET.SubElement(Measurement,
"Value")
2212 if "<pre>" in result[
"ExecTest.stdout"][0:6] :
2219 self._tree.write(self.
_xmlFile,
"utf-8")
2225 self._EndTestTime.text = str(self.
_endTime)
2226 self._EndDateTime.text = time.strftime(
"%b %d %H:%M %Z", time.localtime(self.
_endTime))
2233 self._ElapsedMinutes.text = str(delta/60)
2236 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