py-zope.interface/src/zope/interface/common/tests/__init__.py

136 lines
5.1 KiB
Python

##############################################################################
# Copyright (c) 2020 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
##############################################################################
import unittest
from zope.interface.verify import verifyClass
from zope.interface.verify import verifyObject
from zope.interface.common import ABCInterface
from zope.interface.common import ABCInterfaceClass
def iter_abc_interfaces(predicate=lambda iface: True):
# Iterate ``(iface, classes)``, where ``iface`` is a descendent of
# the ABCInterfaceClass passing the *predicate* and ``classes`` is
# an iterable of classes registered to conform to that interface.
#
# Note that some builtin classes are registered for two distinct
# parts of the ABC/interface tree. For example, bytearray is both ByteString
# and MutableSequence.
seen = set()
stack = list(ABCInterface.dependents) # subclasses, but also implementedBy objects
while stack:
iface = stack.pop(0)
if iface in seen or not isinstance(iface, ABCInterfaceClass):
continue
seen.add(iface)
stack.extend(list(iface.dependents))
if not predicate(iface):
continue
registered = set(iface.getRegisteredConformers())
registered -= set(iface._ABCInterfaceClass__ignored_classes)
if registered:
yield iface, registered
def add_abc_interface_tests(cls, module):
def predicate(iface):
return iface.__module__ == module
add_verify_tests(cls, iter_abc_interfaces(predicate))
def add_verify_tests(cls, iface_classes_iter):
cls.maxDiff = None
for iface, registered_classes in iface_classes_iter:
for stdlib_class in registered_classes:
def test(self, stdlib_class=stdlib_class, iface=iface):
if stdlib_class in self.UNVERIFIABLE or stdlib_class.__name__ in self.UNVERIFIABLE:
self.skipTest("Unable to verify %s" % stdlib_class)
self.assertTrue(self.verify(iface, stdlib_class))
suffix = "%s_%s_%s" % (
stdlib_class.__name__,
iface.__module__.replace('.', '_'),
iface.__name__
)
name = 'test_auto_' + suffix
test.__name__ = name
assert not hasattr(cls, name), (name, list(cls.__dict__))
setattr(cls, name, test)
def test_ro(self, stdlib_class=stdlib_class, iface=iface):
from zope.interface import ro
from zope.interface import implementedBy
from zope.interface import Interface
self.assertEqual(
tuple(ro.ro(iface, strict=True)),
iface.__sro__)
implements = implementedBy(stdlib_class)
sro = implements.__sro__
self.assertIs(sro[-1], Interface)
if stdlib_class not in self.UNVERIFIABLE_RO:
# Check that we got the strict C3 resolution order, unless
# we know we cannot. Note that 'Interface' is virtual base
# that doesn't necessarily appear at the same place in the
# calculated SRO as in the final SRO.
strict = stdlib_class not in self.NON_STRICT_RO
isro = ro.ro(implements, strict=strict)
isro.remove(Interface)
isro.append(Interface)
self.assertEqual(tuple(isro), sro)
name = 'test_auto_ro_' + suffix
test_ro.__name__ = name
assert not hasattr(cls, name)
setattr(cls, name, test_ro)
class VerifyClassMixin(unittest.TestCase):
verifier = staticmethod(verifyClass)
UNVERIFIABLE = ()
NON_STRICT_RO = ()
UNVERIFIABLE_RO = ()
def _adjust_object_before_verify(self, iface, x):
return x
def verify(self, iface, klass, **kwargs):
return self.verifier(iface,
self._adjust_object_before_verify(iface, klass),
**kwargs)
class VerifyObjectMixin(VerifyClassMixin):
verifier = staticmethod(verifyObject)
CONSTRUCTORS = {
}
def _adjust_object_before_verify(self, iface, x):
constructor = self.CONSTRUCTORS.get(x)
if not constructor:
constructor = self.CONSTRUCTORS.get(iface)
if not constructor:
constructor = self.CONSTRUCTORS.get(x.__name__)
if not constructor:
constructor = x
if constructor is unittest.SkipTest:
self.skipTest("Cannot create " + str(x))
result = constructor()
if hasattr(result, 'close'):
self.addCleanup(result.close)
return result