Skip to content

Commit d0d0727

Browse files
authored
[lldb][test] Apply @expectedFailureAll/@skipIf early for debug_info tests (#73067)
The @expectedFailureAll and @skipIf decorators will mark the test case as xfail/skip if _all_ conditions passed in match, including debug_info. * If debug_info is not one of the matching conditions, we can immediately evaluate the check and decide if it should be decorated. * If debug_info *is* present as a match condition, we need to defer whether or not to decorate until when the `LLDBTestCaseFactory` metaclass expands the test case into its potential variants. This is still early enough that the standard `unittest` framework will recognize the test as xfail/skip by the time the test actually runs. TestDecorators exhibits the edge cases more thoroughly. With the exception of `@expectedFailureIf` (added by this commit), all those test cases pass prior to this commit. This is a followup to 212a60e.
1 parent cebe4de commit d0d0727

File tree

3 files changed

+179
-6
lines changed

3 files changed

+179
-6
lines changed

lldb/packages/Python/lldbsuite/test/decorators.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,21 @@ def _compiler_supports(
113113
return True
114114

115115

116+
def expectedFailureIf(condition, bugnumber=None):
117+
def expectedFailure_impl(func):
118+
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
119+
raise Exception("Decorator can only be used to decorate a test method")
120+
121+
if condition:
122+
return unittest2.expectedFailure(func)
123+
return func
124+
125+
if callable(bugnumber):
126+
return expectedFailure_impl(bugnumber)
127+
else:
128+
return expectedFailure_impl
129+
130+
116131
def expectedFailureIfFn(expected_fn, bugnumber=None):
117132
def expectedFailure_impl(func):
118133
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
@@ -174,6 +189,34 @@ def wrapper(*args, **kwargs):
174189
return skipTestIfFn_impl
175190

176191

192+
def _xfailForDebugInfo(expected_fn, bugnumber=None):
193+
def expectedFailure_impl(func):
194+
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
195+
raise Exception("Decorator can only be used to decorate a test method")
196+
197+
func.__xfail_for_debug_info_cat_fn__ = expected_fn
198+
return func
199+
200+
if callable(bugnumber):
201+
return expectedFailure_impl(bugnumber)
202+
else:
203+
return expectedFailure_impl
204+
205+
206+
def _skipForDebugInfo(expected_fn, bugnumber=None):
207+
def skipImpl(func):
208+
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
209+
raise Exception("Decorator can only be used to decorate a test method")
210+
211+
func.__skip_for_debug_info_cat_fn__ = expected_fn
212+
return func
213+
214+
if callable(bugnumber):
215+
return skipImpl(bugnumber)
216+
else:
217+
return skipImpl
218+
219+
177220
def _decorateTest(
178221
mode,
179222
bugnumber=None,
@@ -191,7 +234,7 @@ def _decorateTest(
191234
dwarf_version=None,
192235
setting=None,
193236
):
194-
def fn(self):
237+
def fn(actual_debug_info=None):
195238
skip_for_os = _match_decorator_property(
196239
lldbplatform.translate(oslist), lldbplatformutil.getPlatform()
197240
)
@@ -204,7 +247,7 @@ def fn(self):
204247
skip_for_arch = _match_decorator_property(
205248
archs, lldbplatformutil.getArchitecture()
206249
)
207-
skip_for_debug_info = _match_decorator_property(debug_info, self.getDebugInfo())
250+
skip_for_debug_info = _match_decorator_property(debug_info, actual_debug_info)
208251
skip_for_triple = _match_decorator_property(
209252
triple, lldb.selected_platform.GetTriple()
210253
)
@@ -279,9 +322,13 @@ def fn(self):
279322
return reason_str
280323

281324
if mode == DecorateMode.Skip:
325+
if debug_info:
326+
return _skipForDebugInfo(fn, bugnumber)
282327
return skipTestIfFn(fn, bugnumber)
283328
elif mode == DecorateMode.Xfail:
284-
return expectedFailureIfFn(fn, bugnumber)
329+
if debug_info:
330+
return _xfailForDebugInfo(fn, bugnumber)
331+
return expectedFailureIf(fn(), bugnumber)
285332
else:
286333
return None
287334

lldb/packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,11 @@ def __new__(cls, name, bases, attrs):
16671667
if original_testcase.NO_DEBUG_INFO_TESTCASE:
16681668
return original_testcase
16691669

1670+
# Default implementation for skip/xfail reason based on the debug category,
1671+
# where "None" means to run the test as usual.
1672+
def no_reason(_):
1673+
return None
1674+
16701675
newattrs = {}
16711676
for attrname, attrvalue in attrs.items():
16721677
if attrname.startswith("test") and not getattr(
@@ -1688,6 +1693,12 @@ def __new__(cls, name, bases, attrs):
16881693
if can_replicate
16891694
]
16901695

1696+
xfail_for_debug_info_cat_fn = getattr(
1697+
attrvalue, "__xfail_for_debug_info_cat_fn__", no_reason
1698+
)
1699+
skip_for_debug_info_cat_fn = getattr(
1700+
attrvalue, "__skip_for_debug_info_cat_fn__", no_reason
1701+
)
16911702
for cat in categories:
16921703

16931704
@decorators.add_test_categories([cat])
@@ -1698,6 +1709,17 @@ def test_method(self, attrvalue=attrvalue):
16981709
method_name = attrname + "_" + cat
16991710
test_method.__name__ = method_name
17001711
test_method.debug_info = cat
1712+
1713+
xfail_reason = xfail_for_debug_info_cat_fn(cat)
1714+
if xfail_reason:
1715+
test_method = unittest2.expectedFailure(xfail_reason)(
1716+
test_method
1717+
)
1718+
1719+
skip_reason = skip_for_debug_info_cat_fn(cat)
1720+
if skip_reason:
1721+
test_method = unittest2.skip(skip_reason)(test_method)
1722+
17011723
newattrs[method_name] = test_method
17021724

17031725
else:
Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,115 @@
1-
from lldbsuite.test.lldbtest import Base
1+
import re
2+
3+
from lldbsuite.test.lldbtest import TestBase
24
from lldbsuite.test.decorators import *
35

46

5-
class TestDecorators(Base):
7+
def expectedFailureDwarf(bugnumber=None):
8+
return expectedFailureAll(bugnumber, debug_info="dwarf")
9+
10+
11+
class TestDecoratorsNoDebugInfoClass(TestBase):
612
NO_DEBUG_INFO_TESTCASE = True
713

814
@expectedFailureAll(debug_info="dwarf")
9-
def test_decorator_skip_no_debug_info(self):
15+
def test_decorator_xfail(self):
1016
"""Test that specifying a debug info category works for a NO_DEBUG_INFO_TESTCASE"""
17+
18+
@expectedFailureDwarf
19+
def test_decorator_xfail_bare_decorator(self):
20+
"""Same as test_decorator_xfail, but with a custom decorator w/ a bare syntax"""
21+
22+
@expectedFailureDwarf()
23+
def test_decorator_xfail_decorator_empty_args(self):
24+
"""Same as test_decorator_xfail, but with a custom decorator w/ no args"""
25+
26+
@add_test_categories(["dwarf"])
27+
def test_add_test_categories(self):
28+
# Note: the "dwarf" test category is ignored, because we don't generate any debug info test variants
29+
self.assertIsNone(self.getDebugInfo())
30+
31+
@expectedFailureAll
32+
def test_xfail_regexp(self):
33+
"""Test that expectedFailureAll can be empty (but please just use expectedFailure)"""
34+
self.fail()
35+
36+
@expectedFailureAll(compiler=re.compile(".*"))
37+
def test_xfail_regexp(self):
38+
"""Test that xfail can take a regex as a matcher"""
39+
self.fail()
40+
41+
@expectedFailureAll(compiler=no_match(re.compile(".*")))
42+
def test_xfail_no_match(self):
43+
"""Test that xfail can take a no_match matcher"""
44+
pass
45+
46+
@expectedFailureIf(condition=True)
47+
def test_xfail_condition_true(self):
48+
self.fail()
49+
50+
@expectedFailureIf(condition=False)
51+
def test_xfail_condition_false(self):
52+
pass
53+
54+
55+
class TestDecorators(TestBase):
56+
@expectedFailureAll(debug_info="dwarf")
57+
def test_decorator_xfail(self):
58+
"""Test that expectedFailureAll fails for the debug_info variant"""
59+
if self.getDebugInfo() == "dwarf":
60+
self.fail()
61+
62+
@skipIf(debug_info="dwarf")
63+
def test_decorator_skip(self):
64+
"""Test that skipIf skips the debug_info variant"""
65+
self.assertNotEqual(self.getDebugInfo(), "dwarf")
66+
67+
@expectedFailureDwarf
68+
def test_decorator_xfail2(self):
69+
"""Same as test_decorator_xfail, but with a custom decorator w/ a bare syntax"""
70+
if self.getDebugInfo() == "dwarf":
71+
self.fail()
72+
73+
@expectedFailureDwarf()
74+
def test_decorator_xfail3(self):
75+
"""Same as test_decorator_xfail, but with a custom decorator w/ no args"""
76+
if self.getDebugInfo() == "dwarf":
77+
self.fail()
78+
79+
@add_test_categories(["dwarf"])
80+
def test_add_test_categories(self):
81+
"""Test that add_test_categories limits the kinds of debug info test variants"""
82+
self.assertEqual(self.getDebugInfo(), "dwarf")
83+
84+
@expectedFailureAll(compiler="fake", debug_info="dwarf")
85+
def test_decorator_xfail_all(self):
86+
"""Test that expectedFailureAll requires all conditions to match to be xfail"""
87+
88+
@skipIf(compiler="fake", debug_info="dwarf")
89+
def test_decorator_skip2(self):
90+
"""Test that expectedFailureAll fails for the debug_info variant"""
91+
# Note: the following assertion would fail, if this were not skipped:
92+
# self.assertNotEqual(self.getDebugInfo(), "dwarf")
93+
94+
@expectedFailureAll
95+
def test_xfail_regexp(self):
96+
"""Test that xfail can be empty"""
97+
self.fail()
98+
99+
@expectedFailureAll(compiler=re.compile(".*"))
100+
def test_xfail_regexp(self):
101+
"""Test that xfail can take a regex as a matcher"""
102+
self.fail()
103+
104+
@expectedFailureAll(compiler=no_match(re.compile(".*")))
105+
def test_xfail_no_match(self):
106+
"""Test that xfail can take a no_match matcher"""
107+
pass
108+
109+
@expectedFailureIf(condition=True)
110+
def test_xfail_condition_true(self):
111+
self.fail()
112+
113+
@expectedFailureIf(condition=False)
114+
def test_xfail_condition_false(self):
11115
pass

0 commit comments

Comments
 (0)