mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-17 00:10:28 +00:00
[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 212a60ec37322f853e91e171b305479b1abff2f2.
This commit is contained in:
parent
cebe4de66f
commit
d0d0727104
@ -113,6 +113,21 @@ def _compiler_supports(
|
||||
return True
|
||||
|
||||
|
||||
def expectedFailureIf(condition, bugnumber=None):
|
||||
def expectedFailure_impl(func):
|
||||
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
|
||||
raise Exception("Decorator can only be used to decorate a test method")
|
||||
|
||||
if condition:
|
||||
return unittest2.expectedFailure(func)
|
||||
return func
|
||||
|
||||
if callable(bugnumber):
|
||||
return expectedFailure_impl(bugnumber)
|
||||
else:
|
||||
return expectedFailure_impl
|
||||
|
||||
|
||||
def expectedFailureIfFn(expected_fn, bugnumber=None):
|
||||
def expectedFailure_impl(func):
|
||||
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
|
||||
@ -174,6 +189,34 @@ def skipTestIfFn(expected_fn, bugnumber=None):
|
||||
return skipTestIfFn_impl
|
||||
|
||||
|
||||
def _xfailForDebugInfo(expected_fn, bugnumber=None):
|
||||
def expectedFailure_impl(func):
|
||||
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
|
||||
raise Exception("Decorator can only be used to decorate a test method")
|
||||
|
||||
func.__xfail_for_debug_info_cat_fn__ = expected_fn
|
||||
return func
|
||||
|
||||
if callable(bugnumber):
|
||||
return expectedFailure_impl(bugnumber)
|
||||
else:
|
||||
return expectedFailure_impl
|
||||
|
||||
|
||||
def _skipForDebugInfo(expected_fn, bugnumber=None):
|
||||
def skipImpl(func):
|
||||
if isinstance(func, type) and issubclass(func, unittest2.TestCase):
|
||||
raise Exception("Decorator can only be used to decorate a test method")
|
||||
|
||||
func.__skip_for_debug_info_cat_fn__ = expected_fn
|
||||
return func
|
||||
|
||||
if callable(bugnumber):
|
||||
return skipImpl(bugnumber)
|
||||
else:
|
||||
return skipImpl
|
||||
|
||||
|
||||
def _decorateTest(
|
||||
mode,
|
||||
bugnumber=None,
|
||||
@ -191,7 +234,7 @@ def _decorateTest(
|
||||
dwarf_version=None,
|
||||
setting=None,
|
||||
):
|
||||
def fn(self):
|
||||
def fn(actual_debug_info=None):
|
||||
skip_for_os = _match_decorator_property(
|
||||
lldbplatform.translate(oslist), lldbplatformutil.getPlatform()
|
||||
)
|
||||
@ -204,7 +247,7 @@ def _decorateTest(
|
||||
skip_for_arch = _match_decorator_property(
|
||||
archs, lldbplatformutil.getArchitecture()
|
||||
)
|
||||
skip_for_debug_info = _match_decorator_property(debug_info, self.getDebugInfo())
|
||||
skip_for_debug_info = _match_decorator_property(debug_info, actual_debug_info)
|
||||
skip_for_triple = _match_decorator_property(
|
||||
triple, lldb.selected_platform.GetTriple()
|
||||
)
|
||||
@ -279,9 +322,13 @@ def _decorateTest(
|
||||
return reason_str
|
||||
|
||||
if mode == DecorateMode.Skip:
|
||||
if debug_info:
|
||||
return _skipForDebugInfo(fn, bugnumber)
|
||||
return skipTestIfFn(fn, bugnumber)
|
||||
elif mode == DecorateMode.Xfail:
|
||||
return expectedFailureIfFn(fn, bugnumber)
|
||||
if debug_info:
|
||||
return _xfailForDebugInfo(fn, bugnumber)
|
||||
return expectedFailureIf(fn(), bugnumber)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -1667,6 +1667,11 @@ class LLDBTestCaseFactory(type):
|
||||
if original_testcase.NO_DEBUG_INFO_TESTCASE:
|
||||
return original_testcase
|
||||
|
||||
# Default implementation for skip/xfail reason based on the debug category,
|
||||
# where "None" means to run the test as usual.
|
||||
def no_reason(_):
|
||||
return None
|
||||
|
||||
newattrs = {}
|
||||
for attrname, attrvalue in attrs.items():
|
||||
if attrname.startswith("test") and not getattr(
|
||||
@ -1688,6 +1693,12 @@ class LLDBTestCaseFactory(type):
|
||||
if can_replicate
|
||||
]
|
||||
|
||||
xfail_for_debug_info_cat_fn = getattr(
|
||||
attrvalue, "__xfail_for_debug_info_cat_fn__", no_reason
|
||||
)
|
||||
skip_for_debug_info_cat_fn = getattr(
|
||||
attrvalue, "__skip_for_debug_info_cat_fn__", no_reason
|
||||
)
|
||||
for cat in categories:
|
||||
|
||||
@decorators.add_test_categories([cat])
|
||||
@ -1698,6 +1709,17 @@ class LLDBTestCaseFactory(type):
|
||||
method_name = attrname + "_" + cat
|
||||
test_method.__name__ = method_name
|
||||
test_method.debug_info = cat
|
||||
|
||||
xfail_reason = xfail_for_debug_info_cat_fn(cat)
|
||||
if xfail_reason:
|
||||
test_method = unittest2.expectedFailure(xfail_reason)(
|
||||
test_method
|
||||
)
|
||||
|
||||
skip_reason = skip_for_debug_info_cat_fn(cat)
|
||||
if skip_reason:
|
||||
test_method = unittest2.skip(skip_reason)(test_method)
|
||||
|
||||
newattrs[method_name] = test_method
|
||||
|
||||
else:
|
||||
|
@ -1,11 +1,115 @@
|
||||
from lldbsuite.test.lldbtest import Base
|
||||
import re
|
||||
|
||||
from lldbsuite.test.lldbtest import TestBase
|
||||
from lldbsuite.test.decorators import *
|
||||
|
||||
|
||||
class TestDecorators(Base):
|
||||
def expectedFailureDwarf(bugnumber=None):
|
||||
return expectedFailureAll(bugnumber, debug_info="dwarf")
|
||||
|
||||
|
||||
class TestDecoratorsNoDebugInfoClass(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
@expectedFailureAll(debug_info="dwarf")
|
||||
def test_decorator_skip_no_debug_info(self):
|
||||
def test_decorator_xfail(self):
|
||||
"""Test that specifying a debug info category works for a NO_DEBUG_INFO_TESTCASE"""
|
||||
|
||||
@expectedFailureDwarf
|
||||
def test_decorator_xfail_bare_decorator(self):
|
||||
"""Same as test_decorator_xfail, but with a custom decorator w/ a bare syntax"""
|
||||
|
||||
@expectedFailureDwarf()
|
||||
def test_decorator_xfail_decorator_empty_args(self):
|
||||
"""Same as test_decorator_xfail, but with a custom decorator w/ no args"""
|
||||
|
||||
@add_test_categories(["dwarf"])
|
||||
def test_add_test_categories(self):
|
||||
# Note: the "dwarf" test category is ignored, because we don't generate any debug info test variants
|
||||
self.assertIsNone(self.getDebugInfo())
|
||||
|
||||
@expectedFailureAll
|
||||
def test_xfail_regexp(self):
|
||||
"""Test that expectedFailureAll can be empty (but please just use expectedFailure)"""
|
||||
self.fail()
|
||||
|
||||
@expectedFailureAll(compiler=re.compile(".*"))
|
||||
def test_xfail_regexp(self):
|
||||
"""Test that xfail can take a regex as a matcher"""
|
||||
self.fail()
|
||||
|
||||
@expectedFailureAll(compiler=no_match(re.compile(".*")))
|
||||
def test_xfail_no_match(self):
|
||||
"""Test that xfail can take a no_match matcher"""
|
||||
pass
|
||||
|
||||
@expectedFailureIf(condition=True)
|
||||
def test_xfail_condition_true(self):
|
||||
self.fail()
|
||||
|
||||
@expectedFailureIf(condition=False)
|
||||
def test_xfail_condition_false(self):
|
||||
pass
|
||||
|
||||
|
||||
class TestDecorators(TestBase):
|
||||
@expectedFailureAll(debug_info="dwarf")
|
||||
def test_decorator_xfail(self):
|
||||
"""Test that expectedFailureAll fails for the debug_info variant"""
|
||||
if self.getDebugInfo() == "dwarf":
|
||||
self.fail()
|
||||
|
||||
@skipIf(debug_info="dwarf")
|
||||
def test_decorator_skip(self):
|
||||
"""Test that skipIf skips the debug_info variant"""
|
||||
self.assertNotEqual(self.getDebugInfo(), "dwarf")
|
||||
|
||||
@expectedFailureDwarf
|
||||
def test_decorator_xfail2(self):
|
||||
"""Same as test_decorator_xfail, but with a custom decorator w/ a bare syntax"""
|
||||
if self.getDebugInfo() == "dwarf":
|
||||
self.fail()
|
||||
|
||||
@expectedFailureDwarf()
|
||||
def test_decorator_xfail3(self):
|
||||
"""Same as test_decorator_xfail, but with a custom decorator w/ no args"""
|
||||
if self.getDebugInfo() == "dwarf":
|
||||
self.fail()
|
||||
|
||||
@add_test_categories(["dwarf"])
|
||||
def test_add_test_categories(self):
|
||||
"""Test that add_test_categories limits the kinds of debug info test variants"""
|
||||
self.assertEqual(self.getDebugInfo(), "dwarf")
|
||||
|
||||
@expectedFailureAll(compiler="fake", debug_info="dwarf")
|
||||
def test_decorator_xfail_all(self):
|
||||
"""Test that expectedFailureAll requires all conditions to match to be xfail"""
|
||||
|
||||
@skipIf(compiler="fake", debug_info="dwarf")
|
||||
def test_decorator_skip2(self):
|
||||
"""Test that expectedFailureAll fails for the debug_info variant"""
|
||||
# Note: the following assertion would fail, if this were not skipped:
|
||||
# self.assertNotEqual(self.getDebugInfo(), "dwarf")
|
||||
|
||||
@expectedFailureAll
|
||||
def test_xfail_regexp(self):
|
||||
"""Test that xfail can be empty"""
|
||||
self.fail()
|
||||
|
||||
@expectedFailureAll(compiler=re.compile(".*"))
|
||||
def test_xfail_regexp(self):
|
||||
"""Test that xfail can take a regex as a matcher"""
|
||||
self.fail()
|
||||
|
||||
@expectedFailureAll(compiler=no_match(re.compile(".*")))
|
||||
def test_xfail_no_match(self):
|
||||
"""Test that xfail can take a no_match matcher"""
|
||||
pass
|
||||
|
||||
@expectedFailureIf(condition=True)
|
||||
def test_xfail_condition_true(self):
|
||||
self.fail()
|
||||
|
||||
@expectedFailureIf(condition=False)
|
||||
def test_xfail_condition_false(self):
|
||||
pass
|
||||
|
Loading…
x
Reference in New Issue
Block a user