mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-19 02:32:04 +02:00
Initial commit: OCA Technical packages (595 packages)
This commit is contained in:
commit
2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions
|
|
@ -0,0 +1,5 @@
|
|||
from . import test_build_component
|
||||
from . import test_component
|
||||
from . import test_lookup
|
||||
from . import test_work_on
|
||||
from . import test_utils
|
||||
212
odoo-bringout-oca-connector-component/component/tests/common.py
Normal file
212
odoo-bringout-oca-connector-component/component/tests/common.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# Copyright 2017 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
import copy
|
||||
from contextlib import contextmanager
|
||||
|
||||
import odoo
|
||||
from odoo import api
|
||||
from odoo.tests import common
|
||||
|
||||
from odoo.addons.component.core import ComponentRegistry, MetaComponent, _get_addon_name
|
||||
|
||||
|
||||
@contextmanager
|
||||
def new_rollbacked_env():
|
||||
registry = odoo.registry(common.get_db_name())
|
||||
uid = odoo.SUPERUSER_ID
|
||||
cr = registry.cursor()
|
||||
try:
|
||||
yield api.Environment(cr, uid, {})
|
||||
finally:
|
||||
cr.rollback() # we shouldn't have to commit anything
|
||||
cr.close()
|
||||
|
||||
|
||||
class ComponentMixin:
|
||||
@classmethod
|
||||
def setUpComponent(cls):
|
||||
with new_rollbacked_env() as env:
|
||||
builder = env["component.builder"]
|
||||
# build the components of every installed addons
|
||||
comp_registry = builder._init_global_registry()
|
||||
cls._components_registry = comp_registry
|
||||
# ensure that we load only the components of the 'installed'
|
||||
# modules, not 'to install', which means we load only the
|
||||
# dependencies of the tested addons, not the siblings or
|
||||
# children addons
|
||||
builder.build_registry(comp_registry, states=("installed",))
|
||||
# build the components of the current tested addon
|
||||
current_addon = _get_addon_name(cls.__module__)
|
||||
env["component.builder"].load_components(current_addon)
|
||||
if hasattr(cls, "env"):
|
||||
cls.env.context = dict(
|
||||
cls.env.context, components_registry=cls._components_registry
|
||||
)
|
||||
|
||||
# pylint: disable=W8106
|
||||
def setUp(self):
|
||||
# should be ready only during tests, never during installation
|
||||
# of addons
|
||||
self._components_registry.ready = True
|
||||
|
||||
@self.addCleanup
|
||||
def notready():
|
||||
self._components_registry.ready = False
|
||||
|
||||
|
||||
class TransactionComponentCase(common.TransactionCase, ComponentMixin):
|
||||
"""A TransactionCase that loads all the components
|
||||
|
||||
It it used like an usual Odoo's TransactionCase, but it ensures
|
||||
that all the components of the current addon and its dependencies
|
||||
are loaded.
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.setUpComponent()
|
||||
|
||||
# pylint: disable=W8106
|
||||
def setUp(self):
|
||||
# resolve an inheritance issue (common.TransactionCase does not call
|
||||
# super)
|
||||
common.TransactionCase.setUp(self)
|
||||
ComponentMixin.setUp(self)
|
||||
# There's no env on setUpClass of TransactionCase, must do it here.
|
||||
self.env.context = dict(
|
||||
self.env.context, components_registry=self._components_registry
|
||||
)
|
||||
|
||||
|
||||
class ComponentRegistryCase:
|
||||
"""This test case can be used as a base for writings tests on components
|
||||
|
||||
This test case is meant to test components in a special component registry,
|
||||
where you want to have maximum control on which components are loaded
|
||||
or not, or when you want to create additional components in your tests.
|
||||
|
||||
If you only want to *use* the components of the tested addon in your tests,
|
||||
then consider using:
|
||||
|
||||
* :class:`TransactionComponentCase`
|
||||
|
||||
This test case creates a special
|
||||
:class:`odoo.addons.component.core.ComponentRegistry` for the purpose of
|
||||
the tests. By default, it loads all the components of the dependencies, but
|
||||
not the components of the current addon (which you have to handle
|
||||
manually). In your tests, you can add more components in 2 manners.
|
||||
|
||||
All the components of an Odoo module::
|
||||
|
||||
self._load_module_components('connector')
|
||||
|
||||
Only specific components::
|
||||
|
||||
self._build_components(MyComponent1, MyComponent2)
|
||||
|
||||
Note: for the lookups of the components, the default component
|
||||
registry is a global registry for the database. Here, you will
|
||||
need to explicitly pass ``self.comp_registry`` in the
|
||||
:class:`~odoo.addons.component.core.WorkContext`::
|
||||
|
||||
work = WorkContext(model_name='res.users',
|
||||
collection='my.collection',
|
||||
components_registry=self.comp_registry)
|
||||
|
||||
Or::
|
||||
|
||||
collection_record = self.env['my.collection'].browse(1)
|
||||
with collection_record.work_on(
|
||||
'res.partner',
|
||||
components_registry=self.comp_registry) as work:
|
||||
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _setup_registry(class_or_instance):
|
||||
# keep the original classes registered by the metaclass
|
||||
# so we'll restore them at the end of the tests, it avoid
|
||||
# to pollute it with Stub / Test components
|
||||
class_or_instance._original_components = copy.deepcopy(
|
||||
MetaComponent._modules_components
|
||||
)
|
||||
|
||||
# it will be our temporary component registry for our test session
|
||||
class_or_instance.comp_registry = ComponentRegistry()
|
||||
|
||||
# it builds the 'final component' for every component of the
|
||||
# 'component' addon and push them in the component registry
|
||||
class_or_instance.comp_registry.load_components("component")
|
||||
# build the components of every installed addons already installed
|
||||
# but the current addon (when running with pytest/nosetest, we
|
||||
# simulate the --test-enable behavior by excluding the current addon
|
||||
# which is in 'to install' / 'to upgrade' with --test-enable).
|
||||
current_addon = _get_addon_name(class_or_instance.__module__)
|
||||
with new_rollbacked_env() as env:
|
||||
env["component.builder"].build_registry(
|
||||
class_or_instance.comp_registry,
|
||||
states=("installed",),
|
||||
exclude_addons=[current_addon],
|
||||
)
|
||||
|
||||
# Fake that we are ready to work with the registry
|
||||
# normally, it is set to True and the end of the build
|
||||
# of the components. Here, we'll add components later in
|
||||
# the components registry, but we don't mind for the tests.
|
||||
class_or_instance.comp_registry.ready = True
|
||||
if hasattr(class_or_instance, "env"):
|
||||
# let it propagate via ctx
|
||||
class_or_instance.env.context = dict(
|
||||
class_or_instance.env.context,
|
||||
components_registry=class_or_instance.comp_registry,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _teardown_registry(class_or_instance):
|
||||
# restore the original metaclass' classes
|
||||
MetaComponent._modules_components = class_or_instance._original_components
|
||||
|
||||
def _load_module_components(self, module):
|
||||
self.comp_registry.load_components(module)
|
||||
|
||||
def _build_components(self, *classes):
|
||||
for cls in classes:
|
||||
cls._build_component(self.comp_registry)
|
||||
|
||||
|
||||
class TransactionComponentRegistryCase(common.TransactionCase, ComponentRegistryCase):
|
||||
"""Adds Odoo Transaction in the base Component TestCase.
|
||||
|
||||
This class doesn't set up the registry for you.
|
||||
You're supposed to explicitly call `_setup_registry` and `_teardown_registry`
|
||||
when you need it, either on setUpClass and tearDownClass or setUp and tearDown.
|
||||
|
||||
class MyTestCase(TransactionComponentRegistryCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
|
||||
def tearDown(self):
|
||||
self._teardown_registry(self)
|
||||
super().tearDown()
|
||||
|
||||
class MyTestCase(TransactionComponentRegistryCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls._setup_registry(cls)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls._teardown_registry(cls)
|
||||
super().tearDownClass()
|
||||
"""
|
||||
|
||||
# pylint: disable=W8106
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.collection = cls.env["collection.base"]
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
# Copyright 2017 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
# Tell pylint to not bother us for all our fake component classes
|
||||
# pylint: disable=consider-merging-classes-inherited
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from odoo.addons.component.core import AbstractComponent, Component
|
||||
|
||||
from .common import TransactionComponentRegistryCase
|
||||
|
||||
|
||||
class TestBuildComponent(TransactionComponentRegistryCase):
|
||||
"""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 setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
|
||||
def tearDown(self):
|
||||
self._teardown_registry(self)
|
||||
super().tearDown()
|
||||
|
||||
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__,
|
||||
)
|
||||
|
||||
# pylint: disable=W8110
|
||||
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().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)
|
||||
|
|
@ -0,0 +1,388 @@
|
|||
# Copyright 2017 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
from odoo.addons.component.core import Component
|
||||
from odoo.addons.component.exception import NoComponentError, SeveralComponentError
|
||||
|
||||
from .common import TransactionComponentRegistryCase
|
||||
|
||||
|
||||
class TestComponent(TransactionComponentRegistryCase):
|
||||
"""Test usage of components
|
||||
|
||||
These tests are a bit more broad that mere unit tests.
|
||||
We test the chain odoo Model -> generate a WorkContext instance -> Work
|
||||
with Component.
|
||||
|
||||
Tests are inside Odoo transactions, so we can work
|
||||
with Odoo's env / models.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
self._setUpComponents()
|
||||
|
||||
def tearDown(self):
|
||||
self._teardown_registry(self)
|
||||
super().tearDown()
|
||||
|
||||
def _setUpComponents(self):
|
||||
# create some Component to play with
|
||||
class Component1(Component):
|
||||
_name = "component1"
|
||||
_collection = "collection.base"
|
||||
_usage = "for.test"
|
||||
_apply_on = ["res.partner"]
|
||||
|
||||
class Component2(Component):
|
||||
_name = "component2"
|
||||
_collection = "collection.base"
|
||||
_usage = "for.test"
|
||||
_apply_on = ["res.users"]
|
||||
|
||||
# build the components and register them in our
|
||||
# test component registry
|
||||
Component1._build_component(self.comp_registry)
|
||||
Component2._build_component(self.comp_registry)
|
||||
|
||||
# our collection, in a less abstract use case, it
|
||||
# could be a record of 'magento.backend' for instance
|
||||
self.collection_record = self.collection.new()
|
||||
|
||||
@contextmanager
|
||||
def get_base():
|
||||
# Our WorkContext, it will be passed along in every
|
||||
# components so we can share data transversally.
|
||||
# We are working with res.partner in the following tests,
|
||||
# unless we change it in the test.
|
||||
with self.collection_record.work_on(
|
||||
"res.partner",
|
||||
# we use a custom registry only
|
||||
# for the sake of the tests
|
||||
components_registry=self.comp_registry,
|
||||
) as work:
|
||||
# We get the 'base' component, handy to test the base
|
||||
# methods component, many_components, ...
|
||||
yield work.component_by_name("base")
|
||||
|
||||
self.get_base = get_base
|
||||
|
||||
def test_component_attrs(self):
|
||||
"""Basic access to a Component's attribute"""
|
||||
with self.get_base() as base:
|
||||
# as we are working on res.partner, we should get 'component1'
|
||||
comp = base.work.component(usage="for.test")
|
||||
# but this is not what we test here, we test the attributes:
|
||||
self.assertEqual(self.collection_record, comp.collection)
|
||||
self.assertEqual(base.work, comp.work)
|
||||
self.assertEqual(self.env, comp.env)
|
||||
self.assertEqual(self.env["res.partner"], comp.model)
|
||||
|
||||
def test_component_get_by_name_same_model(self):
|
||||
"""Use component_by_name with current working model"""
|
||||
with self.get_base() as base:
|
||||
# we ask a component directly by it's name, considering
|
||||
# we work with res.partner, we should get 'component1'
|
||||
# this is ok because it's _apply_on contains res.partner
|
||||
comp = base.component_by_name("component1")
|
||||
self.assertEqual("component1", comp._name)
|
||||
self.assertEqual(self.env["res.partner"], comp.model)
|
||||
|
||||
def test_component_get_by_name_other_model(self):
|
||||
"""Use component_by_name with another model"""
|
||||
with self.get_base() as base:
|
||||
# we ask a component directly by it's name, but we
|
||||
# want to work with 'res.users', this is ok since
|
||||
# component2's _apply_on contains res.users
|
||||
comp = base.component_by_name("component2", model_name="res.users")
|
||||
self.assertEqual("component2", comp._name)
|
||||
self.assertEqual(self.env["res.users"], comp.model)
|
||||
# what happens under the hood, is that a new WorkContext
|
||||
# has been created for this model, with all the other values
|
||||
# identical to the previous WorkContext (the one for res.partner)
|
||||
# We can check that with:
|
||||
self.assertNotEqual(base.work, comp.work)
|
||||
self.assertEqual("res.partner", base.work.model_name)
|
||||
self.assertEqual("res.users", comp.work.model_name)
|
||||
|
||||
def test_component_get_by_name_wrong_model(self):
|
||||
"""Use component_by_name with a model not in _apply_on"""
|
||||
msg = (
|
||||
"Component with name 'component2' can't be used "
|
||||
"for model 'res.partner'.*"
|
||||
)
|
||||
with self.get_base() as base:
|
||||
with self.assertRaisesRegex(NoComponentError, msg):
|
||||
# we ask for the model 'component2' but we are working
|
||||
# with res.partner, and it only accepts res.users
|
||||
base.component_by_name("component2")
|
||||
|
||||
def test_component_get_by_name_not_exist(self):
|
||||
"""Use component_by_name on a component that do not exist"""
|
||||
msg = "No component with name 'foo' found."
|
||||
with self.get_base() as base:
|
||||
with self.assertRaisesRegex(NoComponentError, msg):
|
||||
base.component_by_name("foo")
|
||||
|
||||
def test_component_by_usage_same_model(self):
|
||||
"""Use component(usage=...) on the same model"""
|
||||
# we ask for a component having _usage == 'for.test', and
|
||||
# model being res.partner (the model in the current WorkContext)
|
||||
with self.get_base() as base:
|
||||
comp = base.component(usage="for.test")
|
||||
self.assertEqual("component1", comp._name)
|
||||
self.assertEqual(self.env["res.partner"], comp.model)
|
||||
|
||||
def test_component_by_usage_other_model(self):
|
||||
"""Use component(usage=...) on a different model (name)"""
|
||||
# we ask for a component having _usage == 'for.test', and
|
||||
# a different model (res.users)
|
||||
with self.get_base() as base:
|
||||
comp = base.component(usage="for.test", model_name="res.users")
|
||||
self.assertEqual("component2", comp._name)
|
||||
self.assertEqual(self.env["res.users"], comp.model)
|
||||
# what happens under the hood, is that a new WorkContext
|
||||
# has been created for this model, with all the other values
|
||||
# identical to the previous WorkContext (the one for res.partner)
|
||||
# We can check that with:
|
||||
self.assertNotEqual(base.work, comp.work)
|
||||
self.assertEqual("res.partner", base.work.model_name)
|
||||
self.assertEqual("res.users", comp.work.model_name)
|
||||
|
||||
def test_component_by_usage_other_model_env(self):
|
||||
"""Use component(usage=...) on a different model (instance)"""
|
||||
with self.get_base() as base:
|
||||
comp = base.component(usage="for.test", model_name=self.env["res.users"])
|
||||
self.assertEqual("component2", comp._name)
|
||||
self.assertEqual(self.env["res.users"], comp.model)
|
||||
|
||||
def test_component_error_several(self):
|
||||
"""Use component(usage=...) when more than one generic component match"""
|
||||
# we create 1 new Component with _usage 'for.test', in the same
|
||||
# collection and no _apply_on, and we remove the _apply_on of component
|
||||
# 1 so they are generic components for a collection
|
||||
class Component3(Component):
|
||||
_name = "component3"
|
||||
_collection = "collection.base"
|
||||
_usage = "for.test"
|
||||
|
||||
class Component1(Component):
|
||||
_inherit = "component1"
|
||||
_collection = "collection.base"
|
||||
_usage = "for.test"
|
||||
_apply_on = None
|
||||
|
||||
Component3._build_component(self.comp_registry)
|
||||
Component1._build_component(self.comp_registry)
|
||||
|
||||
with self.get_base() as base:
|
||||
with self.assertRaises(SeveralComponentError):
|
||||
# When a component has no _apply_on, it means it can be applied
|
||||
# on *any* model. Here, the candidates components would be:
|
||||
# component3 (because it has no _apply_on so apply in any case)
|
||||
# component4 (for the same reason)
|
||||
base.component(usage="for.test")
|
||||
|
||||
def test_component_error_several_same_model(self):
|
||||
"""Use component(usage=...) when more than one component match a model"""
|
||||
# we create a new Component with _usage 'for.test', in the same
|
||||
# collection and no _apply_on
|
||||
class Component3(Component):
|
||||
_name = "component3"
|
||||
_collection = "collection.base"
|
||||
_usage = "for.test"
|
||||
_apply_on = ["res.partner"]
|
||||
|
||||
Component3._build_component(self.comp_registry)
|
||||
|
||||
with self.get_base() as base:
|
||||
with self.assertRaises(SeveralComponentError):
|
||||
# Here, the candidates components would be:
|
||||
# component1 (because we are working with res.partner),
|
||||
# component3 (for the same reason)
|
||||
base.component(usage="for.test")
|
||||
|
||||
def test_component_specific_model(self):
|
||||
"""Use component(usage=...) when more than one component match but
|
||||
only one for the specific model"""
|
||||
# we create a new Component with _usage 'for.test', in the same
|
||||
# collection and no _apply_on. This is a generic component for the
|
||||
# collection
|
||||
class Component3(Component):
|
||||
_name = "component3"
|
||||
_collection = "collection.base"
|
||||
_usage = "for.test"
|
||||
|
||||
Component3._build_component(self.comp_registry)
|
||||
|
||||
with self.get_base() as base:
|
||||
# When a component has no _apply_on, it means it can be applied on
|
||||
# *any* model. Here, the candidates components would be:
|
||||
# component1 # (because we are working with res.partner),
|
||||
# component3 (because it # has no _apply_on so apply in any case).
|
||||
# When a component is specifically linked to a model with
|
||||
# _apply_on, it takes precedence over a generic component. It
|
||||
# allows to create a generic implementation (component3 here) and
|
||||
# override it only for a given model. So in this case, the final
|
||||
# component is component1.
|
||||
comp = base.component(usage="for.test")
|
||||
self.assertEqual("component1", comp._name)
|
||||
|
||||
def test_component_specific_collection(self):
|
||||
"""Use component(usage=...) when more than one component match but
|
||||
only one for the specific collection"""
|
||||
# we create a new Component with _usage 'for.test', without collection
|
||||
# and no _apply_on
|
||||
class Component3(Component):
|
||||
_name = "component3"
|
||||
_usage = "for.test"
|
||||
|
||||
Component3._build_component(self.comp_registry)
|
||||
|
||||
with self.get_base() as base:
|
||||
# When a component has no _apply_on, it means it can be applied
|
||||
# on *any* model. Here, the candidates components would be:
|
||||
# component1 (because we are working with res.partner),
|
||||
# component3 (because it has no _apply_on so apply in any case).
|
||||
# When a component has no _collection, it means it can be applied
|
||||
# on all model if no component is found for the current collection:
|
||||
# component3 must be ignored since a component (component1) exists
|
||||
# and is specificaly linked to the expected collection.
|
||||
comp = base.component(usage="for.test")
|
||||
self.assertEqual("component1", comp._name)
|
||||
|
||||
def test_component_specific_collection_specific_model(self):
|
||||
"""Use component(usage=...) when more than one component match but
|
||||
only one for the specific model and collection"""
|
||||
# we create a new Component with _usage 'for.test', without collection
|
||||
# and no _apply_on. This is a component generic for all collections and
|
||||
# models
|
||||
class Component3(Component):
|
||||
_name = "component3"
|
||||
_usage = "for.test"
|
||||
|
||||
Component3._build_component(self.comp_registry)
|
||||
|
||||
with self.get_base() as base:
|
||||
# When a component has no _apply_on, it means it can be applied on
|
||||
# *any* model, no _collection, it can be applied on *any*
|
||||
# collection.
|
||||
# Here, the candidates components would be:
|
||||
# component1 (because we are working with res.partner),
|
||||
# component3 (because it has no _apply_on and no _collection so
|
||||
# apply in any case).
|
||||
# When a component is specifically linked to a model with
|
||||
# _apply_on, it takes precedence over a generic component, the same
|
||||
# happens for collection. It allows to create a generic
|
||||
# implementation (component3 here) and override it only for a given
|
||||
# collection and model. So in this case, the final component is
|
||||
# component1.
|
||||
comp = base.component(usage="for.test")
|
||||
self.assertEqual("component1", comp._name)
|
||||
|
||||
def test_many_components(self):
|
||||
"""Use many_components(usage=...) on the same model"""
|
||||
|
||||
class Component3(Component):
|
||||
_name = "component3"
|
||||
_collection = "collection.base"
|
||||
_usage = "for.test"
|
||||
|
||||
Component3._build_component(self.comp_registry)
|
||||
|
||||
with self.get_base() as base:
|
||||
comps = base.many_components(usage="for.test")
|
||||
|
||||
# When a component has no _apply_on, it means it can be applied
|
||||
# on *any* model. So here, both component1 and component3 match
|
||||
self.assertEqual(["component1", "component3"], [c._name for c in comps])
|
||||
|
||||
def test_many_components_other_model(self):
|
||||
"""Use many_components(usage=...) on a different model (name)"""
|
||||
|
||||
class Component3(Component):
|
||||
_name = "component3"
|
||||
_collection = "collection.base"
|
||||
_apply_on = "res.users"
|
||||
_usage = "for.test"
|
||||
|
||||
Component3._build_component(self.comp_registry)
|
||||
|
||||
with self.get_base() as base:
|
||||
comps = base.many_components(usage="for.test", model_name="res.users")
|
||||
|
||||
self.assertEqual(["component2", "component3"], [c._name for c in comps])
|
||||
|
||||
def test_many_components_other_model_env(self):
|
||||
"""Use many_components(usage=...) on a different model (instance)"""
|
||||
|
||||
class Component3(Component):
|
||||
_name = "component3"
|
||||
_collection = "collection.base"
|
||||
_apply_on = "res.users"
|
||||
_usage = "for.test"
|
||||
|
||||
Component3._build_component(self.comp_registry)
|
||||
|
||||
with self.get_base() as base:
|
||||
comps = base.many_components(
|
||||
usage="for.test", model_name=self.env["res.users"]
|
||||
)
|
||||
|
||||
self.assertEqual(["component2", "component3"], [c._name for c in comps])
|
||||
|
||||
def test_no_component(self):
|
||||
"""No component found for asked usage"""
|
||||
with self.get_base() as base:
|
||||
with self.assertRaises(NoComponentError):
|
||||
base.component(usage="foo")
|
||||
|
||||
def test_no_many_component(self):
|
||||
"""No component found for asked usage for many_components()"""
|
||||
with self.get_base() as base:
|
||||
self.assertEqual([], base.many_components(usage="foo"))
|
||||
|
||||
def test_work_on_component(self):
|
||||
"""Check WorkContext.component() (shortcut to Component.component)"""
|
||||
with self.get_base() as base:
|
||||
comp = base.work.component(usage="for.test")
|
||||
self.assertEqual("component1", comp._name)
|
||||
|
||||
def test_work_on_many_components(self):
|
||||
"""Check WorkContext.many_components()
|
||||
|
||||
(shortcut to Component.many_components)
|
||||
"""
|
||||
with self.get_base() as base:
|
||||
comps = base.work.many_components(usage="for.test")
|
||||
self.assertEqual("component1", comps[0]._name)
|
||||
|
||||
def test_component_match(self):
|
||||
"""Lookup with match method"""
|
||||
|
||||
class Foo(Component):
|
||||
_name = "foo"
|
||||
_collection = "collection.base"
|
||||
_usage = "speaker"
|
||||
_apply_on = ["res.partner"]
|
||||
|
||||
@classmethod
|
||||
def _component_match(cls, work, **kw):
|
||||
return False
|
||||
|
||||
class Bar(Component):
|
||||
_name = "bar"
|
||||
_collection = "collection.base"
|
||||
_usage = "speaker"
|
||||
_apply_on = ["res.partner"]
|
||||
|
||||
self._build_components(Foo, Bar)
|
||||
|
||||
with self.get_base() as base:
|
||||
# both components would we returned without the
|
||||
# _component_match method
|
||||
comp = base.component(usage="speaker", model_name=self.env["res.partner"])
|
||||
self.assertEqual("bar", comp._name)
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
# Copyright 2017 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo.addons.component.core import AbstractComponent, Component
|
||||
|
||||
from .common import TransactionComponentRegistryCase
|
||||
|
||||
|
||||
class TestLookup(TransactionComponentRegistryCase):
|
||||
"""Test the ComponentRegistry
|
||||
|
||||
Tests in this testsuite mainly do:
|
||||
|
||||
* 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)
|
||||
* Use the lookup method of the components registry and check
|
||||
that we get the correct result
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
|
||||
def tearDown(self):
|
||||
self._teardown_registry(self)
|
||||
super().tearDown()
|
||||
|
||||
def test_lookup_collection(self):
|
||||
"""Lookup components of a collection"""
|
||||
# we register 2 components in foobar and one in other
|
||||
class Foo(Component):
|
||||
_name = "foo"
|
||||
_collection = "foobar"
|
||||
|
||||
class Bar(Component):
|
||||
_name = "bar"
|
||||
_collection = "foobar"
|
||||
|
||||
class Homer(Component):
|
||||
_name = "homer"
|
||||
_collection = "other"
|
||||
|
||||
self._build_components(Foo, Bar, Homer)
|
||||
|
||||
# we should no see the component in 'other'
|
||||
components = self.comp_registry.lookup("foobar")
|
||||
self.assertEqual(["foo", "bar"], [c._name for c in components])
|
||||
|
||||
def test_lookup_usage(self):
|
||||
"""Lookup components by usage"""
|
||||
|
||||
class Foo(Component):
|
||||
_name = "foo"
|
||||
_collection = "foobar"
|
||||
_usage = "speaker"
|
||||
|
||||
class Bar(Component):
|
||||
_name = "bar"
|
||||
_collection = "foobar"
|
||||
_usage = "speaker"
|
||||
|
||||
class Baz(Component):
|
||||
_name = "baz"
|
||||
_collection = "foobar"
|
||||
_usage = "listener"
|
||||
|
||||
self._build_components(Foo, Bar, Baz)
|
||||
|
||||
components = self.comp_registry.lookup("foobar", usage="listener")
|
||||
self.assertEqual("baz", components[0]._name)
|
||||
|
||||
components = self.comp_registry.lookup("foobar", usage="speaker")
|
||||
self.assertEqual(["foo", "bar"], [c._name for c in components])
|
||||
|
||||
def test_lookup_no_component(self):
|
||||
"""No component"""
|
||||
# we just expect an empty list when no component match, the error
|
||||
# handling is handled at an higher level
|
||||
self.assertEqual([], self.comp_registry.lookup("something", usage="something"))
|
||||
|
||||
def test_get_by_name(self):
|
||||
"""Get component by name"""
|
||||
|
||||
class Foo(AbstractComponent):
|
||||
_name = "foo"
|
||||
_collection = "foobar"
|
||||
|
||||
self._build_components(Foo)
|
||||
# this is just a dict access
|
||||
self.assertEqual("foo", self.comp_registry["foo"]._name)
|
||||
|
||||
def test_lookup_abstract(self):
|
||||
"""Do not include abstract components in lookup"""
|
||||
|
||||
class Foo(AbstractComponent):
|
||||
_name = "foo"
|
||||
_collection = "foobar"
|
||||
_usage = "speaker"
|
||||
|
||||
class Bar(Component):
|
||||
_name = "bar"
|
||||
_inherit = "foo"
|
||||
|
||||
self._build_components(Foo, Bar)
|
||||
|
||||
comp_registry = self.comp_registry
|
||||
|
||||
# we should never have 'foo' in the returned components
|
||||
# as it is abstract
|
||||
components = comp_registry.lookup("foobar", usage="speaker")
|
||||
self.assertEqual("bar", components[0]._name)
|
||||
|
||||
components = comp_registry.lookup("foobar", usage="speaker")
|
||||
self.assertEqual(["bar"], [c._name for c in components])
|
||||
|
||||
def test_lookup_model_name(self):
|
||||
"""Lookup with model names"""
|
||||
|
||||
class Foo(Component):
|
||||
_name = "foo"
|
||||
_collection = "foobar"
|
||||
_usage = "speaker"
|
||||
# support list
|
||||
_apply_on = ["res.partner"]
|
||||
|
||||
class Bar(Component):
|
||||
_name = "bar"
|
||||
_collection = "foobar"
|
||||
_usage = "speaker"
|
||||
# support string
|
||||
_apply_on = "res.users"
|
||||
|
||||
class Any(Component):
|
||||
# can be used with any model as far as we look it up
|
||||
# with its usage
|
||||
_name = "any"
|
||||
_collection = "foobar"
|
||||
_usage = "listener"
|
||||
|
||||
self._build_components(Foo, Bar, Any)
|
||||
|
||||
components = self.comp_registry.lookup(
|
||||
"foobar", usage="speaker", model_name="res.partner"
|
||||
)
|
||||
self.assertEqual("foo", components[0]._name)
|
||||
|
||||
components = self.comp_registry.lookup(
|
||||
"foobar", usage="speaker", model_name="res.users"
|
||||
)
|
||||
self.assertEqual("bar", components[0]._name)
|
||||
|
||||
components = self.comp_registry.lookup(
|
||||
"foobar", usage="listener", model_name="res.users"
|
||||
)
|
||||
self.assertEqual("any", components[0]._name)
|
||||
|
||||
def test_lookup_cache(self):
|
||||
"""Lookup uses a cache"""
|
||||
|
||||
class Foo(Component):
|
||||
_name = "foo"
|
||||
_collection = "foobar"
|
||||
|
||||
self._build_components(Foo)
|
||||
|
||||
components = self.comp_registry.lookup("foobar")
|
||||
self.assertEqual(["foo"], [c._name for c in components])
|
||||
|
||||
# we add a new component
|
||||
class Bar(Component):
|
||||
_name = "bar"
|
||||
_collection = "foobar"
|
||||
|
||||
self._build_components(Bar)
|
||||
|
||||
# As the lookups are cached, we should still see only foo,
|
||||
# even if we added a new component.
|
||||
# We do this for testing, but in a real use case, we can't
|
||||
# add new Component classes on the fly, and when we install
|
||||
# new addons, the registry is rebuilt and cache cleared.
|
||||
components = self.comp_registry.lookup("foobar")
|
||||
self.assertEqual(["foo"], [c._name for c in components])
|
||||
|
||||
self.comp_registry._cache.clear()
|
||||
# now we should find them both as the cache has been cleared
|
||||
components = self.comp_registry.lookup("foobar")
|
||||
self.assertEqual(["foo", "bar"], [c._name for c in components])
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Copyright 2023 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from odoo.addons.component.utils import is_component_registry_ready
|
||||
|
||||
from .common import TransactionComponentRegistryCase
|
||||
|
||||
|
||||
class TestUtils(TransactionComponentRegistryCase):
|
||||
def test_registry_ready(self):
|
||||
path = "odoo.addons.component.utils.get_component_registry"
|
||||
with mock.patch(path) as mocked:
|
||||
mocked.return_value = None
|
||||
self.assertFalse(is_component_registry_ready(self.env.cr.dbname))
|
||||
self._setup_registry(self)
|
||||
mocked.return_value = self.comp_registry
|
||||
self.assertTrue(is_component_registry_ready(self.env.cr.dbname))
|
||||
self._teardown_registry(self)
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
# Copyright 2017 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo.addons.component.core import ComponentRegistry, WorkContext
|
||||
|
||||
from .common import TransactionComponentRegistryCase
|
||||
|
||||
|
||||
class TestWorkOn(TransactionComponentRegistryCase):
|
||||
"""Test on WorkContext
|
||||
|
||||
This model is mostly a container, so we check the access
|
||||
to the attributes and properties.
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
|
||||
def tearDown(self):
|
||||
self._teardown_registry(self)
|
||||
super().tearDown()
|
||||
|
||||
def test_collection_work_on(self):
|
||||
"""Create a new instance and test attributes access"""
|
||||
collection_record = self.collection.new()
|
||||
with collection_record.work_on("res.partner") as work:
|
||||
self.assertEqual(collection_record, work.collection)
|
||||
self.assertEqual("collection.base", work.collection._name)
|
||||
self.assertEqual("res.partner", work.model_name)
|
||||
self.assertEqual(self.env["res.partner"], work.model)
|
||||
self.assertEqual(self.env, work.env)
|
||||
|
||||
def test_collection_work_on_registry_via_context(self):
|
||||
"""Test propagation of registry via context"""
|
||||
registry = ComponentRegistry()
|
||||
collection_record = self.collection.with_context(
|
||||
components_registry=registry
|
||||
).new()
|
||||
with collection_record.work_on("res.partner") as work:
|
||||
self.assertEqual(collection_record, work.collection)
|
||||
self.assertEqual("collection.base", work.collection._name)
|
||||
self.assertEqual("res.partner", work.model_name)
|
||||
self.assertEqual(self.env["res.partner"], work.model)
|
||||
self.assertEqual(work.env, collection_record.env)
|
||||
self.assertEqual(work.components_registry, registry)
|
||||
|
||||
def test_propagate_work_on(self):
|
||||
"""Check custom attributes and their propagation"""
|
||||
registry = ComponentRegistry()
|
||||
work = WorkContext(
|
||||
model_name="res.partner",
|
||||
collection=self.collection,
|
||||
# we can customize the lookup registry, but used mostly for tests
|
||||
components_registry=registry,
|
||||
# we can pass our own keyword args that will set as attributes
|
||||
test_keyword="value",
|
||||
)
|
||||
self.assertIs(registry, work.components_registry)
|
||||
# check that our custom keyword is set as attribute
|
||||
self.assertEqual("value", work.test_keyword)
|
||||
|
||||
# when we want to work on another model, work_on() create
|
||||
# another instance and propagate the attributes to it
|
||||
work2 = work.work_on("res.users")
|
||||
self.assertNotEqual(work, work2)
|
||||
self.assertEqual(self.env, work2.env)
|
||||
self.assertEqual(self.collection, work2.collection)
|
||||
self.assertEqual("res.users", work2.model_name)
|
||||
self.assertIs(registry, work2.components_registry)
|
||||
# test_keyword has been propagated to the new WorkContext instance
|
||||
self.assertEqual("value", work2.test_keyword)
|
||||
Loading…
Add table
Add a link
Reference in a new issue