############################################################################## # # Copyright (c) 2014 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. # ############################################################################## """Resolution ordering utility tests""" import unittest # pylint:disable=blacklisted-name # pylint:disable=protected-access # pylint:disable=attribute-defined-outside-init class Test__mergeOrderings(unittest.TestCase): def _callFUT(self, orderings): from zope.interface.ro import _legacy_mergeOrderings return _legacy_mergeOrderings(orderings) def test_empty(self): self.assertEqual(self._callFUT([]), []) def test_single(self): self.assertEqual(self._callFUT(['a', 'b', 'c']), ['a', 'b', 'c']) def test_w_duplicates(self): self.assertEqual(self._callFUT([['a'], ['b', 'a']]), ['b', 'a']) def test_suffix_across_multiple_duplicates(self): O1 = ['x', 'y', 'z'] O2 = ['q', 'z'] O3 = [1, 3, 5] O4 = ['z'] self.assertEqual(self._callFUT([O1, O2, O3, O4]), ['x', 'y', 'q', 1, 3, 5, 'z']) class Test__flatten(unittest.TestCase): def _callFUT(self, ob): from zope.interface.ro import _legacy_flatten return _legacy_flatten(ob) def test_w_empty_bases(self): class Foo: pass foo = Foo() foo.__bases__ = () self.assertEqual(self._callFUT(foo), [foo]) def test_w_single_base(self): class Foo: pass self.assertEqual(self._callFUT(Foo), [Foo, object]) def test_w_bases(self): class Foo: pass class Bar(Foo): pass self.assertEqual(self._callFUT(Bar), [Bar, Foo, object]) def test_w_diamond(self): class Foo: pass class Bar(Foo): pass class Baz(Foo): pass class Qux(Bar, Baz): pass self.assertEqual(self._callFUT(Qux), [Qux, Bar, Foo, object, Baz, Foo, object]) class Test_ro(unittest.TestCase): maxDiff = None def _callFUT(self, ob, **kwargs): from zope.interface.ro import _legacy_ro return _legacy_ro(ob, **kwargs) def test_w_empty_bases(self): class Foo: pass foo = Foo() foo.__bases__ = () self.assertEqual(self._callFUT(foo), [foo]) def test_w_single_base(self): class Foo: pass self.assertEqual(self._callFUT(Foo), [Foo, object]) def test_w_bases(self): class Foo: pass class Bar(Foo): pass self.assertEqual(self._callFUT(Bar), [Bar, Foo, object]) def test_w_diamond(self): class Foo: pass class Bar(Foo): pass class Baz(Foo): pass class Qux(Bar, Baz): pass self.assertEqual(self._callFUT(Qux), [Qux, Bar, Baz, Foo, object]) def _make_IOErr(self): # This can't be done in the standard C3 ordering. class Foo: def __init__(self, name, *bases): self.__name__ = name self.__bases__ = bases def __repr__(self): # pragma: no cover return self.__name__ # Mimic what classImplements(IOError, IIOError) # does. IEx = Foo('IEx') IStdErr = Foo('IStdErr', IEx) IEnvErr = Foo('IEnvErr', IStdErr) IIOErr = Foo('IIOErr', IEnvErr) IOSErr = Foo('IOSErr', IEnvErr) IOErr = Foo('IOErr', IEnvErr, IIOErr, IOSErr) return IOErr, [IOErr, IIOErr, IOSErr, IEnvErr, IStdErr, IEx] def test_non_orderable(self): IOErr, bases = self._make_IOErr() self.assertEqual(self._callFUT(IOErr), bases) def test_mixed_inheritance_and_implementation(self): # https://github.com/zopefoundation/zope.interface/issues/8 # This test should fail, but doesn't, as described in that issue. # pylint:disable=inherit-non-class from zope.interface import Interface from zope.interface import implementedBy from zope.interface import implementer from zope.interface import providedBy class IFoo(Interface): pass @implementer(IFoo) class ImplementsFoo: pass class ExtendsFoo(ImplementsFoo): pass class ImplementsNothing: pass class ExtendsFooImplementsNothing(ExtendsFoo, ImplementsNothing): pass self.assertEqual( self._callFUT(providedBy(ExtendsFooImplementsNothing())), [implementedBy(ExtendsFooImplementsNothing), implementedBy(ExtendsFoo), implementedBy(ImplementsFoo), IFoo, Interface, implementedBy(ImplementsNothing), implementedBy(object)]) class C3Setting: def __init__(self, setting, value): self._setting = setting self._value = value def __enter__(self): from zope.interface import ro setattr(ro.C3, self._setting.__name__, self._value) def __exit__(self, t, v, tb): from zope.interface import ro setattr(ro.C3, self._setting.__name__, self._setting) class TestC3(unittest.TestCase): def _makeOne(self, C, strict=False, base_mros=None): from zope.interface.ro import C3 return C3.resolver(C, strict, base_mros) def test_base_mros_given(self): c3 = self._makeOne( type(self), base_mros={unittest.TestCase: unittest.TestCase.__mro__} ) memo = c3.memo self.assertIn(unittest.TestCase, memo) # We used the StaticMRO class self.assertIsNone(memo[unittest.TestCase].had_inconsistency) def test_one_base_optimization(self): c3 = self._makeOne(type(self)) # Even though we didn't call .mro() yet, the MRO has been # computed. self.assertIsNotNone(c3._C3__mro) # pylint:disable=no-member c3._merge = None self.assertEqual(c3.mro(), list(type(self).__mro__)) class Test_ROComparison(unittest.TestCase): class MockC3: direct_inconsistency = False bases_had_inconsistency = False def _makeOne(self, c3=None, c3_ro=(), legacy_ro=()): from zope.interface.ro import _ROComparison return _ROComparison(c3 or self.MockC3(), c3_ro, legacy_ro) def test_inconsistent_label(self): comp = self._makeOne() self.assertEqual('no', comp._inconsistent_label) comp.c3.direct_inconsistency = True self.assertEqual("direct", comp._inconsistent_label) comp.c3.bases_had_inconsistency = True self.assertEqual("direct+bases", comp._inconsistent_label) comp.c3.direct_inconsistency = False self.assertEqual('bases', comp._inconsistent_label)