Fixed #27214 -- Made skip db features decorators respect wrapping order and inheritance.

This commit is contained in:
Simon Charette 2016-09-12 22:06:31 -04:00
parent 0627858ada
commit 18c72d59e0
2 changed files with 32 additions and 7 deletions

View File

@ -1083,11 +1083,23 @@ class TestCase(TransactionTestCase):
class CheckCondition(object): class CheckCondition(object):
"""Descriptor class for deferred condition checking""" """Descriptor class for deferred condition checking"""
def __init__(self, cond_func): def __init__(self, *conditions):
self.cond_func = cond_func self.conditions = conditions
def add_condition(self, condition, reason):
return self.__class__(*self.conditions + ((condition, reason),))
def __get__(self, instance, cls=None): def __get__(self, instance, cls=None):
return self.cond_func() # Trigger access for all bases.
if any(getattr(base, '__unittest_skip__', False) for base in cls.__bases__):
return True
for condition, reason in self.conditions:
if condition():
# Override this descriptor's value and set the skip reason.
cls.__unittest_skip__ = True
cls.__unittest_skip_why__ = reason
return True
return False
def _deferredSkip(condition, reason): def _deferredSkip(condition, reason):
@ -1103,8 +1115,13 @@ def _deferredSkip(condition, reason):
else: else:
# Assume a class is decorated # Assume a class is decorated
test_item = test_func test_item = test_func
test_item.__unittest_skip__ = CheckCondition(condition) # Retrieve the possibly existing value from the class's dict to
test_item.__unittest_skip_why__ = reason # avoid triggering the descriptor.
skip = test_func.__dict__.get('__unittest_skip__')
if isinstance(skip, CheckCondition):
test_item.__unittest_skip__ = skip.add_condition(condition, reason)
elif skip is not True:
test_item.__unittest_skip__ = CheckCondition((condition, reason))
return test_item return test_item
return decorator return decorator

View File

@ -106,20 +106,28 @@ class SkippingClassTestCase(SimpleTestCase):
def test_dummy(self): def test_dummy(self):
return return
@skipUnlessDBFeature("missing")
@skipIfDBFeature("__class__") @skipIfDBFeature("__class__")
class SkippedTests(unittest.TestCase): class SkippedTests(unittest.TestCase):
def test_will_be_skipped(self): def test_will_be_skipped(self):
self.fail("We should never arrive here.") self.fail("We should never arrive here.")
@skipIfDBFeature("__dict__")
class SkippedTestsSubclass(SkippedTests):
pass
test_suite = unittest.TestSuite() test_suite = unittest.TestSuite()
test_suite.addTest(NotSkippedTests('test_dummy')) test_suite.addTest(NotSkippedTests('test_dummy'))
try: try:
test_suite.addTest(SkippedTests('test_will_be_skipped')) test_suite.addTest(SkippedTests('test_will_be_skipped'))
test_suite.addTest(SkippedTestsSubclass('test_will_be_skipped'))
except unittest.SkipTest: except unittest.SkipTest:
self.fail("SkipTest should not be raised at this stage") self.fail("SkipTest should not be raised at this stage")
result = unittest.TextTestRunner(stream=six.StringIO()).run(test_suite) result = unittest.TextTestRunner(stream=six.StringIO()).run(test_suite)
self.assertEqual(result.testsRun, 2) self.assertEqual(result.testsRun, 3)
self.assertEqual(len(result.skipped), 1) self.assertEqual(len(result.skipped), 2)
self.assertEqual(result.skipped[0][1], 'Database has feature(s) __class__')
self.assertEqual(result.skipped[1][1], 'Database has feature(s) __class__')
@override_settings(ROOT_URLCONF='test_utils.urls') @override_settings(ROOT_URLCONF='test_utils.urls')