xref: /aosp_15_r20/external/llvm/utils/lit/lit/Test.py (revision 9880d6810fe72a1726cb53787c6711e909410d58)
1*9880d681SAndroid Build Coastguard Workerimport os
2*9880d681SAndroid Build Coastguard Workerfrom xml.sax.saxutils import escape
3*9880d681SAndroid Build Coastguard Workerfrom json import JSONEncoder
4*9880d681SAndroid Build Coastguard Worker
5*9880d681SAndroid Build Coastguard Worker# Test result codes.
6*9880d681SAndroid Build Coastguard Worker
7*9880d681SAndroid Build Coastguard Workerclass ResultCode(object):
8*9880d681SAndroid Build Coastguard Worker    """Test result codes."""
9*9880d681SAndroid Build Coastguard Worker
10*9880d681SAndroid Build Coastguard Worker    # We override __new__ and __getnewargs__ to ensure that pickling still
11*9880d681SAndroid Build Coastguard Worker    # provides unique ResultCode objects in any particular instance.
12*9880d681SAndroid Build Coastguard Worker    _instances = {}
13*9880d681SAndroid Build Coastguard Worker    def __new__(cls, name, isFailure):
14*9880d681SAndroid Build Coastguard Worker        res = cls._instances.get(name)
15*9880d681SAndroid Build Coastguard Worker        if res is None:
16*9880d681SAndroid Build Coastguard Worker            cls._instances[name] = res = super(ResultCode, cls).__new__(cls)
17*9880d681SAndroid Build Coastguard Worker        return res
18*9880d681SAndroid Build Coastguard Worker    def __getnewargs__(self):
19*9880d681SAndroid Build Coastguard Worker        return (self.name, self.isFailure)
20*9880d681SAndroid Build Coastguard Worker
21*9880d681SAndroid Build Coastguard Worker    def __init__(self, name, isFailure):
22*9880d681SAndroid Build Coastguard Worker        self.name = name
23*9880d681SAndroid Build Coastguard Worker        self.isFailure = isFailure
24*9880d681SAndroid Build Coastguard Worker
25*9880d681SAndroid Build Coastguard Worker    def __repr__(self):
26*9880d681SAndroid Build Coastguard Worker        return '%s%r' % (self.__class__.__name__,
27*9880d681SAndroid Build Coastguard Worker                         (self.name, self.isFailure))
28*9880d681SAndroid Build Coastguard Worker
29*9880d681SAndroid Build Coastguard WorkerPASS        = ResultCode('PASS', False)
30*9880d681SAndroid Build Coastguard WorkerFLAKYPASS   = ResultCode('FLAKYPASS', False)
31*9880d681SAndroid Build Coastguard WorkerXFAIL       = ResultCode('XFAIL', False)
32*9880d681SAndroid Build Coastguard WorkerFAIL        = ResultCode('FAIL', True)
33*9880d681SAndroid Build Coastguard WorkerXPASS       = ResultCode('XPASS', True)
34*9880d681SAndroid Build Coastguard WorkerUNRESOLVED  = ResultCode('UNRESOLVED', True)
35*9880d681SAndroid Build Coastguard WorkerUNSUPPORTED = ResultCode('UNSUPPORTED', False)
36*9880d681SAndroid Build Coastguard WorkerTIMEOUT     = ResultCode('TIMEOUT', True)
37*9880d681SAndroid Build Coastguard Worker
38*9880d681SAndroid Build Coastguard Worker# Test metric values.
39*9880d681SAndroid Build Coastguard Worker
40*9880d681SAndroid Build Coastguard Workerclass MetricValue(object):
41*9880d681SAndroid Build Coastguard Worker    def format(self):
42*9880d681SAndroid Build Coastguard Worker        """
43*9880d681SAndroid Build Coastguard Worker        format() -> str
44*9880d681SAndroid Build Coastguard Worker
45*9880d681SAndroid Build Coastguard Worker        Convert this metric to a string suitable for displaying as part of the
46*9880d681SAndroid Build Coastguard Worker        console output.
47*9880d681SAndroid Build Coastguard Worker        """
48*9880d681SAndroid Build Coastguard Worker        raise RuntimeError("abstract method")
49*9880d681SAndroid Build Coastguard Worker
50*9880d681SAndroid Build Coastguard Worker    def todata(self):
51*9880d681SAndroid Build Coastguard Worker        """
52*9880d681SAndroid Build Coastguard Worker        todata() -> json-serializable data
53*9880d681SAndroid Build Coastguard Worker
54*9880d681SAndroid Build Coastguard Worker        Convert this metric to content suitable for serializing in the JSON test
55*9880d681SAndroid Build Coastguard Worker        output.
56*9880d681SAndroid Build Coastguard Worker        """
57*9880d681SAndroid Build Coastguard Worker        raise RuntimeError("abstract method")
58*9880d681SAndroid Build Coastguard Worker
59*9880d681SAndroid Build Coastguard Workerclass IntMetricValue(MetricValue):
60*9880d681SAndroid Build Coastguard Worker    def __init__(self, value):
61*9880d681SAndroid Build Coastguard Worker        self.value = value
62*9880d681SAndroid Build Coastguard Worker
63*9880d681SAndroid Build Coastguard Worker    def format(self):
64*9880d681SAndroid Build Coastguard Worker        return str(self.value)
65*9880d681SAndroid Build Coastguard Worker
66*9880d681SAndroid Build Coastguard Worker    def todata(self):
67*9880d681SAndroid Build Coastguard Worker        return self.value
68*9880d681SAndroid Build Coastguard Worker
69*9880d681SAndroid Build Coastguard Workerclass RealMetricValue(MetricValue):
70*9880d681SAndroid Build Coastguard Worker    def __init__(self, value):
71*9880d681SAndroid Build Coastguard Worker        self.value = value
72*9880d681SAndroid Build Coastguard Worker
73*9880d681SAndroid Build Coastguard Worker    def format(self):
74*9880d681SAndroid Build Coastguard Worker        return '%.4f' % self.value
75*9880d681SAndroid Build Coastguard Worker
76*9880d681SAndroid Build Coastguard Worker    def todata(self):
77*9880d681SAndroid Build Coastguard Worker        return self.value
78*9880d681SAndroid Build Coastguard Worker
79*9880d681SAndroid Build Coastguard Workerclass JSONMetricValue(MetricValue):
80*9880d681SAndroid Build Coastguard Worker    """
81*9880d681SAndroid Build Coastguard Worker        JSONMetricValue is used for types that are representable in the output
82*9880d681SAndroid Build Coastguard Worker        but that are otherwise uninterpreted.
83*9880d681SAndroid Build Coastguard Worker    """
84*9880d681SAndroid Build Coastguard Worker    def __init__(self, value):
85*9880d681SAndroid Build Coastguard Worker        # Ensure the value is a serializable by trying to encode it.
86*9880d681SAndroid Build Coastguard Worker        # WARNING: The value may change before it is encoded again, and may
87*9880d681SAndroid Build Coastguard Worker        #          not be encodable after the change.
88*9880d681SAndroid Build Coastguard Worker        try:
89*9880d681SAndroid Build Coastguard Worker            e = JSONEncoder()
90*9880d681SAndroid Build Coastguard Worker            e.encode(value)
91*9880d681SAndroid Build Coastguard Worker        except TypeError:
92*9880d681SAndroid Build Coastguard Worker            raise
93*9880d681SAndroid Build Coastguard Worker        self.value = value
94*9880d681SAndroid Build Coastguard Worker
95*9880d681SAndroid Build Coastguard Worker    def format(self):
96*9880d681SAndroid Build Coastguard Worker        e = JSONEncoder(indent=2, sort_keys=True)
97*9880d681SAndroid Build Coastguard Worker        return e.encode(self.value)
98*9880d681SAndroid Build Coastguard Worker
99*9880d681SAndroid Build Coastguard Worker    def todata(self):
100*9880d681SAndroid Build Coastguard Worker        return self.value
101*9880d681SAndroid Build Coastguard Worker
102*9880d681SAndroid Build Coastguard Workerdef toMetricValue(value):
103*9880d681SAndroid Build Coastguard Worker    if isinstance(value, MetricValue):
104*9880d681SAndroid Build Coastguard Worker        return value
105*9880d681SAndroid Build Coastguard Worker    elif isinstance(value, int):
106*9880d681SAndroid Build Coastguard Worker        return IntMetricValue(value)
107*9880d681SAndroid Build Coastguard Worker    elif isinstance(value, float):
108*9880d681SAndroid Build Coastguard Worker        return RealMetricValue(value)
109*9880d681SAndroid Build Coastguard Worker    else:
110*9880d681SAndroid Build Coastguard Worker        # 'long' is only present in python2
111*9880d681SAndroid Build Coastguard Worker        try:
112*9880d681SAndroid Build Coastguard Worker            if isinstance(value, long):
113*9880d681SAndroid Build Coastguard Worker                return IntMetricValue(value)
114*9880d681SAndroid Build Coastguard Worker        except NameError:
115*9880d681SAndroid Build Coastguard Worker            pass
116*9880d681SAndroid Build Coastguard Worker
117*9880d681SAndroid Build Coastguard Worker        # Try to create a JSONMetricValue and let the constructor throw
118*9880d681SAndroid Build Coastguard Worker        # if value is not a valid type.
119*9880d681SAndroid Build Coastguard Worker        return JSONMetricValue(value)
120*9880d681SAndroid Build Coastguard Worker
121*9880d681SAndroid Build Coastguard Worker
122*9880d681SAndroid Build Coastguard Worker# Test results.
123*9880d681SAndroid Build Coastguard Worker
124*9880d681SAndroid Build Coastguard Workerclass Result(object):
125*9880d681SAndroid Build Coastguard Worker    """Wrapper for the results of executing an individual test."""
126*9880d681SAndroid Build Coastguard Worker
127*9880d681SAndroid Build Coastguard Worker    def __init__(self, code, output='', elapsed=None):
128*9880d681SAndroid Build Coastguard Worker        # The result code.
129*9880d681SAndroid Build Coastguard Worker        self.code = code
130*9880d681SAndroid Build Coastguard Worker        # The test output.
131*9880d681SAndroid Build Coastguard Worker        self.output = output
132*9880d681SAndroid Build Coastguard Worker        # The wall timing to execute the test, if timing.
133*9880d681SAndroid Build Coastguard Worker        self.elapsed = elapsed
134*9880d681SAndroid Build Coastguard Worker        # The metrics reported by this test.
135*9880d681SAndroid Build Coastguard Worker        self.metrics = {}
136*9880d681SAndroid Build Coastguard Worker
137*9880d681SAndroid Build Coastguard Worker    def addMetric(self, name, value):
138*9880d681SAndroid Build Coastguard Worker        """
139*9880d681SAndroid Build Coastguard Worker        addMetric(name, value)
140*9880d681SAndroid Build Coastguard Worker
141*9880d681SAndroid Build Coastguard Worker        Attach a test metric to the test result, with the given name and list of
142*9880d681SAndroid Build Coastguard Worker        values. It is an error to attempt to attach the metrics with the same
143*9880d681SAndroid Build Coastguard Worker        name multiple times.
144*9880d681SAndroid Build Coastguard Worker
145*9880d681SAndroid Build Coastguard Worker        Each value must be an instance of a MetricValue subclass.
146*9880d681SAndroid Build Coastguard Worker        """
147*9880d681SAndroid Build Coastguard Worker        if name in self.metrics:
148*9880d681SAndroid Build Coastguard Worker            raise ValueError("result already includes metrics for %r" % (
149*9880d681SAndroid Build Coastguard Worker                    name,))
150*9880d681SAndroid Build Coastguard Worker        if not isinstance(value, MetricValue):
151*9880d681SAndroid Build Coastguard Worker            raise TypeError("unexpected metric value: %r" % (value,))
152*9880d681SAndroid Build Coastguard Worker        self.metrics[name] = value
153*9880d681SAndroid Build Coastguard Worker
154*9880d681SAndroid Build Coastguard Worker# Test classes.
155*9880d681SAndroid Build Coastguard Worker
156*9880d681SAndroid Build Coastguard Workerclass TestSuite:
157*9880d681SAndroid Build Coastguard Worker    """TestSuite - Information on a group of tests.
158*9880d681SAndroid Build Coastguard Worker
159*9880d681SAndroid Build Coastguard Worker    A test suite groups together a set of logically related tests.
160*9880d681SAndroid Build Coastguard Worker    """
161*9880d681SAndroid Build Coastguard Worker
162*9880d681SAndroid Build Coastguard Worker    def __init__(self, name, source_root, exec_root, config):
163*9880d681SAndroid Build Coastguard Worker        self.name = name
164*9880d681SAndroid Build Coastguard Worker        self.source_root = source_root
165*9880d681SAndroid Build Coastguard Worker        self.exec_root = exec_root
166*9880d681SAndroid Build Coastguard Worker        # The test suite configuration.
167*9880d681SAndroid Build Coastguard Worker        self.config = config
168*9880d681SAndroid Build Coastguard Worker
169*9880d681SAndroid Build Coastguard Worker    def getSourcePath(self, components):
170*9880d681SAndroid Build Coastguard Worker        return os.path.join(self.source_root, *components)
171*9880d681SAndroid Build Coastguard Worker
172*9880d681SAndroid Build Coastguard Worker    def getExecPath(self, components):
173*9880d681SAndroid Build Coastguard Worker        return os.path.join(self.exec_root, *components)
174*9880d681SAndroid Build Coastguard Worker
175*9880d681SAndroid Build Coastguard Workerclass Test:
176*9880d681SAndroid Build Coastguard Worker    """Test - Information on a single test instance."""
177*9880d681SAndroid Build Coastguard Worker
178*9880d681SAndroid Build Coastguard Worker    def __init__(self, suite, path_in_suite, config, file_path = None):
179*9880d681SAndroid Build Coastguard Worker        self.suite = suite
180*9880d681SAndroid Build Coastguard Worker        self.path_in_suite = path_in_suite
181*9880d681SAndroid Build Coastguard Worker        self.config = config
182*9880d681SAndroid Build Coastguard Worker        self.file_path = file_path
183*9880d681SAndroid Build Coastguard Worker        # A list of conditions under which this test is expected to fail. These
184*9880d681SAndroid Build Coastguard Worker        # can optionally be provided by test format handlers, and will be
185*9880d681SAndroid Build Coastguard Worker        # honored when the test result is supplied.
186*9880d681SAndroid Build Coastguard Worker        self.xfails = []
187*9880d681SAndroid Build Coastguard Worker        # The test result, once complete.
188*9880d681SAndroid Build Coastguard Worker        self.result = None
189*9880d681SAndroid Build Coastguard Worker
190*9880d681SAndroid Build Coastguard Worker    def setResult(self, result):
191*9880d681SAndroid Build Coastguard Worker        if self.result is not None:
192*9880d681SAndroid Build Coastguard Worker            raise ArgumentError("test result already set")
193*9880d681SAndroid Build Coastguard Worker        if not isinstance(result, Result):
194*9880d681SAndroid Build Coastguard Worker            raise ArgumentError("unexpected result type")
195*9880d681SAndroid Build Coastguard Worker
196*9880d681SAndroid Build Coastguard Worker        self.result = result
197*9880d681SAndroid Build Coastguard Worker
198*9880d681SAndroid Build Coastguard Worker        # Apply the XFAIL handling to resolve the result exit code.
199*9880d681SAndroid Build Coastguard Worker        if self.isExpectedToFail():
200*9880d681SAndroid Build Coastguard Worker            if self.result.code == PASS:
201*9880d681SAndroid Build Coastguard Worker                self.result.code = XPASS
202*9880d681SAndroid Build Coastguard Worker            elif self.result.code == FAIL:
203*9880d681SAndroid Build Coastguard Worker                self.result.code = XFAIL
204*9880d681SAndroid Build Coastguard Worker
205*9880d681SAndroid Build Coastguard Worker    def getFullName(self):
206*9880d681SAndroid Build Coastguard Worker        return self.suite.config.name + ' :: ' + '/'.join(self.path_in_suite)
207*9880d681SAndroid Build Coastguard Worker
208*9880d681SAndroid Build Coastguard Worker    def getFilePath(self):
209*9880d681SAndroid Build Coastguard Worker        if self.file_path:
210*9880d681SAndroid Build Coastguard Worker            return self.file_path
211*9880d681SAndroid Build Coastguard Worker        return self.getSourcePath()
212*9880d681SAndroid Build Coastguard Worker
213*9880d681SAndroid Build Coastguard Worker    def getSourcePath(self):
214*9880d681SAndroid Build Coastguard Worker        return self.suite.getSourcePath(self.path_in_suite)
215*9880d681SAndroid Build Coastguard Worker
216*9880d681SAndroid Build Coastguard Worker    def getExecPath(self):
217*9880d681SAndroid Build Coastguard Worker        return self.suite.getExecPath(self.path_in_suite)
218*9880d681SAndroid Build Coastguard Worker
219*9880d681SAndroid Build Coastguard Worker    def isExpectedToFail(self):
220*9880d681SAndroid Build Coastguard Worker        """
221*9880d681SAndroid Build Coastguard Worker        isExpectedToFail() -> bool
222*9880d681SAndroid Build Coastguard Worker
223*9880d681SAndroid Build Coastguard Worker        Check whether this test is expected to fail in the current
224*9880d681SAndroid Build Coastguard Worker        configuration. This check relies on the test xfails property which by
225*9880d681SAndroid Build Coastguard Worker        some test formats may not be computed until the test has first been
226*9880d681SAndroid Build Coastguard Worker        executed.
227*9880d681SAndroid Build Coastguard Worker        """
228*9880d681SAndroid Build Coastguard Worker
229*9880d681SAndroid Build Coastguard Worker        # Check if any of the xfails match an available feature or the target.
230*9880d681SAndroid Build Coastguard Worker        for item in self.xfails:
231*9880d681SAndroid Build Coastguard Worker            # If this is the wildcard, it always fails.
232*9880d681SAndroid Build Coastguard Worker            if item == '*':
233*9880d681SAndroid Build Coastguard Worker                return True
234*9880d681SAndroid Build Coastguard Worker
235*9880d681SAndroid Build Coastguard Worker            # If this is an exact match for one of the features, it fails.
236*9880d681SAndroid Build Coastguard Worker            if item in self.config.available_features:
237*9880d681SAndroid Build Coastguard Worker                return True
238*9880d681SAndroid Build Coastguard Worker
239*9880d681SAndroid Build Coastguard Worker            # If this is a part of the target triple, it fails.
240*9880d681SAndroid Build Coastguard Worker            if item and item in self.suite.config.target_triple:
241*9880d681SAndroid Build Coastguard Worker                return True
242*9880d681SAndroid Build Coastguard Worker
243*9880d681SAndroid Build Coastguard Worker        return False
244*9880d681SAndroid Build Coastguard Worker
245*9880d681SAndroid Build Coastguard Worker    def isEarlyTest(self):
246*9880d681SAndroid Build Coastguard Worker        """
247*9880d681SAndroid Build Coastguard Worker        isEarlyTest() -> bool
248*9880d681SAndroid Build Coastguard Worker
249*9880d681SAndroid Build Coastguard Worker        Check whether this test should be executed early in a particular run.
250*9880d681SAndroid Build Coastguard Worker        This can be used for test suites with long running tests to maximize
251*9880d681SAndroid Build Coastguard Worker        parallelism or where it is desirable to surface their failures early.
252*9880d681SAndroid Build Coastguard Worker        """
253*9880d681SAndroid Build Coastguard Worker        return self.suite.config.is_early
254*9880d681SAndroid Build Coastguard Worker
255*9880d681SAndroid Build Coastguard Worker    def getJUnitXML(self):
256*9880d681SAndroid Build Coastguard Worker        test_name = self.path_in_suite[-1]
257*9880d681SAndroid Build Coastguard Worker        test_path = self.path_in_suite[:-1]
258*9880d681SAndroid Build Coastguard Worker        safe_test_path = [x.replace(".","_") for x in test_path]
259*9880d681SAndroid Build Coastguard Worker        safe_name = self.suite.name.replace(".","-")
260*9880d681SAndroid Build Coastguard Worker
261*9880d681SAndroid Build Coastguard Worker        if safe_test_path:
262*9880d681SAndroid Build Coastguard Worker            class_name = safe_name + "." + "/".join(safe_test_path)
263*9880d681SAndroid Build Coastguard Worker        else:
264*9880d681SAndroid Build Coastguard Worker            class_name = safe_name + "." + safe_name
265*9880d681SAndroid Build Coastguard Worker
266*9880d681SAndroid Build Coastguard Worker        xml = "<testcase classname='" + class_name + "' name='" + \
267*9880d681SAndroid Build Coastguard Worker            test_name + "'"
268*9880d681SAndroid Build Coastguard Worker        xml += " time='%.2f'" % (self.result.elapsed,)
269*9880d681SAndroid Build Coastguard Worker        if self.result.code.isFailure:
270*9880d681SAndroid Build Coastguard Worker            xml += ">\n\t<failure >\n" + escape(self.result.output)
271*9880d681SAndroid Build Coastguard Worker            xml += "\n\t</failure>\n</testcase>"
272*9880d681SAndroid Build Coastguard Worker        else:
273*9880d681SAndroid Build Coastguard Worker            xml += "/>"
274*9880d681SAndroid Build Coastguard Worker        return xml
275