diff options
| author | Martin Trigaux <mat@odoo.com> | 2019-07-16 09:14:17 +0000 |
|---|---|---|
| committer | Martin Trigaux <mat@odoo.com> | 2019-07-16 09:14:17 +0000 |
| commit | cf8e74a1a7d3404f57798573e8b15ce4dc5b9be4 (patch) | |
| tree | bd95e9d20aff9b4f92550bffb2835a55c3698c27 /odoo/tests | |
| parent | b67a3bb403b80e98a2b66dcb8cf570d2b156fb68 (diff) | |
| parent | 1bd434da34731b5467cc17fdffe6f3ed5dfea2d4 (diff) | |
[MERGE] Forward port of 12.0 to saas-12.2 up to 1bd434da34731b546
closes odoo/odoo#34901
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
Diffstat (limited to 'odoo/tests')
| -rw-r--r-- | odoo/tests/common.py | 105 |
1 files changed, 83 insertions, 22 deletions
diff --git a/odoo/tests/common.py b/odoo/tests/common.py index a5b0c7571a2..5b7edeb4c9a 100644 --- a/odoo/tests/common.py +++ b/odoo/tests/common.py @@ -189,8 +189,9 @@ class MetaCase(type): super(MetaCase, cls).__init__(name, bases, attrs) # assign default test tags if cls.__module__.startswith('odoo.addons.'): - module = cls.__module__.split('.')[2] - cls.test_tags = {'standard', 'at_install', module} + cls.test_tags = {'standard', 'at_install'} + cls.test_module = cls.__module__.split('.')[2] + cls.test_class = cls.__name__ class BaseCase(TreeCase, MetaCase('DummyCase', (object,), {})): @@ -244,7 +245,10 @@ class BaseCase(TreeCase, MetaCase('DummyCase', (object,), {})): def _assertRaises(self, exception): """ Context manager that clears the environment upon failure. """ with super(BaseCase, self).assertRaises(exception) as cm: - with self.env.clear_upon_failure(): + if hasattr(self, 'env'): + with self.env.clear_upon_failure(): + yield cm + else: yield cm def assertRaises(self, exception, func=None, *args, **kwargs): @@ -333,9 +337,23 @@ class BaseCase(TreeCase, MetaCase('DummyCase', (object,), {})): return False return True + def _repr_field_value(record, field_name): + record_value = record[field_name] + field_type = record._fields[field_name].type + if field_type == 'monetary': + currency_field_name = record._fields[field_name].currency_field + record_currency = record[currency_field_name] + return record_currency and record_currency.round(record_value) or record_value + elif field_type in ('one2many', 'many2many'): + return set(record_value.ids) + elif field_type == 'many2one': + return record_value.id + else: + return record_value + def _format_message(records, expected_values): ''' Return a formatted representation of records/expected_values. ''' - all_records_values = records.read(list(expected_values[0].keys()), load=False) + all_records_values = [{key: _repr_field_value(record, key) for key in expected_values[0]} for record in records] msg1 = '\n'.join(pprint.pformat(dic) for dic in all_records_values) msg2 = '\n'.join(pprint.pformat(dic) for dic in expected_values) return 'Current values:\n\n%s\n\nExpected values:\n\n%s' % (msg1, msg2) @@ -706,6 +724,8 @@ class ChromeBrowser(): res = None if res and res.get('id') == ready_id: if res.get('result') == awaited_result: + if has_exceeded: + self._logger.info('The ready code tooks too much time : %s', tdiff) return True else: last_bad_res = res @@ -713,7 +733,6 @@ class ChromeBrowser(): tdiff = time.time() - start_time if tdiff >= 2 and not has_exceeded: has_exceeded = True - self._logger.warning('The ready code takes too much time : %s', tdiff) self.take_screenshot(prefix='failed_ready') self._logger.info('Ready code last try result: %s', last_bad_res or res) @@ -1800,7 +1819,7 @@ def _get_node(view, f, *arg): def tagged(*tags): """ - A decorator to tag TestCase objects + A decorator to tag BaseCase objects Tags are stored in a set that can be accessed from a 'test_tags' attribute A tag prefixed by '-' will remove the tag e.g. to remove the 'standard' tag By default, all Test classes from odoo.tests.common have a test_tags @@ -1810,33 +1829,75 @@ def tagged(*tags): def tags_decorator(obj): include = {t for t in tags if not t.startswith('-')} exclude = {t[1:] for t in tags if t.startswith('-')} - obj.test_tags = (getattr(obj, 'test_tags', set()) | include) - exclude + obj.test_tags = (getattr(obj, 'test_tags', set()) | include) - exclude # todo remove getattr in master since we want to limmit tagged to BaseCase and always have +standard tag return obj return tags_decorator class TagsSelector(object): """ Test selector based on tags. """ + filter_spec_re = re.compile(r'^([+-]?)(\*|\w*)(?:/(\w*))?(?::(\w*))?(?:\.(\w*))?$') # [-][tag][/module][:class][.method] def __init__(self, spec): """ Parse the spec to determine tags to include and exclude. """ - clean_tags = {t.strip() for t in spec.split(',') if t.strip() != ''} - self.exclude = {t[1:] for t in clean_tags if t.startswith('-')} - self.include = {t.replace('+', '') for t in clean_tags if not t.startswith('-')} + filter_specs = {t.strip() for t in spec.split(',') if t.strip()} + self.exclude = set() + self.include = set() + + for filter_spec in filter_specs: + match = self.filter_spec_re.match(filter_spec) + if not match: + _logger.error('Invalid tag %s', filter_spec) + continue + + sign, tag, module, klass, method = match.groups() + is_include = sign != '-' + + if not tag and is_include: + # including /module:class.method implicitly requires 'standard' + tag = 'standard' + elif not tag or tag == '*': + # '*' indicates all tests (instead of 'standard' tests only) + tag = None + test_filter = (tag, module, klass, method) - def check(self, arg): + if is_include: + self.include.add(test_filter) + else: + self.exclude.add(test_filter) + + if self.exclude and not self.include: + self.include.add(('standard', None, None, None)) + + def check(self, test): """ Return whether ``arg`` matches the specification: it must have at - least one tag in ``self.include`` and none in ``self.exclude``. + least one tag in ``self.include`` and none in ``self.exclude`` for each tag category. """ - # handle the case where the Test does not inherit from TransactionCase - tags = getattr(arg, 'test_tags', set()) - inter_no_test = self.exclude.intersection(tags) - if inter_no_test: - _logger.debug("Test '%s' not selected because it is tagged with : %s (exclusions: %s)", arg, inter_no_test, self.exclude) + if not hasattr(test, 'test_tags'): # handle the case where the Test does not inherit from BaseCase and has no test_tags + _logger.debug("Skipping test '%s' because no test_tag found.", test) return False - inter_to_test = self.include.intersection(tags) - if not inter_to_test: - _logger.debug("Test '%s' not selected because it was not tagged with %s", arg, self.include) + + test_module = getattr(test, 'test_module', None) + test_class = getattr(test, 'test_class', None) + test_tags = test.test_tags | {test_module} # module as test_tags deprecated, keep for retrocompatibility, + test_method = getattr(test, '_testMethodName', None) + + def _is_matching(test_filter): + (tag, module, klass, method) = test_filter + if tag and tag not in test_tags: + return False + elif module and module != test_module: + return False + elif klass and klass != test_class: + return False + elif method and test_method and method != test_method: + return False + return True + + if any(_is_matching(test_filter) for test_filter in self.exclude): return False - _logger.debug("Test '%s' selected: tagged with %s, exclusions: %s, inclusions: %s", arg, tags, self.exclude, self.include) - return True + + if any(_is_matching(test_filter) for test_filter in self.include): + return True + + return False |
