forked from OSchip/llvm-project
Refactor some of the xfail / skip decorators to share logic.
Previously the logic of skipIf and expectedFailure were 99% the same, but they took different sets of arguments since they were maintained separately, and had slightly differences in their behavior. This makes everything consistent, there is now only one real implementation, and the previous ones are changed to use the single master implementation. llvm-svn: 258966
This commit is contained in:
parent
74b7965d38
commit
7289e43831
|
@ -15,7 +15,8 @@ import lldbsuite.test.lldbutil as lldbutil
|
|||
def python_leaky_fd_version(test):
|
||||
import sys
|
||||
# Python random module leaks file descriptors on some versions.
|
||||
return sys.version_info >= (2, 7, 8) and sys.version_info < (2, 7, 10)
|
||||
return (sys.version_info >= (2, 7, 8) and sys.version_info < (2, 7, 10),
|
||||
"Python random module leaks file descriptors in this python version")
|
||||
|
||||
|
||||
class AvoidsFdLeakTestCase(TestBase):
|
||||
|
|
|
@ -60,7 +60,8 @@ class AssertingInferiorTestCase(TestBase):
|
|||
lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True)
|
||||
|
||||
def check_stop_reason(self):
|
||||
if matchAndroid(api_levels=list(range(1, 16+1)))(self):
|
||||
match_result, _ = matchAndroid(api_levels=list(range(1, 16+1)))(self)
|
||||
if match_result:
|
||||
# On android until API-16 the abort() call ended in a sigsegv instead of in a sigabrt
|
||||
stop_reason = 'stop reason = signal SIGSEGV'
|
||||
else:
|
||||
|
|
|
@ -16,7 +16,7 @@ class JITLoaderGDBTestCase(TestBase):
|
|||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
@skipTestIfFn(lambda x: True, "llvm.org/pr24702", "Skipped because the test crashes the test runner")
|
||||
@skipTestIfFn(lambda x: (True, "Skipped because the test crashes the test runner"), bugnumber="llvm.org/pr24702")
|
||||
@unittest2.expectedFailure("llvm.org/pr24702")
|
||||
def test_bogus_values(self):
|
||||
"""Test that we handle inferior misusing the GDB JIT interface"""
|
||||
|
|
|
@ -509,6 +509,9 @@ def check_expected_version(comparison, expected, actual):
|
|||
#
|
||||
# Decorators for categorizing test cases.
|
||||
#
|
||||
class DecorateMode:
|
||||
Skip, Xfail = range(2)
|
||||
|
||||
from functools import wraps
|
||||
|
||||
def add_test_categories(cat):
|
||||
|
@ -600,7 +603,8 @@ def expectedFailure(expected_fn, bugnumber=None):
|
|||
def wrapper(*args, **kwargs):
|
||||
from unittest2 import case
|
||||
self = args[0]
|
||||
if expected_fn(self):
|
||||
xfail, reason = expected_fn(self)
|
||||
if xfail:
|
||||
if configuration.results_formatter_object is not None:
|
||||
# Mark this test as expected to fail.
|
||||
configuration.results_formatter_object.handle_event(
|
||||
|
@ -610,8 +614,11 @@ def expectedFailure(expected_fn, bugnumber=None):
|
|||
else:
|
||||
func(*args, **kwargs)
|
||||
return wrapper
|
||||
# if bugnumber is not-callable(incluing None), that means decorator function is called with optional arguments
|
||||
# return decorator in this case, so it will be used to decorating original method
|
||||
# Some decorators can be called both with no arguments (e.g. @expectedFailureWindows)
|
||||
# or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called
|
||||
# the first way, the first argument will be the actual function because decorators are
|
||||
# weird like that. So this is basically a check that says "which syntax was the original
|
||||
# function decorated with?"
|
||||
if six.callable(bugnumber):
|
||||
return expectedFailure_impl(bugnumber)
|
||||
else:
|
||||
|
@ -650,26 +657,21 @@ def matchArchitectures(archs, actual_arch):
|
|||
# @expectedFailureAll, xfail for all platform/compiler/arch,
|
||||
# @expectedFailureAll(compiler='gcc'), xfail for gcc on all platform/architecture
|
||||
# @expectedFailureAll(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), xfail for gcc>=4.9 on linux with i386
|
||||
def expectedFailureAll(bugnumber=None, oslist=None, hostoslist=None, compiler=None, compiler_version=None, archs=None, triple=None, debug_info=None, swig_version=None, py_version=None):
|
||||
def fn(self):
|
||||
oslist_passes = check_list_or_lambda(oslist, self.getPlatform())
|
||||
hostoslist_passes = check_list_or_lambda(hostoslist, getHostPlatform())
|
||||
compiler_passes = check_list_or_lambda(self.getCompiler(), compiler) and self.expectedCompilerVersion(compiler_version)
|
||||
arch_passes = check_list_or_lambda(archs, self.getArchitecture())
|
||||
triple_passes = triple is None or re.match(triple, lldb.DBG.GetSelectedPlatform().GetTriple())
|
||||
debug_info_passes = check_list_or_lambda(debug_info, self.debug_info)
|
||||
swig_version_passes = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version))
|
||||
py_version_passes = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info)
|
||||
|
||||
return (oslist_passes and
|
||||
hostoslist_passes and
|
||||
compiler_passes and
|
||||
arch_passes and
|
||||
triple_passes and
|
||||
debug_info_passes and
|
||||
swig_version_passes and
|
||||
py_version_passes)
|
||||
return expectedFailure(fn, bugnumber)
|
||||
def expectedFailureAll(bugnumber=None,
|
||||
oslist=None, hostoslist=None,
|
||||
compiler=None, compiler_version=None,
|
||||
archs=None, triple=None,
|
||||
debug_info=None,
|
||||
swig_version=None, py_version=None,
|
||||
remote=None):
|
||||
return decorateTest(DecorateMode.Xfail,
|
||||
bugnumber=bugnumber,
|
||||
oslist=oslist, hostoslist=hostoslist,
|
||||
compiler=compiler, compiler_version=compiler_version,
|
||||
archs=archs, triple=triple,
|
||||
debug_info=debug_info,
|
||||
swig_version=swig_version, py_version=py_version,
|
||||
remote=remote)
|
||||
|
||||
def expectedFailureDwarf(bugnumber=None):
|
||||
return expectedFailureAll(bugnumber=bugnumber, debug_info="dwarf")
|
||||
|
@ -697,9 +699,7 @@ def expectedFailureIcc(bugnumber=None):
|
|||
return expectedFailureCompiler('icc', None, bugnumber)
|
||||
|
||||
def expectedFailureArch(arch, bugnumber=None):
|
||||
def fn(self):
|
||||
return arch in self.getArchitecture()
|
||||
return expectedFailure(fn, bugnumber)
|
||||
return decorateTest(DecorateMode.Xfail, archs=arch, bugnumber=bugnumber)
|
||||
|
||||
def expectedFailurei386(bugnumber=None):
|
||||
return expectedFailureArch('i386', bugnumber)
|
||||
|
@ -708,18 +708,10 @@ def expectedFailurex86_64(bugnumber=None):
|
|||
return expectedFailureArch('x86_64', bugnumber)
|
||||
|
||||
def expectedFailureOS(oslist, bugnumber=None, compilers=None, debug_info=None, archs=None):
|
||||
def fn(self):
|
||||
return (self.getPlatform() in oslist and
|
||||
self.expectedCompiler(compilers) and
|
||||
(archs is None or self.getArchitecture() in archs) and
|
||||
(debug_info is None or self.debug_info in debug_info))
|
||||
return expectedFailure(fn, bugnumber)
|
||||
return decorateTest(DecorateMode.Xfail, oslist=oslist, bugnumber=bugnumber, compiler=compilers, archs=archs, debug_info=debug_info)
|
||||
|
||||
def expectedFailureHostOS(oslist, bugnumber=None, compilers=None):
|
||||
def fn(self):
|
||||
return (getHostPlatform() in oslist and
|
||||
self.expectedCompiler(compilers))
|
||||
return expectedFailure(fn, bugnumber)
|
||||
return decorateTest(DecorateMode.Xfail, hostoslist=oslist, bugnumber=bugnumber)
|
||||
|
||||
def expectedFailureDarwin(bugnumber=None, compilers=None, debug_info=None):
|
||||
# For legacy reasons, we support both "darwin" and "macosx" as OS X triples.
|
||||
|
@ -743,12 +735,12 @@ def expectedFailureHostWindows(bugnumber=None, compilers=None):
|
|||
def matchAndroid(api_levels=None, archs=None):
|
||||
def match(self):
|
||||
if not target_is_android():
|
||||
return False
|
||||
return (False, "target is not android")
|
||||
if archs is not None and self.getArchitecture() not in archs:
|
||||
return False
|
||||
return (False, "invalid architecture")
|
||||
if api_levels is not None and android_device_api() not in api_levels:
|
||||
return False
|
||||
return True
|
||||
return (False, "invalid api level")
|
||||
return (True, None)
|
||||
return match
|
||||
|
||||
|
||||
|
@ -779,8 +771,11 @@ def expectedFlakey(expected_fn, bugnumber=None):
|
|||
EventBuilder.event_for_mark_test_rerun_eligible(self))
|
||||
func(*args, **kwargs)
|
||||
return wrapper
|
||||
# if bugnumber is not-callable(incluing None), that means decorator function is called with optional arguments
|
||||
# return decorator in this case, so it will be used to decorating original method
|
||||
# Some decorators can be called both with no arguments (e.g. @expectedFailureWindows)
|
||||
# or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called
|
||||
# the first way, the first argument will be the actual function because decorators are
|
||||
# weird like that. So this is basically a check that says "which syntax was the original
|
||||
# function decorated with?"
|
||||
if six.callable(bugnumber):
|
||||
return expectedFailure_impl(bugnumber)
|
||||
else:
|
||||
|
@ -1072,30 +1067,74 @@ def skipUnlessPlatform(oslist):
|
|||
# @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386
|
||||
|
||||
# TODO: refactor current code, to make skipIfxxx functions to call this function
|
||||
def skipIf(bugnumber=None, oslist=None, compiler=None, compiler_version=None, archs=None, debug_info=None, swig_version=None, py_version=None, remote=None):
|
||||
def decorateTest(mode,
|
||||
bugnumber=None, oslist=None, hostoslist=None,
|
||||
compiler=None, compiler_version=None,
|
||||
archs=None, triple=None,
|
||||
debug_info=None,
|
||||
swig_version=None, py_version=None,
|
||||
remote=None):
|
||||
def fn(self):
|
||||
oslist_passes = check_list_or_lambda(oslist, self.getPlatform())
|
||||
compiler_passes = check_list_or_lambda(self.getCompiler(), compiler) and self.expectedCompilerVersion(compiler_version)
|
||||
arch_passes = check_list_or_lambda(archs, self.getArchitecture())
|
||||
debug_info_passes = check_list_or_lambda(debug_info, self.debug_info)
|
||||
swig_version_passes = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version))
|
||||
py_version_passes = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info)
|
||||
remote_passes = (remote is None) or (remote == (lldb.remote_platform is not None))
|
||||
skip_for_os = check_list_or_lambda(oslist, self.getPlatform())
|
||||
skip_for_hostos = check_list_or_lambda(hostoslist, getHostPlatform())
|
||||
skip_for_compiler = check_list_or_lambda(self.getCompiler(), compiler) and self.expectedCompilerVersion(compiler_version)
|
||||
skip_for_arch = check_list_or_lambda(archs, self.getArchitecture())
|
||||
skip_for_debug_info = check_list_or_lambda(debug_info, self.debug_info)
|
||||
skip_for_triple = triple is None or re.match(triple, lldb.DBG.GetSelectedPlatform().GetTriple())
|
||||
skip_for_swig_version = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version))
|
||||
skip_for_py_version = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info)
|
||||
skip_for_remote = (remote is None) or (remote == (lldb.remote_platform is not None))
|
||||
|
||||
return (oslist_passes and
|
||||
compiler_passes and
|
||||
arch_passes and
|
||||
debug_info_passes and
|
||||
swig_version_passes and
|
||||
py_version_passes and
|
||||
remote_passes)
|
||||
# For the test to be skipped, all specified (e.g. not None) parameters must be True.
|
||||
# An unspecified parameter means "any", so those are marked skip by default. And we skip
|
||||
# the final test if all conditions are True.
|
||||
conditions = [(oslist, skip_for_os, "target o/s"),
|
||||
(hostoslist, skip_for_hostos, "host o/s"),
|
||||
(compiler, skip_for_compiler, "compiler or version"),
|
||||
(archs, skip_for_arch, "architecture"),
|
||||
(debug_info, skip_for_debug_info, "debug info format"),
|
||||
(triple, skip_for_triple, "target triple"),
|
||||
(swig_version, skip_for_swig_version, "swig version"),
|
||||
(py_version, skip_for_py_version, "python version"),
|
||||
(remote, skip_for_remote, "platform locality (remote/local)")]
|
||||
reasons = []
|
||||
final_skip_result = True
|
||||
for this_condition in conditions:
|
||||
final_skip_result = final_skip_result and this_condition[1]
|
||||
if this_condition[0] is not None and this_condition[1]:
|
||||
reasons.append(this_condition[2])
|
||||
reason_str = None
|
||||
if final_skip_result:
|
||||
mode_str = {DecorateMode.Skip : "skipping", DecorateMode.Xfail : "xfailing"}[mode]
|
||||
if len(reasons) > 0:
|
||||
reason_str = ",".join(reasons)
|
||||
reason_str = "{} due to the following parameter(s): {}".format(mode_str, reason_str)
|
||||
else:
|
||||
reason_str = "{} unconditionally"
|
||||
return (final_skip_result, reason_str)
|
||||
|
||||
local_vars = locals()
|
||||
args = [x for x in inspect.getargspec(skipIf).args]
|
||||
arg_vals = [eval(x, globals(), local_vars) for x in args]
|
||||
args = [x for x in zip(args, arg_vals) if x[1] is not None]
|
||||
reasons = ['%s=%s' % (x, str(y)) for (x,y) in args]
|
||||
return skipTestIfFn(fn, bugnumber, skipReason='skipping because ' + ' && '.join(reasons))
|
||||
if mode == DecorateMode.Skip:
|
||||
return skipTestIfFn(fn, bugnumber)
|
||||
elif mode == DecorateMode.Xfail:
|
||||
return expectedFailure(fn, bugnumber)
|
||||
else:
|
||||
return None
|
||||
|
||||
def skipIf(bugnumber=None,
|
||||
oslist=None, hostoslist=None,
|
||||
compiler=None, compiler_version=None,
|
||||
archs=None, triple=None,
|
||||
debug_info=None,
|
||||
swig_version=None, py_version=None,
|
||||
remote=None):
|
||||
return decorateTest(DecorateMode.Skip,
|
||||
bugnumber=bugnumber,
|
||||
oslist=oslist, hostoslist=hostoslist,
|
||||
compiler=compiler, compiler_version=compiler_version,
|
||||
archs=archs, triple=triple,
|
||||
debug_info=debug_info,
|
||||
swig_version=swig_version, py_version=py_version,
|
||||
remote=remote)
|
||||
|
||||
def skipIfDebugInfo(bugnumber=None, debug_info=None):
|
||||
return skipIf(bugnumber=bugnumber, debug_info=debug_info)
|
||||
|
@ -1109,17 +1148,23 @@ def skipIfDwarf(bugnumber=None):
|
|||
def skipIfDsym(bugnumber=None):
|
||||
return skipIfDebugInfo(bugnumber, ["dsym"])
|
||||
|
||||
def skipTestIfFn(expected_fn, bugnumber=None, skipReason=None):
|
||||
def skipTestIfFn(expected_fn, bugnumber=None):
|
||||
def skipTestIfFn_impl(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
from unittest2 import case
|
||||
self = args[0]
|
||||
if expected_fn(self):
|
||||
self.skipTest(skipReason)
|
||||
skip, reason = expected_fn(self)
|
||||
if skip:
|
||||
self.skipTest(reason)
|
||||
else:
|
||||
func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
# Some decorators can be called both with no arguments (e.g. @expectedFailureWindows)
|
||||
# or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called
|
||||
# the first way, the first argument will be the actual function because decorators are
|
||||
# weird like that. So this is basically a check that says "how was the decorator used"
|
||||
if six.callable(bugnumber):
|
||||
return skipTestIfFn_impl(bugnumber)
|
||||
else:
|
||||
|
@ -1186,9 +1231,10 @@ def skipIfTargetAndroid(api_levels=None, archs=None):
|
|||
def wrapper(*args, **kwargs):
|
||||
from unittest2 import case
|
||||
self = args[0]
|
||||
if matchAndroid(api_levels, archs)(self):
|
||||
self.skipTest("skiped on Android target with API %d and architecture %s" %
|
||||
(android_device_api(), self.getArchitecture()))
|
||||
skip_for_android, reason = matchAndroid(api_levels, archs)(self)
|
||||
if skip_for_android:
|
||||
self.skipTest("skiped on Android target with API %d and architecture %s. %s" %
|
||||
(android_device_api(), self.getArchitecture(), reason))
|
||||
func(*args, **kwargs)
|
||||
return wrapper
|
||||
return myImpl
|
||||
|
|
Loading…
Reference in New Issue