# -*- coding: utf-8 -*- # Copyright 2017 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) import mock from odoo.addons.component.core import AbstractComponent, Component from .common import ComponentRegistryCase class TestBuildComponent(ComponentRegistryCase): """ Test build of components All the tests in this suite are based on the same principle with variations: * Create new Components (classes inheriting from :class:`component.core.Component` or :class:`component.core.AbstractComponent` * Call :meth:`component.core.Component._build_component` on them in order to build the 'final class' composed from all the ``_inherit`` and push it in the components registry (``self.comp_registry`` here) * Assert that classes are built, registered, have correct ``__bases__``... """ def test_no_name(self): """ Ensure that a component has a _name """ class Component1(Component): pass msg = '.*must have a _name.*' with self.assertRaisesRegex(TypeError, msg): Component1._build_component(self.comp_registry) def test_register(self): """ Able to register components in components registry """ class Component1(Component): _name = 'component1' class Component2(Component): _name = 'component2' # build the 'final classes' for the components and check that we find # them in the components registry Component1._build_component(self.comp_registry) Component2._build_component(self.comp_registry) self.assertEqual( ['base', 'component1', 'component2'], list(self.comp_registry) ) def test_inherit_bases(self): """ Check __bases__ of Component with _inherit """ class Component1(Component): _name = 'component1' class Component2(Component): _inherit = 'component1' class Component3(Component): _inherit = 'component1' Component1._build_component(self.comp_registry) Component2._build_component(self.comp_registry) Component3._build_component(self.comp_registry) self.assertEqual( (Component3, Component2, Component1, self.comp_registry['base']), self.comp_registry['component1'].__bases__ ) def test_prototype_inherit_bases(self): """ Check __bases__ of Component with _inherit and different _name """ class Component1(Component): _name = 'component1' class Component2(Component): _name = 'component2' _inherit = 'component1' class Component3(Component): _name = 'component3' _inherit = 'component1' class Component4(Component): _name = 'component4' _inherit = ['component2', 'component3'] Component1._build_component(self.comp_registry) Component2._build_component(self.comp_registry) Component3._build_component(self.comp_registry) Component4._build_component(self.comp_registry) self.assertEqual( (Component1, self.comp_registry['base']), self.comp_registry['component1'].__bases__ ) self.assertEqual( (Component2, self.comp_registry['component1'], self.comp_registry['base']), self.comp_registry['component2'].__bases__ ) self.assertEqual( (Component3, self.comp_registry['component1'], self.comp_registry['base']), self.comp_registry['component3'].__bases__ ) self.assertEqual( (Component4, self.comp_registry['component2'], self.comp_registry['component3'], self.comp_registry['base']), self.comp_registry['component4'].__bases__ ) def test_custom_build(self): """ Check that we can hook at the end of a Component build """ class Component1(Component): _name = 'component1' @classmethod def _complete_component_build(cls): # This method should be called after the Component # is built, and before it is pushed in the registry cls._build_done = True Component1._build_component(self.comp_registry) # we inspect that our custom build has been executed self.assertTrue( self.comp_registry['component1']._build_done ) def test_inherit_attrs(self): """ Check attributes inheritance of Components with _inherit """ class Component1(Component): _name = 'component1' msg = 'ping' def say(self): return 'foo' class Component2(Component): _name = 'component2' _inherit = 'component1' msg = 'pong' def say(self): return super(Component2, self).say() + ' bar' Component1._build_component(self.comp_registry) Component2._build_component(self.comp_registry) # we initialize the components, normally we should pass # an instance of WorkContext, but we don't need a real one # for this test component1 = self.comp_registry['component1'](mock.Mock()) component2 = self.comp_registry['component2'](mock.Mock()) self.assertEqual('ping', component1.msg) self.assertEqual('pong', component2.msg) self.assertEqual('foo', component1.say()) self.assertEqual('foo bar', component2.say()) def test_duplicate_component(self): """ Check that we can't have 2 components with the same name """ class Component1(Component): _name = 'component1' class Component2(Component): _name = 'component1' Component1._build_component(self.comp_registry) msg = 'Component.*already exists.*' with self.assertRaisesRegex(TypeError, msg): Component2._build_component(self.comp_registry) def test_no_parent(self): """ Ensure we can't _inherit a non-existent component """ class Component1(Component): _name = 'component1' _inherit = 'component1' msg = 'Component.*does not exist in registry.*' with self.assertRaisesRegex(TypeError, msg): Component1._build_component(self.comp_registry) def test_no_parent2(self): """ Ensure we can't _inherit by prototype a non-existent component """ class Component1(Component): _name = 'component1' class Component2(Component): _name = 'component2' _inherit = ['component1', 'component3'] Component1._build_component(self.comp_registry) msg = 'Component.*inherits from non-existing component.*' with self.assertRaisesRegex(TypeError, msg): Component2._build_component(self.comp_registry) def test_add_inheritance(self): """ Ensure we can add a new inheritance """ class Component1(Component): _name = 'component1' class Component2(Component): _name = 'component2' class Component2bis(Component): _name = 'component2' _inherit = ['component2', 'component1'] Component1._build_component(self.comp_registry) Component2._build_component(self.comp_registry) Component2bis._build_component(self.comp_registry) self.assertEqual( (Component2bis, Component2, self.comp_registry['component1'], self.comp_registry['base']), self.comp_registry['component2'].__bases__ ) def test_check_parent_component_over_abstract(self): """ Component can inherit from AbstractComponent """ class Component1(AbstractComponent): _name = 'component1' class Component2(Component): _name = 'component2' _inherit = 'component1' Component1._build_component(self.comp_registry) Component2._build_component(self.comp_registry) self.assertTrue( self.comp_registry['component1']._abstract ) self.assertFalse( self.comp_registry['component2']._abstract ) def test_check_parent_abstract_over_component(self): """ Prevent AbstractComponent to inherit from Component """ class Component1(Component): _name = 'component1' class Component2(AbstractComponent): _name = 'component2' _inherit = 'component1' Component1._build_component(self.comp_registry) msg = '.*cannot inherit from the non-abstract.*' with self.assertRaisesRegex(TypeError, msg): Component2._build_component(self.comp_registry) def test_check_transform_abstract_to_component(self): """ Prevent AbstractComponent to be transformed to Component """ class Component1(AbstractComponent): _name = 'component1' class Component1bis(Component): _inherit = 'component1' Component1._build_component(self.comp_registry) msg = '.*transforms the abstract component.*into a non-abstract.*' with self.assertRaisesRegex(TypeError, msg): Component1bis._build_component(self.comp_registry)