From 632311195013fbbded97a9c41202b63f70a3482a Mon Sep 17 00:00:00 2001 From: Vo Minh Thu Date: Thu, 1 Mar 2012 14:46:08 +0100 Subject: [PATCH] [IMP] tests: - moved a few YAML tests to unittest2 for demonstration purpose - changed --test-disable to --test-enable (and swapped its meaning) bzr revid: vmt@openerp.com-20120301134608-szuktuj8imdhmn0r --- FEATURES.rst | 5 +- openerp/addons/base/__openerp__.py | 1 - openerp/addons/base/test/test_ir_values.yml | 87 ----------------- .../addons/base/test/test_osv_expression.yml | 10 -- openerp/addons/base/tests/__init__.py | 5 + openerp/addons/base/tests/test_ir_values.py | 95 +++++++++++++++++++ openerp/modules/loading.py | 38 ++++---- openerp/modules/module.py | 11 ++- openerp/tests/__init__.py | 4 +- openerp/tests/common.py | 9 +- openerp/tests/test_expression.py | 12 +++ openerp/tools/config.py | 6 +- 12 files changed, 151 insertions(+), 132 deletions(-) delete mode 100644 openerp/addons/base/test/test_ir_values.yml create mode 100644 openerp/addons/base/tests/__init__.py create mode 100644 openerp/addons/base/tests/test_ir_values.py create mode 100644 openerp/tests/test_expression.py diff --git a/FEATURES.rst b/FEATURES.rst index ccbc7d2de7a..0b950db26ed 100644 --- a/FEATURES.rst +++ b/FEATURES.rst @@ -49,8 +49,9 @@ suite. If it really needs that the module it belongs to is freshly installed, add it to `fast_suite`. Finally, if it can not be run in an acceptable time frame, don't add it to any explicit list. -The tests must developed under `.tests` (or `openerp.tests` for the core). -For instance, with respect to the tests, a module `foo` should be organized as follow:: +The tests must be developed under `.tests` (or `openerp.tests` for +the core). For instance, with respect to the tests, a module `foo` should be +organized as follow:: foo/ __init__.py # does not import .tests diff --git a/openerp/addons/base/__openerp__.py b/openerp/addons/base/__openerp__.py index 4e9687e4ea4..802a0510e28 100644 --- a/openerp/addons/base/__openerp__.py +++ b/openerp/addons/base/__openerp__.py @@ -91,7 +91,6 @@ 'test/bug_lp541545.xml', 'test/test_osv_expression.yml', 'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules. - 'test/test_ir_values.yml', # Commented because this takes some time. # This must be (un)commented with the corresponding import statement # in test/__init__.py. diff --git a/openerp/addons/base/test/test_ir_values.yml b/openerp/addons/base/test/test_ir_values.yml deleted file mode 100644 index c26e7257afd..00000000000 --- a/openerp/addons/base/test/test_ir_values.yml +++ /dev/null @@ -1,87 +0,0 @@ -- - Create some default value for some (non-existing) model, for all users. -- - !python {model: ir.values }: | - # use the old API - self.set(cr, uid, 'default', False, 'my_test_field',['unexisting_model'], 'global value') - # use the new API - self.set_default(cr, uid, 'other_unexisting_model', 'my_other_test_field', 'conditional value', condition='foo=bar') -- - Retrieve them. -- - !python {model: ir.values }: | - # d is a list of triplets (id, name, value) - # Old API - d = self.get(cr, uid, 'default', False, ['unexisting_model']) - assert len(d) == 1, "Only one single value should be retrieved for this model" - assert d[0][1] == 'my_test_field', "Can't retrieve the created default value. (1)" - assert d[0][2] == 'global value', "Can't retrieve the created default value. (2)" - - # New API, Conditional version - d = self.get_defaults(cr, uid, 'other_unexisting_model') - assert len(d) == 0, "No value should be retrieved, the condition is not met" - d = self.get_defaults(cr, uid, 'other_unexisting_model', condition="foo=eggs") - assert len(d) == 0, 'Condition is not met either, no defaults should be returned' - d = self.get_defaults(cr, uid, 'other_unexisting_model', condition="foo=bar") - assert len(d) == 1, "Only one single value should be retrieved" - assert d[0][1] == 'my_other_test_field', "Can't retrieve the created default value. (5)" - assert d[0][2] == 'conditional value', "Can't retrieve the created default value. (6)" -- - Do it again but for a specific user. -- - !python {model: ir.values }: | - self.set(cr, uid, 'default', False, 'my_test_field',['unexisting_model'], 'specific value', preserve_user=True) -- - Retrieve it and check it is the one for the current user. -- - !python {model: ir.values }: | - d = self.get(cr, uid, 'default', False, ['unexisting_model']) - assert len(d) == 1, "Only one default must be returned per field" - assert d[0][1] == 'my_test_field', "Can't retrieve the created default value." - assert d[0][2] == 'specific value', "Can't retrieve the created default value." -- - Create some action bindings for a non-existing model -- - !python {model: ir.values }: | - self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True) - self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,11', isobject=True) - self.set(cr, uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,12', isobject=True) - self.set(cr, uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,2', isobject=True) - self.set(cr, uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,14', isobject=True) -- - Replace one action binding to set a new name -- - !python {model: ir.values }: | - self.set(cr, uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True) -- - Retrieve the action bindings and check they're correct -- - !python {model: ir.values }: | - actions = self.get(cr, uid, 'action', 'tree_but_open', ['unexisting_model']) - assert len(actions) == 2, "Mismatching number of bound actions" - #first action - assert len(actions[0]) == 3, "Malformed action definition" - assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition' - assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 11, 'Bound action does not match definition' - #second action - this ones comes last because it was re-created with a different name - assert len(actions[1]) == 3, "Malformed action definition" - assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it' - assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == 10, 'Bound action does not match definition' - - actions = self.get(cr, uid, 'action', 'client_action_multi', ['unexisting_model']) - assert len(actions) == 1, "Mismatching number of bound actions" - assert len(actions[0]) == 3, "Malformed action definition" - assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition' - assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 12, 'Bound action does not match definition' - - actions = self.get(cr, uid, 'action', 'client_print_multi', ['unexisting_model']) - assert len(actions) == 1, "Mismatching number of bound actions" - assert len(actions[0]) == 3, "Malformed action definition" - assert actions[0][1] == 'Nice Report', 'Bound action does not match definition' - assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 2, 'Bound action does not match definition' - - actions = self.get(cr, uid, 'action', 'client_action_relate', ['unexisting_model']) - assert len(actions) == 1, "Mismatching number of bound actions" - assert len(actions[0]) == 3, "Malformed action definition" - assert actions[0][1] == 'Related Stuff', 'Bound action does not match definition' - assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 14, 'Bound action does not match definition' diff --git a/openerp/addons/base/test/test_osv_expression.yml b/openerp/addons/base/test/test_osv_expression.yml index d9a1b215000..618108ef316 100644 --- a/openerp/addons/base/test/test_osv_expression.yml +++ b/openerp/addons/base/test/test_osv_expression.yml @@ -440,16 +440,6 @@ assert res_2 == expected assert res_3 == expected assert res_4 == expected -- - Verify that normalize_domain() works. -- - !python {model: res.partner}: | - from osv import expression - norm_domain = domain = ['&',(1,'=',1),('a','=','b')] - assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched" - domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')] - norm_domain = ['&','&','&'] + domain - assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized" - Unaccent. Create a company with an accent in its name. - diff --git a/openerp/addons/base/tests/__init__.py b/openerp/addons/base/tests/__init__.py new file mode 100644 index 00000000000..5ea7654c008 --- /dev/null +++ b/openerp/addons/base/tests/__init__.py @@ -0,0 +1,5 @@ +import test_ir_values + +checks = [ + test_ir_values, +] diff --git a/openerp/addons/base/tests/test_ir_values.py b/openerp/addons/base/tests/test_ir_values.py new file mode 100644 index 00000000000..e2871a0d199 --- /dev/null +++ b/openerp/addons/base/tests/test_ir_values.py @@ -0,0 +1,95 @@ +import unittest2 + +import openerp.tests.common as common + +class test_ir_values(common.TransactionCase): + + def test_00(self): + # Create some default value for some (non-existing) model, for all users. + + ir_values = self.registry('ir.values') + # use the old API + ir_values.set(self.cr, self.uid, 'default', False, 'my_test_field', + ['unexisting_model'], 'global value') + # use the new API + ir_values.set_default(self.cr, self.uid, 'other_unexisting_model', + 'my_other_test_field', 'conditional value', condition='foo=bar') + + + # Retrieve them. + + ir_values = self.registry('ir.values') + # d is a list of triplets (id, name, value) + # Old API + d = ir_values.get(self.cr, self.uid, 'default', False, ['unexisting_model']) + assert len(d) == 1, "Only one single value should be retrieved for this model" + assert d[0][1] == 'my_test_field', "Can't retrieve the created default value. (1)" + assert d[0][2] == 'global value', "Can't retrieve the created default value. (2)" + + # New API, Conditional version + d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model') + assert len(d) == 0, "No value should be retrieved, the condition is not met" + d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model', condition="foo=eggs") + assert len(d) == 0, 'Condition is not met either, no defaults should be returned' + d = ir_values.get_defaults(self.cr, self.uid, 'other_unexisting_model', condition="foo=bar") + assert len(d) == 1, "Only one single value should be retrieved" + assert d[0][1] == 'my_other_test_field', "Can't retrieve the created default value. (5)" + assert d[0][2] == 'conditional value', "Can't retrieve the created default value. (6)" + + # Do it again but for a specific user. + + ir_values = self.registry('ir.values') + ir_values.set(self.cr, self.uid, 'default', False, 'my_test_field',['unexisting_model'], 'specific value', preserve_user=True) + + # Retrieve it and check it is the one for the current user. + ir_values = self.registry('ir.values') + d = ir_values.get(self.cr, self.uid, 'default', False, ['unexisting_model']) + assert len(d) == 1, "Only one default must be returned per field" + assert d[0][1] == 'my_test_field', "Can't retrieve the created default value." + assert d[0][2] == 'specific value', "Can't retrieve the created default value." + + # Create some action bindings for a non-existing model. + + ir_values = self.registry('ir.values') + ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True) + ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action 2', ['unexisting_model'], 'ir.actions.act_window,11', isobject=True) + ir_values.set(self.cr, self.uid, 'action', 'client_action_multi', 'Side Wizard', ['unexisting_model'], 'ir.actions.act_window,12', isobject=True) + ir_values.set(self.cr, self.uid, 'action', 'client_print_multi', 'Nice Report', ['unexisting_model'], 'ir.actions.report.xml,2', isobject=True) + ir_values.set(self.cr, self.uid, 'action', 'client_action_relate', 'Related Stuff', ['unexisting_model'], 'ir.actions.act_window,14', isobject=True) + + # Replace one action binding to set a new name. + + ir_values = self.registry('ir.values') + ir_values.set(self.cr, self.uid, 'action', 'tree_but_open', 'OnDblClick Action New', ['unexisting_model'], 'ir.actions.act_window,10', isobject=True) + + # Retrieve the action bindings and check they're correct + + ir_values = self.registry('ir.values') + actions = ir_values.get(self.cr, self.uid, 'action', 'tree_but_open', ['unexisting_model']) + assert len(actions) == 2, "Mismatching number of bound actions" + #first action + assert len(actions[0]) == 3, "Malformed action definition" + assert actions[0][1] == 'OnDblClick Action 2', 'Bound action does not match definition' + assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 11, 'Bound action does not match definition' + #second action - this ones comes last because it was re-created with a different name + assert len(actions[1]) == 3, "Malformed action definition" + assert actions[1][1] == 'OnDblClick Action New', 'Re-Registering an action should replace it' + assert isinstance(actions[1][2], dict) and actions[1][2]['id'] == 10, 'Bound action does not match definition' + + actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_multi', ['unexisting_model']) + assert len(actions) == 1, "Mismatching number of bound actions" + assert len(actions[0]) == 3, "Malformed action definition" + assert actions[0][1] == 'Side Wizard', 'Bound action does not match definition' + assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 12, 'Bound action does not match definition' + + actions = ir_values.get(self.cr, self.uid, 'action', 'client_print_multi', ['unexisting_model']) + assert len(actions) == 1, "Mismatching number of bound actions" + assert len(actions[0]) == 3, "Malformed action definition" + assert actions[0][1] == 'Nice Report', 'Bound action does not match definition' + assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 2, 'Bound action does not match definition' + + actions = ir_values.get(self.cr, self.uid, 'action', 'client_action_relate', ['unexisting_model']) + assert len(actions) == 1, "Mismatching number of bound actions" + assert len(actions[0]) == 3, "Malformed action definition" + assert actions[0][1] == 'Related Stuff', 'Bound action does not match definition' + assert isinstance(actions[0][2], dict) and actions[0][2]['id'] == 14, 'Bound action does not match definition' diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index c6558f78eb0..ffc11018949 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -94,19 +94,18 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= def load_test(module_name, idref, mode): cr.commit() - if not tools.config.options['test_disable']: - try: - threading.currentThread().testing = True - _load_data(cr, module_name, idref, mode, 'test') - except Exception, e: - _logger.exception( - 'Tests failed to execute in module %s', module_name) - finally: - threading.currentThread().testing = False - if tools.config.options['test_commit']: - cr.commit() - else: - cr.rollback() + try: + threading.currentThread().testing = True + _load_data(cr, module_name, idref, mode, 'test') + except Exception, e: + _logger.exception( + 'Tests failed to execute in module %s', module_name) + finally: + threading.currentThread().testing = False + if tools.config.options['test_commit']: + cr.commit() + else: + cr.rollback() def _load_data(cr, module_name, idref, mode, kind): """ @@ -201,13 +200,14 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules= # on demo data. Other tests can be added into the regular # 'data' section, but should probably not alter the data, # as there is no rollback. - load_test(module_name, idref, mode) + if tools.config.options['test_enable']: + load_test(module_name, idref, mode) - # Run the `fast_suite` and `checks` tests given by the module. - if module_name == 'base': - # Also run the core tests after the dabase is created. - openerp.modules.module.run_unit_tests('openerp') - openerp.modules.module.run_unit_tests(module_name) + # Run the `fast_suite` and `checks` tests given by the module. + if module_name == 'base': + # Also run the core tests after the dabase is created. + openerp.modules.module.run_unit_tests('openerp') + openerp.modules.module.run_unit_tests(module_name) processed_modules.append(package.name) diff --git a/openerp/modules/module.py b/openerp/modules/module.py index 2e8ef180c2b..3818882aab6 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -556,14 +556,15 @@ def run_unit_tests(module_name): for m in ms: suite.addTests(unittest2.TestLoader().loadTestsFromModule(m)) if ms: - _logger.info('module %s: executing %s `fast_suite` and/or `checks` sub-modules' % (module_name, len(ms))) + _logger.info('module %s: executing %s `fast_suite` and/or `checks` sub-modules', module_name, len(ms)) # Use a custom stream object to log the test executions. class MyStream(object): + def __init__(self): + self.r = re.compile(r'^-*$|^ *... *$|^ok$') def flush(self): pass def write(self, s): - r = re.compile(r'^-*$|^ *... *$|^ok$') - if r.match(s): + if self.r.match(s): return first = True for c in s.split('\n'): @@ -571,6 +572,8 @@ def run_unit_tests(module_name): c = '` ' + c first = False _logger.log(logging.TEST, c) - unittest2.TextTestRunner(verbosity=2, stream=MyStream()).run(suite) + result = unittest2.TextTestRunner(verbosity=2, stream=MyStream()).run(suite) + if not result.wasSuccessful(): + _logger.error('module %s: at least one error occured in a test', module_name) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tests/__init__.py b/openerp/tests/__init__.py index e528da6ba08..5fccb07a082 100644 --- a/openerp/tests/__init__.py +++ b/openerp/tests/__init__.py @@ -8,14 +8,16 @@ Tests can be explicitely added to the `fast_suite` or `checks` lists or not. See the :ref:`test-framework` section in the :ref:`features` list. """ -import test_orm +import test_expression import test_ir_sequence +import test_orm fast_suite = [ test_ir_sequence, ] checks = [ + test_expression, test_orm, ] diff --git a/openerp/tests/common.py b/openerp/tests/common.py index fdbb83f6b5b..ef5b0586136 100644 --- a/openerp/tests/common.py +++ b/openerp/tests/common.py @@ -38,18 +38,17 @@ class TransactionCase(unittest2.TestCase): the tests. """ - def __init__(self, name): - super(TransactionCase, self).__init__(name) - self.registry = lambda model: \ - openerp.modules.registry.RegistryManager.get(DB)[model] - def setUp(self): self.cr = openerp.modules.registry.RegistryManager.get(DB).db.cursor() + self.uid = openerp.SUPERUSER_ID def tearDown(self): self.cr.rollback() self.cr.close() + def registry(self, model): + return openerp.modules.registry.RegistryManager.get(DB)[model] + class RpcCase(unittest2.TestCase): """ Subclass of TestCase with a few XML-RPC proxies. diff --git a/openerp/tests/test_expression.py b/openerp/tests/test_expression.py new file mode 100644 index 00000000000..dd78e3fa88c --- /dev/null +++ b/openerp/tests/test_expression.py @@ -0,0 +1,12 @@ +import unittest2 + +import openerp + +class test_domain_normalization(unittest2.TestCase): + def test_normalize_domain(self): + expression = openerp.osv.expression + norm_domain = domain = ['&',(1,'=',1),('a','=','b')] + assert norm_domain == expression.normalize(domain), "Normalized domains should be left untouched" + domain = [('x','in',['y','z']),('a.v','=','e'),'|','|',('a','=','b'),'!',('c','>','d'),('e','!=','f'),('g','=','h')] + norm_domain = ['&','&','&'] + domain + assert norm_domain == expression.normalize(domain), "Non-normalized domains should be properly normalized" diff --git a/openerp/tools/config.py b/openerp/tools/config.py index a92ad443c7b..6c3f717881b 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -168,8 +168,8 @@ class configmanager(object): help="Launch a YML test file.") group.add_option("--test-report-directory", dest="test_report_directory", my_default=False, help="If set, will save sample of all reports in this directory.") - group.add_option("--test-disable", action="store_true", dest="test_disable", - my_default=False, help="Disable loading test files.") + group.add_option("--test-enable", action="store_true", dest="test_enable", + my_default=False, help="Enable YAML and unit tests.") group.add_option("--test-commit", action="store_true", dest="test_commit", my_default=False, help="Commit database changes performed by tests.") group.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=self._LOGLEVELS.keys(), @@ -395,7 +395,7 @@ class configmanager(object): 'debug_mode', 'smtp_ssl', 'load_language', 'stop_after_init', 'logrotate', 'without_demo', 'netrpc', 'xmlrpc', 'syslog', 'list_db', 'xmlrpcs', 'proxy_mode', - 'test_file', 'test_disable', 'test_commit', 'test_report_directory', + 'test_file', 'test_enable', 'test_commit', 'test_report_directory', 'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'virtual_memory_limit', 'virtual_memory_reset', 'cpu_time_limit', 'unaccent', ]