24 from subprocess
import PIPE, STDOUT, Popen
25 from unittest
import TestCase
28 from html
import escape
as escape_for_html
30 from cgi
import escape
as escape_for_html
34 if sys.version_info < (3, 5):
37 from codecs
import backslashreplace_errors, register_error
40 if isinstance(exc, UnicodeDecodeError):
41 code =
hex(ord(exc.object[exc.start]))
42 return (
"\\" + code[1:], exc.start + 1)
44 return backslashreplace_errors(exc)
46 register_error(
"backslashreplace", _new_backslashreplace_errors)
48 del backslashreplace_errors
49 del _new_backslashreplace_errors
56 Take a string with invalid ASCII/UTF characters and quote them so that the
57 string can be used in an XML text.
59 >>> sanitize_for_xml('this is \x1b')
60 'this is [NON-XML-CHAR-0x1B]'
62 bad_chars = re.compile(
"[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]")
66 return "".join(
"[NON-XML-CHAR-0x%2X]" % ord(c)
for c
in match.group())
68 return bad_chars.sub(quote, data)
72 """helper to debug GAUDI-1084, dump the list of processes"""
73 from getpass
import getuser
75 if "WORKSPACE" in os.environ:
76 p = Popen([
"ps",
"-fH",
"-U", getuser()], stdout=PIPE)
77 with open(os.path.join(os.environ[
"WORKSPACE"], name),
"wb")
as f:
78 f.write(p.communicate()[0])
83 Send a signal to a process and all its child processes (starting from the
86 log = logging.getLogger(
"kill_tree")
87 ps_cmd = [
"ps",
"--no-headers",
"-o",
"pid",
"--ppid", str(ppid)]
90 get_children = Popen(ps_cmd, stdout=PIPE, stderr=PIPE, env={})
91 children =
map(int, get_children.communicate()[0].split())
92 for child
in children:
95 log.debug(
"killing process %d", ppid)
97 except OSError
as err:
100 log.debug(
"no such process %d", ppid)
108 _common_tmpdir =
None
138 logging.debug(
"running test %s", self.
name)
149 "TIMEOUT_DETAIL":
None,
155 r"from\s+Gaudi.Configuration\s+import\s+\*|"
156 "from\s+Configurables\s+import",
159 suffix, lang =
".py",
"python"
161 suffix, lang =
".opts",
"c++"
162 self.
result[
"Options"] =
'<code lang="{}"><pre>{}</pre></code>'.
format(
163 lang, escape_for_html(self.
options)
165 optionFile = tempfile.NamedTemporaryFile(suffix=suffix)
166 optionFile.file.write(self.
options.encode(
"utf-8"))
173 or platform.platform()
180 if re.search(prex, platform_id)
191 workdir = tempfile.mkdtemp()
202 prog_ext = os.path.splitext(prog)[1]
203 if prog_ext
not in [
".exe",
".py",
".bat"]:
207 prog =
which(prog)
or prog
209 args = list(
map(RationalizePath, self.
args))
211 if prog_ext ==
".py":
222 logging.debug(
"executing %r in %s", params, workdir)
224 params, stdout=PIPE, stderr=PIPE, env=self.
environment
226 logging.debug(
"(pid: %d)", self.
proc.pid)
227 out, err = self.
proc.communicate()
228 self.
out = out.decode(
"utf-8", errors=
"backslashreplace")
229 self.
err = err.decode(
"utf-8", errors=
"backslashreplace")
231 thread = threading.Thread(target=target)
236 if thread.is_alive():
237 logging.debug(
"time out in test %s (pid %d)", self.
name, self.
proc.pid)
244 "--eval-command=thread apply all backtrace",
246 gdb = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
248 "utf-8", errors=
"backslashreplace"
253 if thread.is_alive():
255 self.
causes.append(
"timeout")
260 f
"completed test {self.name} with returncode = {self.returnedCode}"
262 logging.debug(
"validating test...")
263 val_start_time = time.perf_counter()
267 self.
validate_time = round(time.perf_counter() - val_start_time, 2)
269 logging.debug(f
"skipped test {self.name}")
274 shutil.rmtree(workdir,
True)
278 if self.
status !=
"skipped":
280 if self.
signal is not None:
282 self.
causes.append(
"exit code")
286 self.
causes.append(
"exit code")
289 self.
causes.append(
"exit code")
299 logging.debug(
"%s: %s", self.
name, self.
status)
301 "Exit Code":
"returnedCode",
304 "Runtime Environment":
"environment",
307 "Program Name":
"program",
309 "Validator":
"validator",
310 "Validation execution time":
"validate_time",
311 "Output Reference File":
"reference",
312 "Error Reference File":
"error_reference",
315 "Unsupported Platforms":
"unsupported_platforms",
316 "Stack Trace":
"stack_trace",
319 (key, getattr(self, attr))
320 for key, attr
in field_mapping.items()
321 if getattr(self, attr)
330 resultDict.extend(self.
result.annotations.items())
332 resultDict = dict(resultDict)
335 if "Validator" in resultDict:
336 resultDict[
"Validator"] =
'<code lang="{}"><pre>{}</pre></code>'.
format(
337 "python", escape_for_html(resultDict[
"Validator"])
348 elif stderr.strip() != self.
stderr.strip():
349 self.
causes.append(
"standard error")
350 return result, self.
causes
363 Given a block of text, tries to find it in the output. The block had to be identified by a signature line. By default, the first line is used as signature, or the line pointed to by signature_offset. If signature_offset points outside the block, a signature line can be passed as signature argument. Note: if 'signature' is None (the default), a negative signature_offset is interpreted as index in a list (e.g. -1 means the last line), otherwise the it is interpreted as the number of lines before the first one of the block the signature must appear. The parameter 'id' allow to distinguish between different calls to this function in the same validation code.
366 if reference
is None:
375 reflines = list(filter(
None,
map(
lambda s: s.rstrip(), reference.splitlines())))
377 raise RuntimeError(
"Empty (or null) reference")
379 outlines = list(filter(
None,
map(
lambda s: s.rstrip(), stdout.splitlines())))
381 res_field =
"GaudiTest.RefBlock"
383 res_field +=
"_%s" % id
385 if signature
is None:
386 if signature_offset < 0:
387 signature_offset = len(reference) + signature_offset
388 signature = reflines[signature_offset]
391 pos = outlines.index(signature)
393 pos - signature_offset : pos + len(reflines) - signature_offset
395 if reflines != outlines:
396 msg =
"standard output"
399 if not msg
in causes:
401 result[res_field +
".observed"] = result.Quote(
"\n".join(outlines))
403 causes.append(
"missing signature")
404 result[res_field +
".signature"] = result.Quote(signature)
405 if len(reflines) > 1
or signature != reflines[0]:
406 result[res_field +
".expected"] = result.Quote(
"\n".join(reflines))
410 self, expected={"ERROR": 0,
"FATAL": 0}, stdout=
None, result=
None, causes=
None
413 Count the number of messages with required severity (by default ERROR and FATAL)
414 and check if their numbers match the expected ones (0 by default).
415 The dictionary "expected" can be used to tune the number of errors and fatals
416 allowed, or to limit the number of expected warnings etc.
431 outlines = stdout.splitlines()
432 from math
import log10
434 fmt =
"%%%dd - %%s" % (int(log10(len(outlines) + 1)))
440 if len(words) >= 2
and words[1]
in errors:
441 errors[words[1]].append(fmt % (linecount, l.rstrip()))
444 if len(errors[e]) != expected[e]:
445 causes.append(
"%s(%d)" % (e, len(errors[e])))
446 result[
"GaudiTest.lines.%s" % e] = result.Quote(
"\n".join(errors[e]))
447 result[
"GaudiTest.lines.%s.expected#" % e] = result.Quote(
459 ignore=r"Basket|.*size|Compression",
462 Compare the TTree summaries in stdout with the ones in trees_dict or in
463 the reference file. By default ignore the size, compression and basket
465 The presence of TTree summaries when none is expected is not a failure.
473 if trees_dict
is None:
476 if lreference
and os.path.isfile(lreference):
481 from pprint
import PrettyPrinter
485 result[
"GaudiTest.TTrees.expected"] = result.Quote(pp.pformat(trees_dict))
487 result[
"GaudiTest.TTrees.ignore"] = result.Quote(ignore)
492 causes.append(
"trees summaries")
494 result[
"GaudiTest.TTrees.failure_on"] = result.Quote(msg)
495 result[
"GaudiTest.TTrees.found"] = result.Quote(pp.pformat(trees))
500 self, stdout=None, result=None, causes=None, dict=None, ignore=None
503 Compare the TTree summaries in stdout with the ones in trees_dict or in
504 the reference file. By default ignore the size, compression and basket
506 The presence of TTree summaries when none is expected is not a failure.
518 if lreference
and os.path.isfile(lreference):
523 from pprint
import PrettyPrinter
527 result[
"GaudiTest.Histos.expected"] = result.Quote(pp.pformat(dict))
529 result[
"GaudiTest.Histos.ignore"] = result.Quote(ignore)
534 causes.append(
"histos summaries")
536 result[
"GaudiTest.Histos.failure_on"] = result.Quote(msg)
537 result[
"GaudiTest.Histos.found"] = result.Quote(pp.pformat(histos))
542 self, stdout=None, stderr=None, result=None, causes=None, preproc=None
545 Default validation acti*on: compare standard output and error to the
560 preproc = normalizeExamples
564 if lreference
and os.path.isfile(lreference):
566 lreference,
"standard output",
"Output Diff", preproc=preproc
569 causes += [
"missing reference file"]
573 if causes
and lreference:
576 newrefname =
".".join([lreference,
"new"])
577 while os.path.exists(newrefname):
579 newrefname =
".".join([lreference,
"~%d~" % cnt,
"new"])
580 newref = open(newrefname,
"w")
582 for l
in stdout.splitlines():
583 newref.write(l.rstrip() +
"\n")
585 result[
"New Output Reference File"] = os.path.relpath(
597 if os.path.isfile(lreference):
599 lreference,
"standard error",
"Error Diff", preproc=preproc
602 newcauses = [
"missing error reference file"]
604 if newcauses
and lreference:
606 newrefname =
".".join([lreference,
"new"])
607 while os.path.exists(newrefname):
609 newrefname =
".".join([lreference,
"~%d~" % cnt,
"new"])
610 newref = open(newrefname,
"w")
612 for l
in stderr.splitlines():
613 newref.write(l.rstrip() +
"\n")
615 result[
"New Error Reference File"] = os.path.relpath(
620 lreference,
"standard error",
"ExecTest.expected_stderr"
633 JSON validation action: compare json file to reference file
641 if not os.path.isfile(output_file):
642 causes.append(f
"output file {output_file} does not exist")
646 with open(output_file)
as f:
647 output = json.load(f)
648 except json.JSONDecodeError
as err:
649 causes.append(
"json parser error")
650 result[
"output_parse_error"] = f
"json parser error in {output_file}: {err}"
655 causes.append(
"reference file not set")
656 elif not os.path.isfile(lreference):
657 causes.append(
"reference file does not exist")
660 if causes
and lreference:
663 newrefname =
".".join([lreference,
"new"])
664 while os.path.exists(newrefname):
666 newrefname =
".".join([lreference,
"~%d~" % cnt,
"new"])
667 with open(newrefname,
"w")
as newref:
668 json.dump(output, newref, indent=4)
669 result[
"New JSON Output Reference File"] = os.path.relpath(
686 platformSplit =
lambda p: set(re.split(
r"[-+]", p))
688 reference = os.path.normpath(
689 os.path.join(self.
basedir, os.path.expandvars(reffile))
693 spec_ref = reference[:-3] +
GetPlatform(self)[0:3] + reference[-3:]
694 if os.path.isfile(spec_ref):
698 dirname, basename = os.path.split(reference)
701 head = basename +
"."
704 if "do0" in platform:
707 for f
in os.listdir(dirname):
708 if f.startswith(head):
709 req_plat = platformSplit(f[head_len:])
710 if platform.issuperset(req_plat):
711 candidates.append((len(req_plat), f))
716 reference = os.path.join(dirname, candidates[-1][1])
728 from GaudiKernel
import ROOT6WorkAroundEnabled
741 Function used to normalize the used path
743 newPath = os.path.normpath(os.path.expandvars(p))
744 if os.path.exists(newPath):
745 p = os.path.realpath(newPath)
751 Locates an executable in the executables path ($PATH) and returns the full
752 path to it. An application is looked for with or without the '.exe' suffix.
753 If the executable cannot be found, None is returned
755 if os.path.isabs(executable):
756 if not os.path.isfile(executable):
757 if executable.endswith(
".exe"):
758 if os.path.isfile(executable[:-4]):
759 return executable[:-4]
761 executable = os.path.split(executable)[1]
764 for d
in os.environ.get(
"PATH").split(os.pathsep):
765 fullpath = os.path.join(d, executable)
766 if os.path.isfile(fullpath):
768 elif executable.endswith(
".exe")
and os.path.isfile(fullpath[:-4]):
784 UNTESTED =
"UNTESTED"
794 def __init__(self, kind=None, id=None, outcome=PASS, annotations={}):
798 assert isinstance(key, six.string_types)
802 assert isinstance(key, six.string_types)
803 assert isinstance(value, six.string_types),
"{!r} is not a string".
format(value)
808 Convert text to html by escaping special chars and adding <pre> tags.
810 return "<pre>{}</pre>".
format(escape_for_html(text))
829 """Validate the output of the program.
830 'stdout' -- A string containing the data written to the standard output
832 'stderr' -- A string containing the data written to the standard error
834 'result' -- A 'Result' object. It may be used to annotate
835 the outcome according to the content of stderr.
836 returns -- A list of strings giving causes of failure."""
841 causes.append(self.
cause)
847 """Compare 's1' and 's2', ignoring line endings.
850 returns -- True if 's1' and 's2' are the same, ignoring
851 differences in line endings."""
855 to_ignore = re.compile(
856 r"Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*"
860 return not to_ignore.match(l)
862 return list(filter(keep_line, s1.splitlines())) == list(
863 filter(keep_line, s2.splitlines())
866 return s1.splitlines() == s2.splitlines()
871 """Base class for a callable that takes a file and returns a modified
886 if not isinstance(input, six.string_types):
890 lines = input.splitlines()
894 output =
"\n".join(output)
924 if line.find(s) >= 0:
939 if self.
start in line:
942 elif self.
end in line:
952 when = re.compile(when)
956 if isinstance(rhs, RegexpReplacer):
958 res._operations = self.
_operations + rhs._operations
960 res = FilePreprocessor.__add__(self, rhs)
965 if w
is None or w.search(line):
966 line = o.sub(r, line)
973 "[0-2]?[0-9]:[0-5][0-9]:[0-5][0-9] [0-9]{4}[-/][01][0-9][-/][0-3][0-9][ A-Z]*",
974 "00:00:00 1970-01-01",
977 normalizeEOL.__processLine__ =
lambda line: str(line).rstrip() +
"\n"
981 skipEmptyLines.__processLine__ =
lambda line: (line.strip()
and line)
or None
995 line = line[: (pos + self.
siglen)]
996 lst = line[(pos + self.
siglen) :].split()
998 line +=
" ".join(lst)
1004 Sort group of lines matching a regular expression
1008 self.
exp = exp
if hasattr(exp,
"match")
else re.compile(exp)
1011 match = self.
exp.match
1020 output.extend(group)
1027 normalizeExamples = maskPointers + normalizeDate
1030 (
"TIMER.TIMER",
r"\s+[+-]?[0-9]+[0-9.]*",
" 0"),
1031 (
"release all pending",
r"^.*/([^/]*:.*)",
r"\1"),
1032 (
"^#.*file",
r"file '.*[/\\]([^/\\]*)$",
r"file '\1"),
1034 "^JobOptionsSvc.*options successfully read in from",
1035 r"read in from .*[/\\]([^/\\]*)$",
1041 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}",
1042 "00000000-0000-0000-0000-000000000000",
1046 "ServiceLocatorHelper::",
1047 "ServiceLocatorHelper::(create|locate)Service",
1048 "ServiceLocatorHelper::service",
1051 (
None,
r"e([-+])0([0-9][0-9])",
r"e\1\2"),
1053 (
None,
r"Service reference count check:",
r"Looping over all active services..."),
1057 r"^(.*(DEBUG|SUCCESS) List of ALL properties of .*#properties = )\d+",
1060 (
"ApplicationMgr",
r"(declareMultiSvcType|addMultiSvc): ",
""),
1061 (
r"Property \['Name': Value\]",
r"( = '[^']+':)'(.*)'",
r"\1\2"),
1062 (
"TimelineSvc",
"to file 'TimelineFile':",
"to file "),
1063 (
"DataObjectHandleBase",
r'DataObjectHandleBase\("([^"]*)"\)',
r"'\1'"),
1070 "JobOptionsSvc INFO # ",
1071 "JobOptionsSvc WARNING # ",
1074 "This machine has a speed",
1076 "ToolSvc.Sequenc... INFO",
1077 "DataListenerSvc INFO XML written to file:",
1080 "DEBUG No writable file catalog found which contains FID:",
1081 "DEBUG Service base class initialized successfully",
1083 "DEBUG Incident timing:",
1087 "INFO 'CnvServices':[",
1089 "DEBUG 'CnvServices':[",
1094 "ServiceLocatorHelper::service: found service JobOptionsSvc",
1096 "mismatching case for property name:",
1098 "Histograms saving not required.",
1100 "Properties are dumped into",
1103 r"^JobOptionsSvc INFO *$",
1106 r"(Always|SUCCESS)\s*(Root f|[^ ]* F)ile version:",
1107 r"File '.*.xml' does not exist",
1108 r"INFO Refer to dataset .* by its file ID:",
1109 r"INFO Referring to dataset .* by its file ID:",
1110 r"INFO Disconnect from dataset",
1111 r"INFO Disconnected from dataset",
1112 r"INFO Disconnected data IO:",
1113 r"IncidentSvc\s*(DEBUG (Adding|Removing)|VERBOSE Calling)",
1115 r".*StatusCodeSvc.*",
1116 r".*StatusCodeCheck.*",
1117 r"Num\s*\|\s*Function\s*\|\s*Source Library",
1120 r"ERROR Failed to modify file: .* Errno=2 No such file or directory",
1122 r"^ +[0-9]+ \|.*ROOT",
1123 r"^ +[0-9]+ \|.*\|.*Dict",
1125 r"EventLoopMgr.*---> Loop Finished",
1126 r"HiveSlimEventLo.*---> Loop Finished",
1131 r"SUCCESS\s*Booked \d+ Histogram\(s\)",
1135 r"Property(.*)'Audit(Algorithm|Tool|Service)s':",
1136 r"Property(.*)'Audit(Begin|End)Run':",
1138 r"Property(.*)'AuditRe(start|initialize)':",
1139 r"Property(.*)'Blocking':",
1141 r"Property(.*)'ErrorCount(er)?':",
1143 r"Property(.*)'Sequential':",
1145 r"Property(.*)'FilterCircularDependencies':",
1147 r"Property(.*)'IsClonable':",
1149 r"Property update for OutputLevel : new value =",
1150 r"EventLoopMgr\s*DEBUG Creating OutputStream",
1159 r"Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*",
1163 normalizeExamples = (
1176 def __init__(self, reffile, cause, result_key, preproc=normalizeExamples):
1184 if os.path.isfile(self.
reffile):
1185 orig = open(self.
reffile).readlines()
1188 result[self.
result_key +
".preproc.orig"] = result.Quote(
1189 "\n".join(
map(str.strip, orig))
1193 new = stdout.splitlines()
1198 difflib.unified_diff(
1199 orig, new, n=1, fromfile=
"Reference file", tofile=
"Actual output"
1203 result[self.
result_key] = result.Quote(
"".join(filterdiffs))
1204 result[self.
result_key +
".preproc.new"] = result.Quote(
1205 "\n".join(
map(str.strip, new))
1207 causes.append(self.
cause)
1213 Scan stdout to find ROOT TTree summaries and digest them.
1215 stars = re.compile(
r"^\*+$")
1216 outlines = stdout.splitlines()
1217 nlines = len(outlines)
1223 while i < nlines
and not stars.match(outlines[i]):
1228 trees[tree[
"Name"]] = tree
1235 Check that all the keys in reference are in to_check too, with the same value.
1236 If the value is a dict, the function is called recursively. to_check can
1237 contain more keys than reference, that will not be tested.
1238 The function returns at the first difference found.
1243 ignore_re = re.compile(ignore)
1244 keys = [key
for key
in reference
if not ignore_re.match(key)]
1246 keys = reference.keys()
1250 if (
type(reference[k])
is dict)
and (
type(to_check[k])
is dict):
1253 failed = fail_keys =
cmpTreesDicts(reference[k], to_check[k], ignore)
1256 failed = to_check[k] != reference[k]
1261 fail_keys.insert(0, k)
1272 if c
is None or r
is None:
1274 return (fail_path, r, c)
1278 h_count_re = re.compile(
r"^(.*)SUCCESS\s+Booked (\d+) Histogram\(s\) :\s+([\s\w=-]*)")
1283 Parse the TTree summary table in lines, starting from pos.
1284 Returns a tuple with the dictionary with the digested informations and the
1285 position of the first line after the summary.
1292 return [f.strip()
for f
in l.strip(
"*\n").split(
":", 2)]
1296 cols = splitcols(ll[0])
1297 r[
"Name"], r[
"Title"] = cols[1:]
1299 cols = splitcols(ll[1])
1300 r[
"Entries"] = int(cols[1])
1302 sizes = cols[2].split()
1303 r[
"Total size"] = int(sizes[2])
1304 if sizes[-1] ==
"memory":
1307 r[
"File size"] = int(sizes[-1])
1309 cols = splitcols(ll[2])
1310 sizes = cols[2].split()
1311 if cols[0] ==
"Baskets":
1312 r[
"Baskets"] = int(cols[1])
1313 r[
"Basket size"] = int(sizes[2])
1314 r[
"Compression"] = float(sizes[-1])
1317 if i < (count - 3)
and lines[i].startswith(
"*Tree"):
1318 result = parseblock(lines[i : i + 3])
1319 result[
"Branches"] = {}
1321 while i < (count - 3)
and lines[i].startswith(
"*Br"):
1322 if i < (count - 2)
and lines[i].startswith(
"*Branch "):
1326 branch = parseblock(lines[i : i + 3])
1327 result[
"Branches"][branch[
"Name"]] = branch
1335 Extract the histograms infos from the lines starting at pos.
1336 Returns the position of the first line after the summary block.
1339 h_table_head = re.compile(
1340 r'SUCCESS\s+(1D|2D|3D|1D profile|2D profile) histograms in directory\s+"(\w*)"'
1342 h_short_summ = re.compile(
r"ID=([^\"]+)\s+\"([^\"]+)\"\s+(.*)")
1347 m = h_count_re.search(lines[pos])
1348 name = m.group(1).strip()
1349 total = int(m.group(2))
1351 for k, v
in [x.split(
"=")
for x
in m.group(3).split()]:
1354 header[
"Total"] = total
1358 m = h_table_head.search(lines[pos])
1361 t = t.replace(
" profile",
"Prof")
1368 if l.startswith(
" | ID"):
1370 titles = [x.strip()
for x
in l.split(
"|")][1:]
1372 while pos < nlines
and lines[pos].startswith(
" |"):
1374 values = [x.strip()
for x
in l.split(
"|")][1:]
1376 for i
in range(len(titles)):
1377 hcont[titles[i]] = values[i]
1378 cont[hcont[
"ID"]] = hcont
1380 elif l.startswith(
" ID="):
1381 while pos < nlines
and lines[pos].startswith(
" ID="):
1383 x.strip()
for x
in h_short_summ.search(lines[pos]).groups()
1385 cont[values[0]] = values
1388 raise RuntimeError(
"Cannot understand line %d: '%s'" % (pos, l))
1392 summ[d][
"header"] = header
1397 summ[name] = {
"header": header}
1403 Scan stdout to find ROOT TTree summaries and digest them.
1405 outlines = stdout.splitlines()
1406 nlines = len(outlines) - 1
1414 match = h_count_re.search(outlines[pos])
1415 while pos < nlines
and not match:
1417 match = h_count_re.search(outlines[pos])
1420 summaries.update(summ)
1426 Return the platform Id defined in CMTCONFIG or SCRAM_ARCH.
1430 if "BINARY_TAG" in os.environ:
1431 arch = os.environ[
"BINARY_TAG"]
1432 elif "CMTCONFIG" in os.environ:
1433 arch = os.environ[
"CMTCONFIG"]
1434 elif "SCRAM_ARCH" in os.environ:
1435 arch = os.environ[
"SCRAM_ARCH"]
1436 elif os.environ.get(
"ENV_CMAKE_BUILD_TYPE",
"")
in (
1442 elif os.environ.get(
"ENV_CMAKE_BUILD_TYPE",
"")
in (
1454 Return True if the current platform is Windows.
1456 This function was needed because of the change in the CMTCONFIG format,
1457 from win32_vc71_dbg to i686-winxp-vc9-dbg.
1460 return "winxp" in platform
or platform.startswith(
"win")
1465 """Validate JSON output.
1466 returns -- A list of strings giving causes of failure."""
1470 with open(ref)
as f:
1471 expected = json.load(f)
1472 except json.JSONDecodeError
as err:
1473 causes.append(
"json parser error")
1474 result[
"reference_parse_error"] = f
"json parser error in {ref}: {err}"
1479 causes.append(
"json content")
1480 result[
"json_diff"] =
"detailed diff was turned off"
1486 t.assertEqual(expected, out)
1487 except AssertionError
as err:
1488 causes.append(
"json content")
1489 result[
"json_diff"] = str(err).splitlines()[0]