-
Notifications
You must be signed in to change notification settings - Fork 0
/
vcloud2junit.py
executable file
·146 lines (114 loc) · 4.6 KB
/
vcloud2junit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env python3
from __future__ import annotations
import bz2
from dataclasses import dataclass
from datetime import datetime
from enum import auto, Enum, unique
from io import TextIOWrapper
from os.path import basename
from sys import argv
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
from zipfile import ZipFile
@unique
class Status(Enum):
TRUE_POSITIVE = auto()
TRUE_NEGATIVE = auto()
FALSE_POSITIVE = auto()
FALSE_NEGATIVE = auto()
DONT_KNOW = auto()
@unique
class Verdict(Enum):
TRUE = 'true'
FALSE = 'false'
OUT_OF_MEMORY = 'OUT OF MEMORY'
TIMEOUT = 'TIMEOUT'
UNKNOWN = 'unknown'
@dataclass
class Run:
name: str
expected_verdict: Verdict
verdict: Verdict
hostname: str
timestamp: datetime
walltime: float
output: str
@property
def status(self: Run) -> Status:
if self.verdict in {Verdict.OUT_OF_MEMORY, Verdict.TIMEOUT, Verdict.UNKNOWN}:
return Status.DONT_KNOW
elif self.verdict == Verdict.TRUE:
if self.expected_verdict == Verdict.TRUE:
return Status.TRUE_POSITIVE
else:
return Status.FALSE_POSITIVE
elif self.verdict == Verdict.FALSE:
if self.expected_verdict == Verdict.FALSE:
return Status.TRUE_NEGATIVE
else:
return Status.FALSE_NEGATIVE
raise ValueError(f"verdict {self.verdict} unhandled")
def main(source: str, target: str) -> None:
result = ElementTree.parse(bz2.open(source)).getroot()
name = result.get("name")
name_prefix = name.rsplit(".", maxsplit=1)[0]
logfiles = source.split(".results.")[0] + ".logfiles"
runs = []
with ZipFile(f"{logfiles}.zip") as log_zip:
for child in result:
if child.tag == "run":
runs.append(read_run(child, f"{basename(logfiles)}/{name_prefix}", log_zip))
testsuites = ElementTree.Element("testsuites")
for run_id, run in enumerate(runs):
write_run(testsuites, run, run_id, name)
ElementTree.ElementTree(testsuites).write(target)
def read_run(run: Element, name_prefix: str, log_zip: ZipFile) -> Run:
name = run.get("name")
expected_verdict = run.get("expectedVerdict")
hostname = starttime = verdict = walltime = None
for column in run:
if column.get("title") == "host":
hostname = column.get("value")
elif column.get("title") == "starttime":
starttime = column.get("value")
elif column.get("title") == "status":
verdict = column.get("value")
elif column.get("title") == "walltime":
walltime = column.get("value")
with TextIOWrapper(log_zip.open(f"{name_prefix}.{basename(name)}.log")) as log:
output = log.read()
if not hostname or not starttime or not verdict or not walltime:
raise ValueError("column(s) missing")
return Run(name, Verdict(expected_verdict), Verdict(verdict), hostname, datetime.fromisoformat(starttime),
float(walltime[:-1]), output)
def write_run(testsuites: Element, run: Run, run_id: int, name: str) -> None:
testsuite = ElementTree.SubElement(testsuites, "testsuite")
ElementTree.SubElement(testsuite, "properties")
ElementTree.SubElement(testsuite, "system-err")
testsuite.set("id", str(run_id))
testsuite.set("package", name)
testsuite.set("errors", "0")
testsuite.set("tests", "1")
testsuite.set("name", run.name)
testsuite.set("hostname", run.hostname)
testsuite.set("timestamp", run.timestamp.isoformat())
testsuite.set("time", str(run.walltime))
sysout = ElementTree.SubElement(testsuite, "system-out")
sysout.text = run.output[:1000] # libXML does not accept large texts
testcase = ElementTree.SubElement(testsuite, "testcase")
testcase.set("classname", basename(run.name))
testcase.set("name", run.name)
testcase.set("time", str(run.walltime))
if run.status == Status.FALSE_NEGATIVE or run.status == Status.FALSE_POSITIVE:
testsuite.set("failures", "1")
failure = ElementTree.SubElement(testcase, "failure")
failure.set("type", str(run.status))
failure.set("message", f"Incorrect verdict: {run.verdict.value} (expected: {run.expected_verdict.value})")
elif run.status == Status.DONT_KNOW:
testsuite.set("failures", "0")
skipped = ElementTree.SubElement(testcase, "skipped")
skipped.set("message", f"No verdict: {run.verdict.value} (expected: {run.expected_verdict.value})")
else:
testsuite.set("failures", "0")
if __name__ == '__main__':
main(argv[1], argv[2])