Initial commit: OCA Technical packages (595 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:03 +02:00
commit 2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions

View file

@ -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

View 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"]

View file

@ -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)

View file

@ -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)

View file

@ -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])

View file

@ -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)

View file

@ -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)