test_ro.py 7.2 KB

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