1"""
2                     The LLVM Compiler Infrastructure
3
4This file is distributed under the University of Illinois Open Source
5License. See LICENSE.TXT for details.
6
7Provides the LLDBTestResult class, which holds information about progress
8and results of a single test run.
9"""
10
11from __future__ import absolute_import
12from __future__ import print_function
13
14# System modules
15import inspect
16
17# Third-party modules
18import unittest2
19
20# LLDB Modules
21from . import configuration
22from lldbsuite.test_event.event_builder import EventBuilder
23
24
25class LLDBTestResult(unittest2.TextTestResult):
26    """
27    Enforce a singleton pattern to allow introspection of test progress.
28
29    Overwrite addError(), addFailure(), and addExpectedFailure() methods
30    to enable each test instance to track its failure/error status.  It
31    is used in the LLDB test framework to emit detailed trace messages
32    to a log file for easier human inspection of test failures/errors.
33    """
34    __singleton__ = None
35    __ignore_singleton__ = False
36
37    @staticmethod
38    def getTerminalSize():
39        import os
40        env = os.environ
41        def ioctl_GWINSZ(fd):
42            try:
43                import fcntl, termios, struct, os
44                cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
45            '1234'))
46            except:
47                return
48            return cr
49        cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
50        if not cr:
51            try:
52                fd = os.open(os.ctermid(), os.O_RDONLY)
53                cr = ioctl_GWINSZ(fd)
54                os.close(fd)
55            except:
56                pass
57        if not cr:
58            cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
59        return int(cr[1]), int(cr[0])
60
61    def __init__(self, *args):
62        if not LLDBTestResult.__ignore_singleton__ and LLDBTestResult.__singleton__:
63            raise Exception("LLDBTestResult instantiated more than once")
64        super(LLDBTestResult, self).__init__(*args)
65        LLDBTestResult.__singleton__ = self
66        # Now put this singleton into the lldb module namespace.
67        configuration.test_result = self
68        # Computes the format string for displaying the counter.
69        counterWidth = len(str(configuration.suite.countTestCases()))
70        self.fmt = "%" + str(counterWidth) + "d: "
71        self.indentation = ' ' * (counterWidth + 2)
72        # This counts from 1 .. suite.countTestCases().
73        self.counter = 0
74        (width, height) = LLDBTestResult.getTerminalSize()
75        self.results_formatter = configuration.results_formatter_object
76
77    def _config_string(self, test):
78        compiler = getattr(test, "getCompiler", None)
79        arch = getattr(test, "getArchitecture", None)
80        return "%s-%s" % (compiler() if compiler else "", arch() if arch else "")
81
82    def _exc_info_to_string(self, err, test):
83        """Overrides superclass TestResult's method in order to append
84        our test config info string to the exception info string."""
85        if hasattr(test, "getArchitecture") and hasattr(test, "getCompiler"):
86            return '%sConfig=%s-%s' % (super(LLDBTestResult, self)._exc_info_to_string(err, test),
87                                                        test.getArchitecture(),
88                                                        test.getCompiler())
89        else:
90            return super(LLDBTestResult, self)._exc_info_to_string(err, test)
91
92    def getDescription(self, test):
93        doc_first_line = test.shortDescription()
94        if self.descriptions and doc_first_line:
95            return '\n'.join((str(test), self.indentation + doc_first_line))
96        else:
97            return str(test)
98
99    def getCategoriesForTest(self, test):
100        """
101        Gets all the categories for the currently running test method in test case
102        """
103        test_categories = []
104        test_method = getattr(test, test._testMethodName)
105        if test_method != None and hasattr(test_method, "categories"):
106            test_categories.extend(test_method.categories)
107
108        test_categories.extend(test.getCategories())
109
110        return test_categories
111
112    def hardMarkAsSkipped(self,test):
113        getattr(test, test._testMethodName).__func__.__unittest_skip__ = True
114        getattr(test, test._testMethodName).__func__.__unittest_skip_why__ = "test case does not fall in any category of interest for this run"
115        test.__class__.__unittest_skip__ = True
116        test.__class__.__unittest_skip_why__ = "test case does not fall in any category of interest for this run"
117
118    def startTest(self, test):
119        if configuration.shouldSkipBecauseOfCategories(self.getCategoriesForTest(test)):
120            self.hardMarkAsSkipped(test)
121        configuration.setCrashInfoHook("%s at %s" % (str(test),inspect.getfile(test.__class__)))
122        self.counter += 1
123        #if self.counter == 4:
124        #    import crashinfo
125        #    crashinfo.testCrashReporterDescription(None)
126        test.test_number = self.counter
127        if self.showAll:
128            self.stream.write(self.fmt % self.counter)
129        super(LLDBTestResult, self).startTest(test)
130        if self.results_formatter:
131            self.results_formatter.handle_event(
132                EventBuilder.event_for_start(test))
133
134    def addSuccess(self, test):
135        super(LLDBTestResult, self).addSuccess(test)
136        if configuration.parsable:
137            self.stream.write("PASS: LLDB (%s) :: %s\n" % (self._config_string(test), str(test)))
138        if self.results_formatter:
139            self.results_formatter.handle_event(
140                EventBuilder.event_for_success(test))
141
142    def addError(self, test, err):
143        configuration.sdir_has_content = True
144        super(LLDBTestResult, self).addError(test, err)
145        method = getattr(test, "markError", None)
146        if method:
147            method()
148        if configuration.parsable:
149            self.stream.write("FAIL: LLDB (%s) :: %s\n" % (self._config_string(test), str(test)))
150        if self.results_formatter:
151            self.results_formatter.handle_event(
152                EventBuilder.event_for_error(test, err))
153
154    def addCleanupError(self, test, err):
155        configuration.sdir_has_content = True
156        super(LLDBTestResult, self).addCleanupError(test, err)
157        method = getattr(test, "markCleanupError", None)
158        if method:
159            method()
160        if configuration.parsable:
161            self.stream.write("CLEANUP ERROR: LLDB (%s) :: %s\n" % (self._config_string(test), str(test)))
162        if self.results_formatter:
163            self.results_formatter.handle_event(
164                EventBuilder.event_for_cleanup_error(
165                    test, err))
166
167    def addFailure(self, test, err):
168        configuration.sdir_has_content = True
169        super(LLDBTestResult, self).addFailure(test, err)
170        method = getattr(test, "markFailure", None)
171        if method:
172            method()
173        if configuration.parsable:
174            self.stream.write("FAIL: LLDB (%s) :: %s\n" % (self._config_string(test), str(test)))
175        if configuration.useCategories:
176            test_categories = self.getCategoriesForTest(test)
177            for category in test_categories:
178                if category in configuration.failuresPerCategory:
179                    configuration.failuresPerCategory[category] = configuration.failuresPerCategory[category] + 1
180                else:
181                    configuration.failuresPerCategory[category] = 1
182        if self.results_formatter:
183            self.results_formatter.handle_event(
184                EventBuilder.event_for_failure(test, err))
185
186
187    def addExpectedFailure(self, test, err, bugnumber):
188        configuration.sdir_has_content = True
189        super(LLDBTestResult, self).addExpectedFailure(test, err, bugnumber)
190        method = getattr(test, "markExpectedFailure", None)
191        if method:
192            method(err, bugnumber)
193        if configuration.parsable:
194            self.stream.write("XFAIL: LLDB (%s) :: %s\n" % (self._config_string(test), str(test)))
195        if self.results_formatter:
196            self.results_formatter.handle_event(
197                EventBuilder.event_for_expected_failure(
198                test, err, bugnumber))
199
200    def addSkip(self, test, reason):
201        configuration.sdir_has_content = True
202        super(LLDBTestResult, self).addSkip(test, reason)
203        method = getattr(test, "markSkippedTest", None)
204        if method:
205            method()
206        if configuration.parsable:
207            self.stream.write("UNSUPPORTED: LLDB (%s) :: %s (%s) \n" % (self._config_string(test), str(test), reason))
208        if self.results_formatter:
209            self.results_formatter.handle_event(
210                EventBuilder.event_for_skip(test, reason))
211
212    def addUnexpectedSuccess(self, test, bugnumber):
213        configuration.sdir_has_content = True
214        super(LLDBTestResult, self).addUnexpectedSuccess(test, bugnumber)
215        method = getattr(test, "markUnexpectedSuccess", None)
216        if method:
217            method(bugnumber)
218        if configuration.parsable:
219            self.stream.write("XPASS: LLDB (%s) :: %s\n" % (self._config_string(test), str(test)))
220        if self.results_formatter:
221            self.results_formatter.handle_event(
222                EventBuilder.event_for_unexpected_success(
223                    test, bugnumber))
224