test_ro.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. ##############################################################################
  2. #
  3. # Copyright (c) 2014 Zope Foundation and Contributors.
  4. # All Rights Reserved.
  5. #
  6. # This software is subject to the provisions of the Zope Public License,
  7. # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
  8. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  9. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  10. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  11. # FOR A PARTICULAR PURPOSE.
  12. #
  13. ##############################################################################
  14. """Resolution ordering utility tests"""
  15. import unittest
  16. # pylint:disable=blacklisted-name,protected-access,attribute-defined-outside-init
  17. class Test__mergeOrderings(unittest.TestCase):
  18. def _callFUT(self, orderings):
  19. from zope.interface.ro import _legacy_mergeOrderings
  20. return _legacy_mergeOrderings(orderings)
  21. def test_empty(self):
  22. self.assertEqual(self._callFUT([]), [])
  23. def test_single(self):
  24. self.assertEqual(self._callFUT(['a', 'b', 'c']), ['a', 'b', 'c'])
  25. def test_w_duplicates(self):
  26. self.assertEqual(self._callFUT([['a'], ['b', 'a']]), ['b', 'a'])
  27. def test_suffix_across_multiple_duplicates(self):
  28. O1 = ['x', 'y', 'z']
  29. O2 = ['q', 'z']
  30. O3 = [1, 3, 5]
  31. O4 = ['z']
  32. self.assertEqual(self._callFUT([O1, O2, O3, O4]),
  33. ['x', 'y', 'q', 1, 3, 5, 'z'])
  34. class Test__flatten(unittest.TestCase):
  35. def _callFUT(self, ob):
  36. from zope.interface.ro import _legacy_flatten
  37. return _legacy_flatten(ob)
  38. def test_w_empty_bases(self):
  39. class Foo(object):
  40. pass
  41. foo = Foo()
  42. foo.__bases__ = ()
  43. self.assertEqual(self._callFUT(foo), [foo])
  44. def test_w_single_base(self):
  45. class Foo(object):
  46. pass
  47. self.assertEqual(self._callFUT(Foo), [Foo, object])
  48. def test_w_bases(self):
  49. class Foo(object):
  50. pass
  51. class Bar(Foo):
  52. pass
  53. self.assertEqual(self._callFUT(Bar), [Bar, Foo, object])
  54. def test_w_diamond(self):
  55. class Foo(object):
  56. pass
  57. class Bar(Foo):
  58. pass
  59. class Baz(Foo):
  60. pass
  61. class Qux(Bar, Baz):
  62. pass
  63. self.assertEqual(self._callFUT(Qux),
  64. [Qux, Bar, Foo, object, Baz, Foo, object])
  65. class Test_ro(unittest.TestCase):
  66. maxDiff = None
  67. def _callFUT(self, ob, **kwargs):
  68. from zope.interface.ro import _legacy_ro
  69. return _legacy_ro(ob, **kwargs)
  70. def test_w_empty_bases(self):
  71. class Foo(object):
  72. pass
  73. foo = Foo()
  74. foo.__bases__ = ()
  75. self.assertEqual(self._callFUT(foo), [foo])
  76. def test_w_single_base(self):
  77. class Foo(object):
  78. pass
  79. self.assertEqual(self._callFUT(Foo), [Foo, object])
  80. def test_w_bases(self):
  81. class Foo(object):
  82. pass
  83. class Bar(Foo):
  84. pass
  85. self.assertEqual(self._callFUT(Bar), [Bar, Foo, object])
  86. def test_w_diamond(self):
  87. class Foo(object):
  88. pass
  89. class Bar(Foo):
  90. pass
  91. class Baz(Foo):
  92. pass
  93. class Qux(Bar, Baz):
  94. pass
  95. self.assertEqual(self._callFUT(Qux),
  96. [Qux, Bar, Baz, Foo, object])
  97. def _make_IOErr(self):
  98. # This can't be done in the standard C3 ordering.
  99. class Foo(object):
  100. def __init__(self, name, *bases):
  101. self.__name__ = name
  102. self.__bases__ = bases
  103. def __repr__(self): # pragma: no cover
  104. return self.__name__
  105. # Mimic what classImplements(IOError, IIOError)
  106. # does.
  107. IEx = Foo('IEx')
  108. IStdErr = Foo('IStdErr', IEx)
  109. IEnvErr = Foo('IEnvErr', IStdErr)
  110. IIOErr = Foo('IIOErr', IEnvErr)
  111. IOSErr = Foo('IOSErr', IEnvErr)
  112. IOErr = Foo('IOErr', IEnvErr, IIOErr, IOSErr)
  113. return IOErr, [IOErr, IIOErr, IOSErr, IEnvErr, IStdErr, IEx]
  114. def test_non_orderable(self):
  115. IOErr, bases = self._make_IOErr()
  116. self.assertEqual(self._callFUT(IOErr), bases)
  117. def test_mixed_inheritance_and_implementation(self):
  118. # https://github.com/zopefoundation/zope.interface/issues/8
  119. # This test should fail, but doesn't, as described in that issue.
  120. # pylint:disable=inherit-non-class
  121. from zope.interface import implementer
  122. from zope.interface import Interface
  123. from zope.interface import providedBy
  124. from zope.interface import implementedBy
  125. class IFoo(Interface):
  126. pass
  127. @implementer(IFoo)
  128. class ImplementsFoo(object):
  129. pass
  130. class ExtendsFoo(ImplementsFoo):
  131. pass
  132. class ImplementsNothing(object):
  133. pass
  134. class ExtendsFooImplementsNothing(ExtendsFoo, ImplementsNothing):
  135. pass
  136. self.assertEqual(
  137. self._callFUT(providedBy(ExtendsFooImplementsNothing())),
  138. [implementedBy(ExtendsFooImplementsNothing),
  139. implementedBy(ExtendsFoo),
  140. implementedBy(ImplementsFoo),
  141. IFoo,
  142. Interface,
  143. implementedBy(ImplementsNothing),
  144. implementedBy(object)])
  145. class C3Setting(object):
  146. def __init__(self, setting, value):
  147. self._setting = setting
  148. self._value = value
  149. def __enter__(self):
  150. from zope.interface import ro
  151. setattr(ro.C3, self._setting.__name__, self._value)
  152. def __exit__(self, t, v, tb):
  153. from zope.interface import ro
  154. setattr(ro.C3, self._setting.__name__, self._setting)
  155. class TestC3(unittest.TestCase):
  156. def _makeOne(self, C, strict=False, base_mros=None):
  157. from zope.interface.ro import C3
  158. return C3.resolver(C, strict, base_mros)
  159. def test_base_mros_given(self):
  160. c3 = self._makeOne(type(self), base_mros={unittest.TestCase: unittest.TestCase.__mro__})
  161. memo = c3.memo
  162. self.assertIn(unittest.TestCase, memo)
  163. # We used the StaticMRO class
  164. self.assertIsNone(memo[unittest.TestCase].had_inconsistency)
  165. def test_one_base_optimization(self):
  166. c3 = self._makeOne(type(self))
  167. # Even though we didn't call .mro() yet, the MRO has been
  168. # computed.
  169. self.assertIsNotNone(c3._C3__mro) # pylint:disable=no-member
  170. c3._merge = None
  171. self.assertEqual(c3.mro(), list(type(self).__mro__))
  172. class Test_ROComparison(unittest.TestCase):
  173. class MockC3(object):
  174. direct_inconsistency = False
  175. bases_had_inconsistency = False
  176. def _makeOne(self, c3=None, c3_ro=(), legacy_ro=()):
  177. from zope.interface.ro import _ROComparison
  178. return _ROComparison(c3 or self.MockC3(), c3_ro, legacy_ro)
  179. def test_inconsistent_label(self):
  180. comp = self._makeOne()
  181. self.assertEqual('no', comp._inconsistent_label)
  182. comp.c3.direct_inconsistency = True
  183. self.assertEqual("direct", comp._inconsistent_label)
  184. comp.c3.bases_had_inconsistency = True
  185. self.assertEqual("direct+bases", comp._inconsistent_label)
  186. comp.c3.direct_inconsistency = False
  187. self.assertEqual('bases', comp._inconsistent_label)