results.py (11828:36b064696175) results.py (12581:a8f1d31d3492)
1#!/usr/bin/env python2
2#
3# Copyright (c) 2016 ARM Limited
4# All rights reserved
5#
6# The license below extends only to copyright in the software and shall
7# not be construed as granting a license to any other intellectual
8# property including but not limited to intellectual property relating
9# to a hardware implementation of the functionality of the software
10# licensed hereunder. You may use the software subject to the license
11# terms below provided that you ensure that this notice is replicated
12# unmodified and in its entirety in all distributions of the software,
13# modified or unmodified, in source code or in binary form.
14#
15# Redistribution and use in source and binary forms, with or without
16# modification, are permitted provided that the following conditions are
17# met: redistributions of source code must retain the above copyright
18# notice, this list of conditions and the following disclaimer;
19# redistributions in binary form must reproduce the above copyright
20# notice, this list of conditions and the following disclaimer in the
21# documentation and/or other materials provided with the distribution;
22# neither the name of the copyright holders nor the names of its
23# contributors may be used to endorse or promote products derived from
24# this software without specific prior written permission.
25#
26# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37#
38# Authors: Andreas Sandberg
39
1#!/usr/bin/env python2
2#
3# Copyright (c) 2016 ARM Limited
4# All rights reserved
5#
6# The license below extends only to copyright in the software and shall
7# not be construed as granting a license to any other intellectual
8# property including but not limited to intellectual property relating
9# to a hardware implementation of the functionality of the software
10# licensed hereunder. You may use the software subject to the license
11# terms below provided that you ensure that this notice is replicated
12# unmodified and in its entirety in all distributions of the software,
13# modified or unmodified, in source code or in binary form.
14#
15# Redistribution and use in source and binary forms, with or without
16# modification, are permitted provided that the following conditions are
17# met: redistributions of source code must retain the above copyright
18# notice, this list of conditions and the following disclaimer;
19# redistributions in binary form must reproduce the above copyright
20# notice, this list of conditions and the following disclaimer in the
21# documentation and/or other materials provided with the distribution;
22# neither the name of the copyright holders nor the names of its
23# contributors may be used to endorse or promote products derived from
24# this software without specific prior written permission.
25#
26# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37#
38# Authors: Andreas Sandberg
39
40from __future__ import print_function
41
40from abc import ABCMeta, abstractmethod
41import inspect
42import pickle
43import string
44import sys
45
46import xml.etree.cElementTree as ET
47
48class UnitResult(object):
49 """Results of a single test unit.
50
51 A test result can be one of:
52 - STATE_OK: Test ran successfully.
53 - STATE_SKIPPED: The test was skipped.
54 - STATE_ERROR: The test failed to run.
55 - STATE_FAILED: Test ran, but failed.
56
57 The difference between STATE_ERROR and STATE_FAILED is very
58 subtle. In a gem5 context, STATE_ERROR would mean that gem5 failed
59 to start or crashed, while STATE_FAILED would mean that a test
60 failed (e.g., statistics mismatch).
61
62 """
63
64 STATE_OK = 0
65 STATE_SKIPPED = 1
66 STATE_ERROR = 2
67 STATE_FAILURE = 3
68
69 state_names = {
70 STATE_OK : "OK",
71 STATE_SKIPPED : "SKIPPED",
72 STATE_ERROR : "ERROR",
73 STATE_FAILURE : "FAILURE",
74 }
75
76 def __init__(self, name, state, message="", stderr="", stdout="",
77 runtime=0.0):
78 self.name = name
79 self.state = state
80 self.message = message
81 self.stdout = stdout
82 self.stderr = stderr
83 self.runtime = runtime
84
85 def skipped(self):
86 return self.state == UnitResult.STATE_SKIPPED
87
88 def success(self):
89 return self.state == UnitResult.STATE_OK
90
91 def state_name(self):
92 return UnitResult.state_names[self.state]
93
94 def __nonzero__(self):
95 return self.success() or self.skipped()
96
97 def __str__(self):
98 state_name = self.state_name()
99
100 status = "%s: %s" % (state_name, self.message) if self.message else \
101 state_name
102
103 return "%s: %s" % (self.name, status)
104
105class TestResult(object):
106 """Results for from a single test consisting of one or more units."""
107
108 def __init__(self, name, run_results=[], verify_results=[]):
109 self.name = name
110 self.results = run_results + verify_results
111 self.run_results = run_results
112 self.verify_results = verify_results
113
114 def success(self):
115 return self.success_run() and self.success_verify()
116
117 def success_run(self):
118 return all([ r.success() for r in self.run_results ])
119
120 def success_verify(self):
121 return all([ r.success() for r in self.verify_results ])
122
123 def failed(self):
124 return self.failed_run() or self.failed_verify()
125
126 def failed_run(self):
127 return any([ not r for r in self.run_results ])
128
129 def failed_verify(self):
130 return any([ not r for r in self.verify_results ])
131
132 def skipped(self):
133 return all([ r.skipped() for r in self.run_results ])
134
135 def changed(self):
136 return self.success_run() and self.failed_verify()
137
138 def runtime(self):
139 return sum([ r.runtime for r in self.results ])
140
141 def __nonzero__(self):
142 return all([ r for r in self.results ])
143
144class ResultFormatter(object):
145 __metaclass__ = ABCMeta
146
147 def __init__(self, fout=sys.stdout, verbose=False):
148 self.verbose = verbose
149 self.fout = fout
150
151 @abstractmethod
152 def dump_suites(self, suites):
153 pass
154
155class Pickle(ResultFormatter):
156 """Save test results as a binary using Python's pickle
157 functionality.
158
159 """
160
161 def __init__(self, **kwargs):
162 super(Pickle, self).__init__(**kwargs)
163
164 def dump_suites(self, suites):
165 pickle.dump(suites, self.fout, pickle.HIGHEST_PROTOCOL)
166
167class Text(ResultFormatter):
168 """Output test results as text."""
169
170 def __init__(self, **kwargs):
171 super(Text, self).__init__(**kwargs)
172
173 def dump_suites(self, suites):
174 fout = self.fout
175 for suite in suites:
42from abc import ABCMeta, abstractmethod
43import inspect
44import pickle
45import string
46import sys
47
48import xml.etree.cElementTree as ET
49
50class UnitResult(object):
51 """Results of a single test unit.
52
53 A test result can be one of:
54 - STATE_OK: Test ran successfully.
55 - STATE_SKIPPED: The test was skipped.
56 - STATE_ERROR: The test failed to run.
57 - STATE_FAILED: Test ran, but failed.
58
59 The difference between STATE_ERROR and STATE_FAILED is very
60 subtle. In a gem5 context, STATE_ERROR would mean that gem5 failed
61 to start or crashed, while STATE_FAILED would mean that a test
62 failed (e.g., statistics mismatch).
63
64 """
65
66 STATE_OK = 0
67 STATE_SKIPPED = 1
68 STATE_ERROR = 2
69 STATE_FAILURE = 3
70
71 state_names = {
72 STATE_OK : "OK",
73 STATE_SKIPPED : "SKIPPED",
74 STATE_ERROR : "ERROR",
75 STATE_FAILURE : "FAILURE",
76 }
77
78 def __init__(self, name, state, message="", stderr="", stdout="",
79 runtime=0.0):
80 self.name = name
81 self.state = state
82 self.message = message
83 self.stdout = stdout
84 self.stderr = stderr
85 self.runtime = runtime
86
87 def skipped(self):
88 return self.state == UnitResult.STATE_SKIPPED
89
90 def success(self):
91 return self.state == UnitResult.STATE_OK
92
93 def state_name(self):
94 return UnitResult.state_names[self.state]
95
96 def __nonzero__(self):
97 return self.success() or self.skipped()
98
99 def __str__(self):
100 state_name = self.state_name()
101
102 status = "%s: %s" % (state_name, self.message) if self.message else \
103 state_name
104
105 return "%s: %s" % (self.name, status)
106
107class TestResult(object):
108 """Results for from a single test consisting of one or more units."""
109
110 def __init__(self, name, run_results=[], verify_results=[]):
111 self.name = name
112 self.results = run_results + verify_results
113 self.run_results = run_results
114 self.verify_results = verify_results
115
116 def success(self):
117 return self.success_run() and self.success_verify()
118
119 def success_run(self):
120 return all([ r.success() for r in self.run_results ])
121
122 def success_verify(self):
123 return all([ r.success() for r in self.verify_results ])
124
125 def failed(self):
126 return self.failed_run() or self.failed_verify()
127
128 def failed_run(self):
129 return any([ not r for r in self.run_results ])
130
131 def failed_verify(self):
132 return any([ not r for r in self.verify_results ])
133
134 def skipped(self):
135 return all([ r.skipped() for r in self.run_results ])
136
137 def changed(self):
138 return self.success_run() and self.failed_verify()
139
140 def runtime(self):
141 return sum([ r.runtime for r in self.results ])
142
143 def __nonzero__(self):
144 return all([ r for r in self.results ])
145
146class ResultFormatter(object):
147 __metaclass__ = ABCMeta
148
149 def __init__(self, fout=sys.stdout, verbose=False):
150 self.verbose = verbose
151 self.fout = fout
152
153 @abstractmethod
154 def dump_suites(self, suites):
155 pass
156
157class Pickle(ResultFormatter):
158 """Save test results as a binary using Python's pickle
159 functionality.
160
161 """
162
163 def __init__(self, **kwargs):
164 super(Pickle, self).__init__(**kwargs)
165
166 def dump_suites(self, suites):
167 pickle.dump(suites, self.fout, pickle.HIGHEST_PROTOCOL)
168
169class Text(ResultFormatter):
170 """Output test results as text."""
171
172 def __init__(self, **kwargs):
173 super(Text, self).__init__(**kwargs)
174
175 def dump_suites(self, suites):
176 fout = self.fout
177 for suite in suites:
176 print >> fout, "--- %s ---" % suite.name
178 print("--- %s ---" % suite.name, file=fout)
177
178 for t in suite.results:
179
180 for t in suite.results:
179 print >> fout, "*** %s" % t
181 print("*** %s" % t, file=fout)
180
181 if t and not self.verbose:
182 continue
183
184 if t.message:
182
183 if t and not self.verbose:
184 continue
185
186 if t.message:
185 print >> fout, t.message
187 print(t.message, file=fout)
186
187 if t.stderr:
188
189 if t.stderr:
188 print >> fout, t.stderr
190 print(t.stderr, file=fout)
189 if t.stdout:
191 if t.stdout:
190 print >> fout, t.stdout
192 print(t.stdout, file=fout)
191
192class TextSummary(ResultFormatter):
193 """Output test results as a text summary"""
194
195 def __init__(self, **kwargs):
196 super(TextSummary, self).__init__(**kwargs)
197
198 def test_status(self, suite):
199 if suite.skipped():
200 return "SKIPPED"
201 elif suite.changed():
202 return "CHANGED"
203 elif suite:
204 return "OK"
205 else:
206 return "FAILED"
207
208 def dump_suites(self, suites):
209 fout = self.fout
210 for suite in suites:
211 status = self.test_status(suite)
193
194class TextSummary(ResultFormatter):
195 """Output test results as a text summary"""
196
197 def __init__(self, **kwargs):
198 super(TextSummary, self).__init__(**kwargs)
199
200 def test_status(self, suite):
201 if suite.skipped():
202 return "SKIPPED"
203 elif suite.changed():
204 return "CHANGED"
205 elif suite:
206 return "OK"
207 else:
208 return "FAILED"
209
210 def dump_suites(self, suites):
211 fout = self.fout
212 for suite in suites:
213 status = self.test_status(suite)
212 print >> fout, "%s: %s" % (suite.name, status)
214 print("%s: %s" % (suite.name, status), file=fout)
213
214class JUnit(ResultFormatter):
215 """Output test results as JUnit XML"""
216
217 def __init__(self, translate_names=True, **kwargs):
218 super(JUnit, self).__init__(**kwargs)
219
220 if translate_names:
221 self.name_table = string.maketrans(
222 "/.",
223 ".-",
224 )
225 else:
226 self.name_table = string.maketrans("", "")
227
228 def convert_unit(self, x_suite, test):
229 x_test = ET.SubElement(x_suite, "testcase",
230 name=test.name,
231 time="%f" % test.runtime)
232
233 x_state = None
234 if test.state == UnitResult.STATE_OK:
235 pass
236 elif test.state == UnitResult.STATE_SKIPPED:
237 x_state = ET.SubElement(x_test, "skipped")
238 elif test.state == UnitResult.STATE_FAILURE:
239 x_state = ET.SubElement(x_test, "failure")
240 elif test.state == UnitResult.STATE_ERROR:
241 x_state = ET.SubElement(x_test, "error")
242 else:
243 assert False, "Unknown test state"
244
245 if x_state is not None:
246 if test.message:
247 x_state.set("message", test.message)
248
249 msg = []
250 if test.stderr:
251 msg.append("*** Standard Errror: ***")
252 msg.append(test.stderr)
253 if test.stdout:
254 msg.append("*** Standard Out: ***")
255 msg.append(test.stdout)
256
257 x_state.text = "\n".join(msg)
258
259 return x_test
260
261 def convert_suite(self, x_suites, suite):
262 x_suite = ET.SubElement(x_suites, "testsuite",
263 name=suite.name.translate(self.name_table),
264 time="%f" % suite.runtime())
265 errors = 0
266 failures = 0
267 skipped = 0
268
269 for test in suite.results:
270 if test.state != UnitResult.STATE_OK:
271 if test.state == UnitResult.STATE_SKIPPED:
272 skipped += 1
273 elif test.state == UnitResult.STATE_ERROR:
274 errors += 1
275 elif test.state == UnitResult.STATE_FAILURE:
276 failures += 1
277
278 x_test = self.convert_unit(x_suite, test)
279
280 x_suite.set("errors", str(errors))
281 x_suite.set("failures", str(failures))
282 x_suite.set("skipped", str(skipped))
283 x_suite.set("tests", str(len(suite.results)))
284
285 return x_suite
286
287 def convert_suites(self, suites):
288 x_root = ET.Element("testsuites")
289
290 for suite in suites:
291 self.convert_suite(x_root, suite)
292
293 return x_root
294
295 def dump_suites(self, suites):
296 et = ET.ElementTree(self.convert_suites(suites))
297 et.write(self.fout, encoding="UTF-8")
215
216class JUnit(ResultFormatter):
217 """Output test results as JUnit XML"""
218
219 def __init__(self, translate_names=True, **kwargs):
220 super(JUnit, self).__init__(**kwargs)
221
222 if translate_names:
223 self.name_table = string.maketrans(
224 "/.",
225 ".-",
226 )
227 else:
228 self.name_table = string.maketrans("", "")
229
230 def convert_unit(self, x_suite, test):
231 x_test = ET.SubElement(x_suite, "testcase",
232 name=test.name,
233 time="%f" % test.runtime)
234
235 x_state = None
236 if test.state == UnitResult.STATE_OK:
237 pass
238 elif test.state == UnitResult.STATE_SKIPPED:
239 x_state = ET.SubElement(x_test, "skipped")
240 elif test.state == UnitResult.STATE_FAILURE:
241 x_state = ET.SubElement(x_test, "failure")
242 elif test.state == UnitResult.STATE_ERROR:
243 x_state = ET.SubElement(x_test, "error")
244 else:
245 assert False, "Unknown test state"
246
247 if x_state is not None:
248 if test.message:
249 x_state.set("message", test.message)
250
251 msg = []
252 if test.stderr:
253 msg.append("*** Standard Errror: ***")
254 msg.append(test.stderr)
255 if test.stdout:
256 msg.append("*** Standard Out: ***")
257 msg.append(test.stdout)
258
259 x_state.text = "\n".join(msg)
260
261 return x_test
262
263 def convert_suite(self, x_suites, suite):
264 x_suite = ET.SubElement(x_suites, "testsuite",
265 name=suite.name.translate(self.name_table),
266 time="%f" % suite.runtime())
267 errors = 0
268 failures = 0
269 skipped = 0
270
271 for test in suite.results:
272 if test.state != UnitResult.STATE_OK:
273 if test.state == UnitResult.STATE_SKIPPED:
274 skipped += 1
275 elif test.state == UnitResult.STATE_ERROR:
276 errors += 1
277 elif test.state == UnitResult.STATE_FAILURE:
278 failures += 1
279
280 x_test = self.convert_unit(x_suite, test)
281
282 x_suite.set("errors", str(errors))
283 x_suite.set("failures", str(failures))
284 x_suite.set("skipped", str(skipped))
285 x_suite.set("tests", str(len(suite.results)))
286
287 return x_suite
288
289 def convert_suites(self, suites):
290 x_root = ET.Element("testsuites")
291
292 for suite in suites:
293 self.convert_suite(x_root, suite)
294
295 return x_root
296
297 def dump_suites(self, suites):
298 et = ET.ElementTree(self.convert_suites(suites))
299 et.write(self.fout, encoding="UTF-8")