63d31b9212c0a1fe9845f7cb3679396e1393283b
[oota-llvm.git] / utils / lit / lit / Test.py
1 import os
2 from xml.sax.saxutils import escape
3
4 # Test result codes.
5
6 class ResultCode(object):
7     """Test result codes."""
8
9     # We override __new__ and __getnewargs__ to ensure that pickling still
10     # provides unique ResultCode objects in any particular instance.
11     _instances = {}
12     def __new__(cls, name, isFailure):
13         res = cls._instances.get(name)
14         if res is None:
15             cls._instances[name] = res = super(ResultCode, cls).__new__(cls)
16         return res
17     def __getnewargs__(self):
18         return (self.name, self.isFailure)
19
20     def __init__(self, name, isFailure):
21         self.name = name
22         self.isFailure = isFailure
23
24     def __repr__(self):
25         return '%s%r' % (self.__class__.__name__,
26                          (self.name, self.isFailure))
27
28 PASS        = ResultCode('PASS', False)
29 XFAIL       = ResultCode('XFAIL', False)
30 FAIL        = ResultCode('FAIL', True)
31 XPASS       = ResultCode('XPASS', True)
32 UNRESOLVED  = ResultCode('UNRESOLVED', True)
33 UNSUPPORTED = ResultCode('UNSUPPORTED', False)
34
35 # Test metric values.
36
37 class MetricValue(object):
38     def format(self):
39         """
40         format() -> str
41
42         Convert this metric to a string suitable for displaying as part of the
43         console output.
44         """
45         raise RuntimeError("abstract method")
46
47     def todata(self):
48         """
49         todata() -> json-serializable data
50
51         Convert this metric to content suitable for serializing in the JSON test
52         output.
53         """
54         raise RuntimeError("abstract method")
55
56 class IntMetricValue(MetricValue):
57     def __init__(self, value):
58         self.value = value
59
60     def format(self):
61         return str(self.value)
62
63     def todata(self):
64         return self.value
65
66 class RealMetricValue(MetricValue):
67     def __init__(self, value):
68         self.value = value
69
70     def format(self):
71         return '%.4f' % self.value
72
73     def todata(self):
74         return self.value
75
76 # Test results.
77
78 class Result(object):
79     """Wrapper for the results of executing an individual test."""
80
81     def __init__(self, code, output='', elapsed=None):
82         # The result code.
83         self.code = code
84         # The test output.
85         self.output = output
86         # The wall timing to execute the test, if timing.
87         self.elapsed = elapsed
88         # The metrics reported by this test.
89         self.metrics = {}
90
91     def addMetric(self, name, value):
92         """
93         addMetric(name, value)
94
95         Attach a test metric to the test result, with the given name and list of
96         values. It is an error to attempt to attach the metrics with the same
97         name multiple times.
98
99         Each value must be an instance of a MetricValue subclass.
100         """
101         if name in self.metrics:
102             raise ValueError("result already includes metrics for %r" % (
103                     name,))
104         if not isinstance(value, MetricValue):
105             raise TypeError("unexpected metric value: %r" % (value,))
106         self.metrics[name] = value
107
108 # Test classes.
109
110 class TestSuite:
111     """TestSuite - Information on a group of tests.
112
113     A test suite groups together a set of logically related tests.
114     """
115
116     def __init__(self, name, source_root, exec_root, config):
117         self.name = name
118         self.source_root = source_root
119         self.exec_root = exec_root
120         # The test suite configuration.
121         self.config = config
122
123     def getSourcePath(self, components):
124         return os.path.join(self.source_root, *components)
125
126     def getExecPath(self, components):
127         return os.path.join(self.exec_root, *components)
128
129 class Test:
130     """Test - Information on a single test instance."""
131
132     def __init__(self, suite, path_in_suite, config, file_path = None):
133         self.suite = suite
134         self.path_in_suite = path_in_suite
135         self.config = config
136         self.file_path = file_path
137         # A list of conditions under which this test is expected to fail. These
138         # can optionally be provided by test format handlers, and will be
139         # honored when the test result is supplied.
140         self.xfails = []
141         # The test result, once complete.
142         self.result = None
143
144     def setResult(self, result):
145         if self.result is not None:
146             raise ArgumentError("test result already set")
147         if not isinstance(result, Result):
148             raise ArgumentError("unexpected result type")
149
150         self.result = result
151
152         # Apply the XFAIL handling to resolve the result exit code.
153         if self.isExpectedToFail():
154             if self.result.code == PASS:
155                 self.result.code = XPASS
156             elif self.result.code == FAIL:
157                 self.result.code = XFAIL
158         
159     def getFullName(self):
160         return self.suite.config.name + ' :: ' + '/'.join(self.path_in_suite)
161
162     def getFilePath(self):
163         if self.file_path:
164             return self.file_path
165         return self.getSourcePath()
166
167     def getSourcePath(self):
168         return self.suite.getSourcePath(self.path_in_suite)
169
170     def getExecPath(self):
171         return self.suite.getExecPath(self.path_in_suite)
172
173     def isExpectedToFail(self):
174         """
175         isExpectedToFail() -> bool
176
177         Check whether this test is expected to fail in the current
178         configuration. This check relies on the test xfails property which by
179         some test formats may not be computed until the test has first been
180         executed.
181         """
182
183         # Check if any of the xfails match an available feature or the target.
184         for item in self.xfails:
185             # If this is the wildcard, it always fails.
186             if item == '*':
187                 return True
188
189             # If this is an exact match for one of the features, it fails.
190             if item in self.config.available_features:
191                 return True
192
193             # If this is a part of the target triple, it fails.
194             if item in self.suite.config.target_triple:
195                 return True
196
197         return False
198
199
200     def getJUnitXML(self):
201         test_name = self.path_in_suite[-1]
202         test_path = self.path_in_suite[:-1]
203         safe_test_path = [x.replace(".","_") for x in test_path]
204         safe_name = self.suite.name.replace(".","-")
205
206         if safe_test_path:
207             class_name = safe_name + "." + "/".join(safe_test_path) 
208         else:
209             class_name = safe_name + "." + safe_name
210
211         xml = "<testcase classname='" + class_name + "' name='" + \
212             test_name + "'"
213         xml += " time='%.2f'" % (self.result.elapsed,)
214         if self.result.code.isFailure:
215             xml += ">\n\t<failure >\n" + escape(self.result.output)
216             xml += "\n\t</failure>\n</testcase>"
217         else:
218             xml += "/>"
219         return xml