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.
18 from . import configuration
19 from lldbsuite
.test_event
import build_exception
22 class LLDBTestResult(unittest
.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.
33 __ignore_singleton__
= False
36 def getTerminalSize():
47 cr
= struct
.unpack("hh", fcntl
.ioctl(fd
, termios
.TIOCGWINSZ
, "1234"))
52 cr
= ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
55 fd
= os
.open(os
.ctermid(), os
.O_RDONLY
)
61 cr
= (env
.get("LINES", 25), env
.get("COLUMNS", 80))
62 return int(cr
[1]), int(cr
[0])
64 def __init__(self
, *args
):
65 if not LLDBTestResult
.__ignore
_singleton
__ and LLDBTestResult
.__singleton
__:
66 raise Exception("LLDBTestResult instantiated more than once")
67 super(LLDBTestResult
, self
).__init
__(*args
)
68 LLDBTestResult
.__singleton
__ = self
69 # Now put this singleton into the lldb module namespace.
70 configuration
.test_result
= self
71 # Computes the format string for displaying the counter.
72 counterWidth
= len(str(configuration
.suite
.countTestCases()))
73 self
.fmt
= "%" + str(counterWidth
) + "d: "
74 self
.indentation
= " " * (counterWidth
+ 2)
75 # This counts from 1 .. suite.countTestCases().
77 (width
, height
) = LLDBTestResult
.getTerminalSize()
79 def _config_string(self
, test
):
80 compiler
= getattr(test
, "getCompiler", None)
81 arch
= getattr(test
, "getArchitecture", None)
82 return "%s-%s" % (compiler() if compiler
else "", arch() if arch
else "")
84 def _exc_info_to_string(self
, err
, test
):
85 """Overrides superclass TestResult's method in order to append
86 our test config info string to the exception info string."""
87 if hasattr(test
, "getArchitecture") and hasattr(test
, "getCompiler"):
88 return "%sConfig=%s-%s" % (
89 super(LLDBTestResult
, self
)._exc
_info
_to
_string
(err
, test
),
90 test
.getArchitecture(),
94 return super(LLDBTestResult
, self
)._exc
_info
_to
_string
(err
, test
)
96 def getDescription(self
, test
):
97 doc_first_line
= test
.shortDescription()
98 if self
.descriptions
and doc_first_line
:
99 return "\n".join((str(test
), self
.indentation
+ doc_first_line
))
103 def _getTestPath(self
, test
):
104 # Use test.test_filename if the test was created with
105 # lldbinline.MakeInlineTest().
108 elif hasattr(test
, "test_filename"):
109 return test
.test_filename
113 return inspect
.getsourcefile(test
.__class
__)
115 def _getFileBasedCategories(self
, test
):
117 Returns the list of categories to which this test case belongs by
118 collecting values of "categories" files. We start at the folder the test is in
119 and traverse the hierarchy upwards until the test-suite root directory.
121 start_path
= self
._getTestPath
(test
)
125 folder
= os
.path
.dirname(start_path
)
127 from lldbsuite
import lldb_test_root
as test_root
129 if test_root
!= os
.path
.commonprefix([folder
, test_root
]):
131 "The test file %s is outside the test root directory" % start_path
135 while not os
.path
.samefile(folder
, test_root
):
136 categories_file_name
= os
.path
.join(folder
, "categories")
137 if os
.path
.exists(categories_file_name
):
138 categories_file
= open(categories_file_name
, "r")
139 categories_str
= categories_file
.readline().strip()
140 categories_file
.close()
141 categories
.update(categories_str
.split(","))
142 folder
= os
.path
.dirname(folder
)
144 return list(categories
)
146 def getCategoriesForTest(self
, test
):
148 Gets all the categories for the currently running test method in test case
151 test_categories
.extend(getattr(test
, "categories", []))
153 test_method
= getattr(test
, test
._testMethodName
)
154 if test_method
is not None:
155 test_categories
.extend(getattr(test_method
, "categories", []))
157 test_categories
.extend(self
._getFileBasedCategories
(test
))
159 return test_categories
161 def hardMarkAsSkipped(self
, test
):
162 getattr(test
, test
._testMethodName
).__func
__.__unittest
_skip
__ = True
164 test
, test
._testMethodName
165 ).__func
__.__unittest
_skip
_why
__ = (
166 "test case does not fall in any category of interest for this run"
169 def checkExclusion(self
, exclusion_list
, name
):
173 for item
in exclusion_list
:
174 if re
.search(item
, name
):
178 def checkCategoryExclusion(self
, exclusion_list
, test
):
179 return not set(exclusion_list
).isdisjoint(self
.getCategoriesForTest(test
))
181 def startTest(self
, test
):
182 if configuration
.shouldSkipBecauseOfCategories(self
.getCategoriesForTest(test
)):
183 self
.hardMarkAsSkipped(test
)
184 if self
.checkExclusion(configuration
.skip_tests
, test
.id()):
185 self
.hardMarkAsSkipped(test
)
188 test
.test_number
= self
.counter
190 self
.stream
.write(self
.fmt
% self
.counter
)
191 super(LLDBTestResult
, self
).startTest(test
)
193 def addSuccess(self
, test
):
194 if self
.checkExclusion(
195 configuration
.xfail_tests
, test
.id()
196 ) or self
.checkCategoryExclusion(configuration
.xfail_categories
, test
):
197 self
.addUnexpectedSuccess(test
, None)
200 super(LLDBTestResult
, self
).addSuccess(test
)
202 "PASS: LLDB (%s) :: %s\n" % (self
._config
_string
(test
), str(test
))
205 def _isBuildError(self
, err_tuple
):
206 exception
= err_tuple
[1]
207 return isinstance(exception
, build_exception
.BuildError
)
209 def _saveBuildErrorTuple(self
, test
, err
):
210 # Adjust the error description so it prints the build command and build error
211 # rather than an uninformative Python backtrace.
213 error_description
= "{}\nTest Directory:\n{}".format(
214 str(build_error
), os
.path
.dirname(self
._getTestPath
(test
))
216 self
.errors
.append((test
, error_description
))
217 self
._mirrorOutput
= True
219 def addError(self
, test
, err
):
220 configuration
.sdir_has_content
= True
221 if self
._isBuildError
(err
):
222 self
._saveBuildErrorTuple
(test
, err
)
224 super(LLDBTestResult
, self
).addError(test
, err
)
226 method
= getattr(test
, "markError", None)
230 "FAIL: LLDB (%s) :: %s\n" % (self
._config
_string
(test
), str(test
))
233 def addCleanupError(self
, test
, err
):
234 configuration
.sdir_has_content
= True
235 super(LLDBTestResult
, self
).addCleanupError(test
, err
)
236 method
= getattr(test
, "markCleanupError", None)
240 "CLEANUP ERROR: LLDB (%s) :: %s\n%s\n"
241 % (self
._config
_string
(test
), str(test
), traceback
.format_exc())
244 def addFailure(self
, test
, err
):
245 if self
.checkExclusion(
246 configuration
.xfail_tests
, test
.id()
247 ) or self
.checkCategoryExclusion(configuration
.xfail_categories
, test
):
248 self
.addExpectedFailure(test
, err
)
251 configuration
.sdir_has_content
= True
252 super(LLDBTestResult
, self
).addFailure(test
, err
)
253 method
= getattr(test
, "markFailure", None)
257 "FAIL: LLDB (%s) :: %s\n" % (self
._config
_string
(test
), str(test
))
259 if configuration
.use_categories
:
260 test_categories
= self
.getCategoriesForTest(test
)
261 for category
in test_categories
:
262 if category
in configuration
.failures_per_category
:
263 configuration
.failures_per_category
[category
] = (
264 configuration
.failures_per_category
[category
] + 1
267 configuration
.failures_per_category
[category
] = 1
269 def addExpectedFailure(self
, test
, err
):
270 configuration
.sdir_has_content
= True
271 super(LLDBTestResult
, self
).addExpectedFailure(test
, err
)
272 method
= getattr(test
, "markExpectedFailure", None)
276 "XFAIL: LLDB (%s) :: %s\n" % (self
._config
_string
(test
), str(test
))
279 def addSkip(self
, test
, reason
):
280 configuration
.sdir_has_content
= True
281 super(LLDBTestResult
, self
).addSkip(test
, reason
)
282 method
= getattr(test
, "markSkippedTest", None)
286 "UNSUPPORTED: LLDB (%s) :: %s (%s) \n"
287 % (self
._config
_string
(test
), str(test
), reason
)
290 def addUnexpectedSuccess(self
, test
):
291 configuration
.sdir_has_content
= True
292 super(LLDBTestResult
, self
).addUnexpectedSuccess(test
)
293 method
= getattr(test
, "markUnexpectedSuccess", None)
297 "XPASS: LLDB (%s) :: %s\n" % (self
._config
_string
(test
), str(test
))