2 Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 See https://llvm.org/LICENSE.txt for license information.
4 SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 Provides the LLDBTestResult class, which holds information about progress
7 and results of a single test run.
17 from . import configuration
18 from lldbsuite
.test_event
.event_builder
import EventBuilder
19 from lldbsuite
.test_event
import build_exception
22 class LLDBTestResult(unittest2
.TextTestResult
):
24 Enforce a singleton pattern to allow introspection of test progress.
26 Overwrite addError(), addFailure(), and addExpectedFailure() methods
27 to enable each test instance to track its failure/error status. It
28 is used in the LLDB test framework to emit detailed trace messages
29 to a log file for easier human inspection of test failures/errors.
32 __ignore_singleton__
= False
35 def getTerminalSize():
44 cr
= struct
.unpack('hh', fcntl
.ioctl(fd
, termios
.TIOCGWINSZ
,
49 cr
= ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
52 fd
= os
.open(os
.ctermid(), os
.O_RDONLY
)
58 cr
= (env
.get('LINES', 25), env
.get('COLUMNS', 80))
59 return int(cr
[1]), int(cr
[0])
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().
74 (width
, height
) = LLDBTestResult
.getTerminalSize()
75 self
.results_formatter
= configuration
.results_formatter_object
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 "",
81 arch() if arch
else "")
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
,
88 self
)._exc
_info
_to
_string
(err
,
90 test
.getArchitecture(),
93 return super(LLDBTestResult
, self
)._exc
_info
_to
_string
(err
, test
)
95 def getDescription(self
, test
):
96 doc_first_line
= test
.shortDescription()
97 if self
.descriptions
and doc_first_line
:
98 return '\n'.join((str(test
), self
.indentation
+ doc_first_line
))
102 def _getTestPath(self
, test
):
103 # Use test.test_filename if the test was created with
104 # lldbinline.MakeInlineTest().
107 elif hasattr(test
, "test_filename"):
108 return test
.test_filename
111 return inspect
.getsourcefile(test
.__class
__)
113 def _getFileBasedCategories(self
, test
):
115 Returns the list of categories to which this test case belongs by
116 collecting values of ".categories" files. We start at the folder the test is in
117 and traverse the hierarchy upwards until the test-suite root directory.
119 start_path
= self
._getTestPath
(test
)
122 folder
= os
.path
.dirname(start_path
)
124 from lldbsuite
import lldb_test_root
as test_root
125 if test_root
!= os
.path
.commonprefix([folder
, test_root
]):
126 raise Exception("The test file %s is outside the test root directory" % start_path
)
129 while not os
.path
.samefile(folder
, test_root
):
130 categories_file_name
= os
.path
.join(folder
, ".categories")
131 if os
.path
.exists(categories_file_name
):
132 categories_file
= open(categories_file_name
, 'r')
133 categories_str
= categories_file
.readline().strip()
134 categories_file
.close()
135 categories
.update(categories_str
.split(','))
136 folder
= os
.path
.dirname(folder
)
138 return list(categories
)
140 def getCategoriesForTest(self
, test
):
142 Gets all the categories for the currently running test method in test case
145 test_method
= getattr(test
, test
._testMethodName
)
146 if test_method
is not None and hasattr(test_method
, "categories"):
147 test_categories
.extend(test_method
.categories
)
149 test_categories
.extend(self
._getFileBasedCategories
(test
))
151 return test_categories
153 def hardMarkAsSkipped(self
, test
):
154 getattr(test
, test
._testMethodName
).__func
__.__unittest
_skip
__ = True
157 test
._testMethodName
).__func
__.__unittest
_skip
_why
__ = "test case does not fall in any category of interest for this run"
159 def checkExclusion(self
, exclusion_list
, name
):
162 for item
in exclusion_list
:
163 if re
.search(item
, name
):
167 def checkCategoryExclusion(self
, exclusion_list
, test
):
168 return not set(exclusion_list
).isdisjoint(
169 self
.getCategoriesForTest(test
))
171 def startTest(self
, test
):
172 if configuration
.shouldSkipBecauseOfCategories(
173 self
.getCategoriesForTest(test
)):
174 self
.hardMarkAsSkipped(test
)
175 if self
.checkExclusion(
176 configuration
.skip_tests
, test
.id()):
177 self
.hardMarkAsSkipped(test
)
180 test
.test_number
= self
.counter
182 self
.stream
.write(self
.fmt
% self
.counter
)
183 super(LLDBTestResult
, self
).startTest(test
)
184 if self
.results_formatter
:
185 self
.results_formatter
.handle_event(
186 EventBuilder
.event_for_start(test
))
188 def addSuccess(self
, test
):
189 if (self
.checkExclusion(
190 configuration
.xfail_tests
, test
.id()) or
191 self
.checkCategoryExclusion(
192 configuration
.xfail_categories
, test
)):
193 self
.addUnexpectedSuccess(test
, None)
196 super(LLDBTestResult
, self
).addSuccess(test
)
198 "PASS: LLDB (%s) :: %s\n" %
199 (self
._config
_string
(test
), str(test
)))
200 if self
.results_formatter
:
201 self
.results_formatter
.handle_event(
202 EventBuilder
.event_for_success(test
))
204 def _isBuildError(self
, err_tuple
):
205 exception
= err_tuple
[1]
206 return isinstance(exception
, build_exception
.BuildError
)
208 def _saveBuildErrorTuple(self
, test
, err
):
209 # Adjust the error description so it prints the build command and build error
210 # rather than an uninformative Python backtrace.
212 error_description
= "{}\nTest Directory:\n{}".format(
214 os
.path
.dirname(self
._getTestPath
(test
)))
215 self
.errors
.append((test
, error_description
))
216 self
._mirrorOutput
= True
218 def addError(self
, test
, err
):
219 configuration
.sdir_has_content
= True
220 if self
._isBuildError
(err
):
221 self
._saveBuildErrorTuple
(test
, err
)
223 super(LLDBTestResult
, self
).addError(test
, err
)
225 method
= getattr(test
, "markError", None)
229 "FAIL: LLDB (%s) :: %s\n" %
230 (self
._config
_string
(test
), str(test
)))
231 if self
.results_formatter
:
232 # Handle build errors as a separate event type
233 if self
._isBuildError
(err
):
234 error_event
= EventBuilder
.event_for_build_error(test
, err
)
236 error_event
= EventBuilder
.event_for_error(test
, err
)
237 self
.results_formatter
.handle_event(error_event
)
239 def addCleanupError(self
, test
, err
):
240 configuration
.sdir_has_content
= True
241 super(LLDBTestResult
, self
).addCleanupError(test
, err
)
242 method
= getattr(test
, "markCleanupError", None)
246 "CLEANUP ERROR: LLDB (%s) :: %s\n" %
247 (self
._config
_string
(test
), str(test
)))
248 if self
.results_formatter
:
249 self
.results_formatter
.handle_event(
250 EventBuilder
.event_for_cleanup_error(
253 def addFailure(self
, test
, err
):
254 if (self
.checkExclusion(
255 configuration
.xfail_tests
, test
.id()) or
256 self
.checkCategoryExclusion(
257 configuration
.xfail_categories
, test
)):
258 self
.addExpectedFailure(test
, err
, None)
261 configuration
.sdir_has_content
= True
262 super(LLDBTestResult
, self
).addFailure(test
, err
)
263 method
= getattr(test
, "markFailure", None)
267 "FAIL: LLDB (%s) :: %s\n" %
268 (self
._config
_string
(test
), str(test
)))
269 if configuration
.use_categories
:
270 test_categories
= self
.getCategoriesForTest(test
)
271 for category
in test_categories
:
272 if category
in configuration
.failures_per_category
:
273 configuration
.failures_per_category
[
274 category
] = configuration
.failures_per_category
[category
] + 1
276 configuration
.failures_per_category
[category
] = 1
277 if self
.results_formatter
:
278 self
.results_formatter
.handle_event(
279 EventBuilder
.event_for_failure(test
, err
))
281 def addExpectedFailure(self
, test
, err
, bugnumber
):
282 configuration
.sdir_has_content
= True
283 super(LLDBTestResult
, self
).addExpectedFailure(test
, err
, bugnumber
)
284 method
= getattr(test
, "markExpectedFailure", None)
286 method(err
, bugnumber
)
288 "XFAIL: LLDB (%s) :: %s\n" %
289 (self
._config
_string
(test
), str(test
)))
290 if self
.results_formatter
:
291 self
.results_formatter
.handle_event(
292 EventBuilder
.event_for_expected_failure(
293 test
, err
, bugnumber
))
295 def addSkip(self
, test
, reason
):
296 configuration
.sdir_has_content
= True
297 super(LLDBTestResult
, self
).addSkip(test
, reason
)
298 method
= getattr(test
, "markSkippedTest", None)
302 "UNSUPPORTED: LLDB (%s) :: %s (%s) \n" %
303 (self
._config
_string
(test
), str(test
), reason
))
304 if self
.results_formatter
:
305 self
.results_formatter
.handle_event(
306 EventBuilder
.event_for_skip(test
, reason
))
308 def addUnexpectedSuccess(self
, test
, bugnumber
):
309 configuration
.sdir_has_content
= True
310 super(LLDBTestResult
, self
).addUnexpectedSuccess(test
, bugnumber
)
311 method
= getattr(test
, "markUnexpectedSuccess", None)
315 "XPASS: LLDB (%s) :: %s\n" %
316 (self
._config
_string
(test
), str(test
)))
317 if self
.results_formatter
:
318 self
.results_formatter
.handle_event(
319 EventBuilder
.event_for_unexpected_success(