2 from xml.sax.saxutils import escape
3 from json import JSONEncoder
7 class ResultCode(object):
8 """Test result codes."""
10 # We override __new__ and __getnewargs__ to ensure that pickling still
11 # provides unique ResultCode objects in any particular instance.
13 def __new__(cls, name, isFailure):
14 res = cls._instances.get(name)
16 cls._instances[name] = res = super(ResultCode, cls).__new__(cls)
18 def __getnewargs__(self):
19 return (self.name, self.isFailure)
21 def __init__(self, name, isFailure):
23 self.isFailure = isFailure
26 return '%s%r' % (self.__class__.__name__,
27 (self.name, self.isFailure))
29 PASS = ResultCode('PASS', False)
30 FLAKYPASS = ResultCode('FLAKYPASS', False)
31 XFAIL = ResultCode('XFAIL', False)
32 FAIL = ResultCode('FAIL', True)
33 XPASS = ResultCode('XPASS', True)
34 UNRESOLVED = ResultCode('UNRESOLVED', True)
35 UNSUPPORTED = ResultCode('UNSUPPORTED', False)
36 TIMEOUT = ResultCode('TIMEOUT', True)
40 class MetricValue(object):
45 Convert this metric to a string suitable for displaying as part of the
48 raise RuntimeError("abstract method")
52 todata() -> json-serializable data
54 Convert this metric to content suitable for serializing in the JSON test
57 raise RuntimeError("abstract method")
59 class IntMetricValue(MetricValue):
60 def __init__(self, value):
64 return str(self.value)
69 class RealMetricValue(MetricValue):
70 def __init__(self, value):
74 return '%.4f' % self.value
79 class JSONMetricValue(MetricValue):
81 JSONMetricValue is used for types that are representable in the output
82 but that are otherwise uninterpreted.
84 def __init__(self, value):
85 # Ensure the value is a serializable by trying to encode it.
86 # WARNING: The value may change before it is encoded again, and may
87 # not be encodable after the change.
96 e = JSONEncoder(indent=2, sort_keys=True)
97 return e.encode(self.value)
102 def toMetricValue(value):
103 if isinstance(value, MetricValue):
105 elif isinstance(value, int) or isinstance(value, long):
106 return IntMetricValue(value)
107 elif isinstance(value, float):
108 return RealMetricValue(value)
110 # Try to create a JSONMetricValue and let the constructor throw
111 # if value is not a valid type.
112 return JSONMetricValue(value)
117 class Result(object):
118 """Wrapper for the results of executing an individual test."""
120 def __init__(self, code, output='', elapsed=None):
125 # The wall timing to execute the test, if timing.
126 self.elapsed = elapsed
127 # The metrics reported by this test.
130 def addMetric(self, name, value):
132 addMetric(name, value)
134 Attach a test metric to the test result, with the given name and list of
135 values. It is an error to attempt to attach the metrics with the same
138 Each value must be an instance of a MetricValue subclass.
140 if name in self.metrics:
141 raise ValueError("result already includes metrics for %r" % (
143 if not isinstance(value, MetricValue):
144 raise TypeError("unexpected metric value: %r" % (value,))
145 self.metrics[name] = value
150 """TestSuite - Information on a group of tests.
152 A test suite groups together a set of logically related tests.
155 def __init__(self, name, source_root, exec_root, config):
157 self.source_root = source_root
158 self.exec_root = exec_root
159 # The test suite configuration.
162 def getSourcePath(self, components):
163 return os.path.join(self.source_root, *components)
165 def getExecPath(self, components):
166 return os.path.join(self.exec_root, *components)
169 """Test - Information on a single test instance."""
171 def __init__(self, suite, path_in_suite, config, file_path = None):
173 self.path_in_suite = path_in_suite
175 self.file_path = file_path
176 # A list of conditions under which this test is expected to fail. These
177 # can optionally be provided by test format handlers, and will be
178 # honored when the test result is supplied.
180 # The test result, once complete.
183 def setResult(self, result):
184 if self.result is not None:
185 raise ArgumentError("test result already set")
186 if not isinstance(result, Result):
187 raise ArgumentError("unexpected result type")
191 # Apply the XFAIL handling to resolve the result exit code.
192 if self.isExpectedToFail():
193 if self.result.code == PASS:
194 self.result.code = XPASS
195 elif self.result.code == FAIL:
196 self.result.code = XFAIL
198 def getFullName(self):
199 return self.suite.config.name + ' :: ' + '/'.join(self.path_in_suite)
201 def getFilePath(self):
203 return self.file_path
204 return self.getSourcePath()
206 def getSourcePath(self):
207 return self.suite.getSourcePath(self.path_in_suite)
209 def getExecPath(self):
210 return self.suite.getExecPath(self.path_in_suite)
212 def isExpectedToFail(self):
214 isExpectedToFail() -> bool
216 Check whether this test is expected to fail in the current
217 configuration. This check relies on the test xfails property which by
218 some test formats may not be computed until the test has first been
222 # Check if any of the xfails match an available feature or the target.
223 for item in self.xfails:
224 # If this is the wildcard, it always fails.
228 # If this is an exact match for one of the features, it fails.
229 if item in self.config.available_features:
232 # If this is a part of the target triple, it fails.
233 if item in self.suite.config.target_triple:
239 def getJUnitXML(self):
240 test_name = self.path_in_suite[-1]
241 test_path = self.path_in_suite[:-1]
242 safe_test_path = [x.replace(".","_") for x in test_path]
243 safe_name = self.suite.name.replace(".","-")
246 class_name = safe_name + "." + "/".join(safe_test_path)
248 class_name = safe_name + "." + safe_name
250 xml = "<testcase classname='" + class_name + "' name='" + \
252 xml += " time='%.2f'" % (self.result.elapsed,)
253 if self.result.code.isFailure:
254 xml += ">\n\t<failure >\n" + escape(self.result.output)
255 xml += "\n\t</failure>\n</testcase>"