208 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			208 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
| # -*- coding: utf-8 -*-
 | |
| # Copyright 2017 Camptocamp SA
 | |
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
 | |
| 
 | |
| import copy
 | |
| 
 | |
| from contextlib import contextmanager
 | |
| 
 | |
| import unittest
 | |
| 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(object):
 | |
| 
 | |
|     @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
 | |
|             # chilren 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)
 | |
| 
 | |
|     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(TransactionComponentCase, cls).setUpClass()
 | |
|         cls.setUpComponent()
 | |
| 
 | |
|     def setUp(self):
 | |
|         # resolve an inheritance issue (common.TransactionCase does not call
 | |
|         # super)
 | |
|         common.TransactionCase.setUp(self)
 | |
|         ComponentMixin.setUp(self)
 | |
| 
 | |
| 
 | |
| class SavepointComponentCase(common.SavepointCase, ComponentMixin):
 | |
|     """ A SavepointCase that loads all the components
 | |
| 
 | |
|     It it used like an usual Odoo's SavepointCase, but it ensures
 | |
|     that all the components of the current addon and its dependencies
 | |
|     are loaded.
 | |
| 
 | |
|     """
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super(SavepointComponentCase, cls).setUpClass()
 | |
|         cls.setUpComponent()
 | |
| 
 | |
|     def setUp(self):
 | |
|         # resolve an inheritance issue (common.SavepointCase does not call
 | |
|         # super)
 | |
|         common.SavepointCase.setUp(self)
 | |
|         ComponentMixin.setUp(self)
 | |
| 
 | |
| 
 | |
| class ComponentRegistryCase(unittest.TestCase):
 | |
|     """ 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 one of:
 | |
| 
 | |
|     * :class:`TransactionComponentCase`
 | |
|     * :class:`SavepointComponentCase`
 | |
| 
 | |
|     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:
 | |
| 
 | |
|     """
 | |
| 
 | |
|     def setUp(self):
 | |
|         super(ComponentRegistryCase, self).setUp()
 | |
| 
 | |
|         # 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
 | |
|         self._original_components = copy.deepcopy(
 | |
|             MetaComponent._modules_components
 | |
|         )
 | |
| 
 | |
|         # it will be our temporary component registry for our test session
 | |
|         self.comp_registry = ComponentRegistry()
 | |
| 
 | |
|         # it builds the 'final component' for every component of the
 | |
|         # 'component' addon and push them in the component registry
 | |
|         self.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(self.__module__)
 | |
|         with new_rollbacked_env() as env:
 | |
|             env['component.builder'].build_registry(
 | |
|                 self.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.
 | |
|         self.comp_registry.ready = True
 | |
| 
 | |
|     def tearDown(self):
 | |
|         super(ComponentRegistryCase, self).tearDown()
 | |
|         # restore the original metaclass' classes
 | |
|         MetaComponent._modules_components = self._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 """
 | |
| 
 | |
|     def setUp(self):
 | |
|         # resolve an inheritance issue (common.TransactionCase does not use
 | |
|         # super)
 | |
|         common.TransactionCase.setUp(self)
 | |
|         ComponentRegistryCase.setUp(self)
 | |
|         self.collection = self.env['collection.base']
 | |
| 
 | |
|     def teardown(self):
 | |
|         common.TransactionCase.tearDown(self)
 | |
|         ComponentRegistryCase.tearDown(self)
 |