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