def test_selection_fields(self): 'Test selection values' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for field_name, field in model._fields.items(): selection = getattr(field, 'selection', None) if selection is None: continue selection_values = field.selection if not isinstance(selection_values, (tuple, list)): sel_func = getattr(model, field.selection) if not is_instance_method(model, field.selection): selection_values = sel_func() else: record = model() selection_values = sel_func(record) self.assertTrue( all(len(v) == 2 for v in selection_values), msg='Invalid selection values "%(values)s" on field ' '"%(field)s" of model "%(model)s"' % { 'values': selection_values, 'field': field_name, 'model': model.__name__, })
def test_field_methods(self): 'Test field methods' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for attr in dir(model): for prefixes in [['default_'], ['on_change_', 'on_change_with_'], ['order_'], ['domain_'], ['autocomplete_']]: if attr == 'on_change_with': continue # TODO those method should be renamed if attr == 'default_get': continue if mname == 'ir.rule' and attr == 'domain_get': continue # Skip if it is a field if attr in model._fields: continue fnames = [ attr[len(prefix):] for prefix in prefixes if attr.startswith(prefix) ] if not fnames: continue assert any(f in model._fields for f in fnames), ( 'Field method "%s"."%s" for unknown field' % (mname, attr)) if attr.startswith('default_'): getattr(model, attr)() elif attr.startswith('order_'): tables = {None: (model.__table__(), None)} getattr(model, attr)(tables)
def test_wizards(self): 'Test wizards are correctly defined' for wizard_name, wizard in Pool().iterobject(type='wizard'): if not isregisteredby(wizard, self.module, type_='wizard'): continue session_id, start_state, _ = wizard.create() self.assertIn(start_state, wizard.states.keys(), msg='Unknown start state ' '"%(state)s" on wizard "%(wizard)s"' % { 'state': start_state, 'wizard': wizard_name, }) wizard_instance = wizard(session_id) for state_name, state in wizard_instance.states.items(): if isinstance(state, StateView): # Don't test defaults as they may depend on context state.get_view(wizard_instance, state_name) for button in state.get_buttons(wizard_instance, state_name): if button['state'] == wizard.end_state: continue self.assertIn( button['state'], wizard_instance.states.keys(), msg='Unknown button state from "%(state)s" ' 'on wizard "%(wizard)s' % { 'state': state_name, 'wizard': wizard_name, }) if isinstance(state, StateAction): state.get_action()
def test_missing_depends(self): 'Test for missing depends' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.items(): depends = { f for f in field.depends if not f.startswith('_parent_') } with self.subTest(model=mname, field=fname): self.assertLessEqual( depends, set(model._fields), msg='Unknown depends %s in "%s"."%s"' % (list(depends - set(model._fields)), mname, fname)) if issubclass(model, ModelView): for bname, button in model._buttons.items(): depends = set(button.get('depends', [])) with self.subTest(model=mname, button=bname): self.assertLessEqual( depends, set(model._fields), msg='Unknown depends %s in button "%s"."%s"' % (list(depends - set(model._fields)), mname, bname))
def test_workflow_transitions(self): 'Test all workflow transitions exist' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue if not issubclass(model, Workflow): continue field = getattr(model, model._transition_state) if isinstance(field.selection, (tuple, list)): values = field.selection else: # instance method may not return all the possible values if is_instance_method(model, field.selection): continue values = getattr(model, field.selection)() states = set(dict(values)) transition_states = set(chain(*model._transitions)) self.assertLessEqual( transition_states, states, msg='Unknown transition states "%(states)s" ' 'in model "%(model)s". ' % { 'states': list(transition_states - states), 'model': model.__name__, })
def test_field_methods(self): 'Test field methods' with Transaction().start(DB_NAME, USER, context=CONTEXT): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for attr in dir(model): for prefixes in [['default_'], ['on_change_', 'on_change_with_'], ['order_'], ['domain_'], ['autocomplete_']]: if attr == 'on_change_with': continue # TODO those method should be renamed if attr == 'default_get': continue if mname == 'ir.rule' and attr == 'domain_get': continue # Skip if it is a field if attr in model._fields: continue fnames = [attr[len(prefix):] for prefix in prefixes if attr.startswith(prefix)] if not fnames: continue assert any(f in model._fields for f in fnames), ( 'Field method "%s"."%s" for unknown field' % ( mname, attr))
def test_depends(self): 'Test for missing depends' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.items(): fields = set() fields |= get_eval_fields(field.domain) if hasattr(field, 'digits'): fields |= get_eval_fields(field.digits) if hasattr(field, 'add_remove'): fields |= get_eval_fields(field.add_remove) if hasattr(field, 'size'): fields |= get_eval_fields(field.size) fields.discard(fname) fields.discard('context') fields.discard('_user') depends = set(field.depends) assert fields <= depends, ( 'Missing depends %s in "%s"."%s"' % (list(fields - depends), mname, fname)) assert depends <= set(model._fields), ( 'Unknown depends %s in "%s"."%s"' % (list(depends - set(model._fields)), mname, fname)) if issubclass(model, ModelView): for bname, button in model._buttons.items(): depends = set(button.get('depends', [])) assert depends <= set(model._fields), ( 'Unknown depends %s in button "%s"."%s"' % (list(depends - set(model._fields)), mname, bname))
def test_company_multivalue_context(self): "Test context of company multivalue target" pool = Pool() Company = pool.get('company.company') for mname, model in pool.iterobject(): if (not isregisteredby(model, self.module) or issubclass(model, Company)): continue company = None for fname, field in model._fields.items(): if (field._type == 'many2one' and issubclass(field.get_target(), Company)): company = fname break else: continue for fname, field in model._fields.items(): if not hasattr(field, 'get_target'): continue Target = field.get_target() if not issubclass(Target, CompanyMultiValueMixin): continue if company in model._fields: self.assertIn('company', list(field.context.keys()), msg="Missing '%s' value as company " 'in "%s"."%s" context' % (company, mname, fname))
def test_depends(self): "Test depends" def test_missing_relation(depend, depends, qualname): prefix = [] for d in depend.split('.'): if d.startswith('_parent_'): relation = '.'.join(prefix + [d[len('_parent_'):]]) assert relation in depends, ('Missing "%s" in %s' % (relation, qualname)) prefix.append(d) def test_parent_empty(depend, qualname): if depend.startswith('_parent_'): assert '.' in depend, ('Invalid empty "%s" in %s' % (depend, qualname)) def test_missing_parent(model, depend, depends, qualname): dfield = model._fields.get(depend) parent_depends = {d.split('.', 1)[0] for d in depends} if dfield and dfield._type == 'many2one': target = dfield.get_target() for tfield in target._fields.values(): if (tfield._type == 'one2many' and tfield.model_name == mname and tfield.field == depend): assert '_parent_%s' % depend in parent_depends, ( 'Missing "_parent_%s" in %s' % (depend, qualname)) def test_depend_exists(model, depend, qualname): try: depend, nested = depend.split('.', 1) except ValueError: nested = None if depend.startswith('_parent_'): depend = depend[len('_parent_'):] assert isinstance(getattr(model, depend, None), fields.Field), ('Unknonw "%s" in %s' % (depend, qualname)) if nested: target = getattr(model, depend).get_target() test_depend_exists(target, nested, qualname) for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.items(): for attribute in [ 'depends', 'on_change', 'on_change_with', 'selection_change_with', 'autocomplete' ]: depends = getattr(field, attribute, []) qualname = '"%s"."%s"."%s"' % (mname, fname, attribute) for depend in depends: test_depend_exists(model, depend, qualname) test_missing_relation(depend, depends, qualname) test_parent_empty(depend, qualname) if attribute != 'depends': test_missing_parent(model, depend, depends, qualname)
def test_buttons_registered(self): 'Test all buttons are registered in ir.model.button' pool = Pool() Button = pool.get('ir.model.button') for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue if not issubclass(model, ModelView): continue ir_buttons = { b.name for b in Button.search([ ('model.model', '=', model.__name__), ]) } buttons = set(model._buttons) with self.subTest(model=mname): self.assertGreaterEqual( ir_buttons, buttons, msg='The buttons "%(buttons)s" of Model "%(model)s" ' 'are not registered in ir.model.button.' % { 'buttons': list(buttons - ir_buttons), 'model': model.__name__, })
def test_rec_name(self): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue # Skip testing default value even if the field doesn't exist # as there is a fallback to id if model._rec_name == 'name': continue assert model._rec_name in model._fields, ( 'Wrong _rec_name "%s" for %s' % (model._rec_name, mname))
def test_field_methods(self): 'Test field methods' def test_methods(mname, model, attr): for prefixes in [['default_'], ['on_change_', 'on_change_with_'], ['order_'], ['domain_'], ['autocomplete_']]: if attr in {'on_change_with', 'on_change_notify'}: continue # TODO those method should be renamed if attr == 'default_get': continue if mname == 'ir.rule' and attr == 'domain_get': continue # Skip if it is a field if attr in model._fields: continue fnames = [ attr[len(prefix):] for prefix in prefixes if attr.startswith(prefix) ] if not fnames: continue self.assertTrue( any(f in model._fields for f in fnames), msg='Field method "%s"."%s" for unknown field' % (mname, attr)) if attr.startswith('default_'): fname = attr[len('default_'):] if isinstance(model._fields[fname], fields.MultiValue): try: getattr(model, attr)(pattern=None) # get_multivalue may raise an AttributeError # if pattern is not defined on the model except AttributeError: pass else: getattr(model, attr)() elif attr.startswith('order_'): tables = {None: (model.__table__(), None)} getattr(model, attr)(tables) elif any( attr.startswith(p) for p in ['on_change_', 'on_change_with_', 'autocomplete_']): record = model() getattr(record, attr)() for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for attr in dir(model): with self.subTest(model=mname, attr=attr): test_methods(mname, model, attr)
def test_pool_slots(self): "Test pool object has __slots__" for type_ in ['model', 'wizard', 'report']: for name, cls in Pool().iterobject(type_): if not isregisteredby(cls, self.module): continue if getattr(cls, '__no_slots__', None): continue for kls in cls.__mro__: if kls is object: continue self.assertTrue(hasattr(kls, '__slots__'), msg="The %s of %s '%s' has no __slots__" % (kls, type_, name))
def test_rec_name(self): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue # Skip testing default value even if the field doesn't exist # as there is a fallback to id if model._rec_name == 'name': continue assert model._rec_name in model._fields, ( 'Wrong _rec_name "%s" for %s' % (model._rec_name, mname)) field = model._fields[model._rec_name] assert field._type in { 'char', 'text' }, ("Wrong '%s' type for _rec_name of %s'" % (field._type, mname))
def test_modelsingleton_inherit_order(self): 'Test ModelSingleton, ModelSQL, ModelStorage order in the MRO' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue if (not issubclass(model, ModelSingleton) or not issubclass(model, ModelSQL)): continue mro = inspect.getmro(model) singleton_index = mro.index(ModelSingleton) sql_index = mro.index(ModelSQL) assert singleton_index < sql_index, ( "ModelSingleton must appear before ModelSQL in the parent " "classes of '%s'." % mname)
def test_field_relation_target(self): "Test field relation and target" pool = Pool() def test_relation_target(mname, model, fname, field): if isinstance(field, fields.One2Many): Relation = field.get_target() rfield = field.field elif isinstance(field, fields.Many2Many): Relation = field.get_relation() rfield = field.origin else: return if rfield: self.assertIn(rfield, Relation._fields.keys(), msg=('Missing relation field "%s" on "%s" ' 'for "%s"."%s"') % (rfield, Relation.__name__, mname, fname)) reverse_field = Relation._fields[rfield] self.assertIn( reverse_field._type, ['reference', 'many2one', 'one2one'], msg=('Wrong type for relation field "%s" on "%s" ' 'for "%s"."%s"') % (rfield, Relation.__name__, mname, fname)) if (reverse_field._type == 'many2one' and issubclass(model, ModelSQL) # Do not test table_query models # as they can manipulate their id and not callable(model.table_query)): self.assertEqual( reverse_field.model_name, model.__name__, msg=('Wrong model for relation field "%s" on "%s" ' 'for "%s"."%s"') % (rfield, Relation.__name__, mname, fname)) Target = field.get_target() self.assertTrue(Target, msg='Missing target for "%s"."%s"' % (mname, fname)) for mname, model in pool.iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.items(): with self.subTest(model=mname, field=fname): test_relation_target(mname, model, fname, field)
def test_model__access__(self): "Test existing model __access__" pool = Pool() for mname, Model in pool.iterobject(): if not isregisteredby(Model, self.module): continue for field_name in Model.__access__: self.assertIn(field_name, Model._fields.keys(), msg="Wrong __access__ '%s' for %s" % (field_name, mname)) field = Model._fields[field_name] Target = field.get_target() self.assertTrue( Target, msg='Missing target for __access__ "%s" of %s' % (field_name, mname))
def test_rec_name(self): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue # Skip testing default value even if the field doesn't exist # as there is a fallback to id if model._rec_name == 'name': continue with self.subTest(model=mname): self.assertIn(model._rec_name, model._fields.keys(), msg='Wrong _rec_name "%s" for %s' % (model._rec_name, mname)) field = model._fields[model._rec_name] self.assertIn(field._type, {'char', 'text'}, msg="Wrong '%s' type for _rec_name of %s'" % (field._type, mname))
def test_buttons_states(self): "Test the states of buttons" pool = Pool() keys = {'readonly', 'invisible', 'icon', 'pre_validate', 'depends'} for mname, model in pool.iterobject(): if not isregisteredby(model, self.module): continue if not issubclass(model, ModelView): continue for button, states in model._buttons.items(): assert set(states).issubset(keys), ( 'The button "%(button)s" of Model "%(model)s" has extra ' 'keys "%(keys)s".' % { 'button': button, 'model': mname, 'keys': set(states) - keys, })
def test_function_fields(self): "Test function fields methods" for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for field_name, field in model._fields.items(): if not isinstance(field, Function): continue for func_name in [field.getter, field.setter, field.searcher]: if not func_name: continue assert getattr(model, func_name, None), ( "Missing method '%(func_name)s' " "on model '%(model)s' for field '%(field)s" % { 'func_name': func_name, 'model': model.__name__, 'field': field_name, })
def test_depends_parent(self): "Test depends on _parent_ contains also the parent relation" for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.items(): for attribute in ['depends', 'on_change', 'on_change_with', 'selection_change_with', 'autocomplete']: depends = getattr(field, attribute, []) for depend in depends: prefix = [] for d in depend.split('.'): if d.startswith('_parent_'): relation = '.'.join( prefix + [d[len('_parent_'):]]) assert relation in depends, ( 'Missing "%s" in "%s"."%s"."%s"' % ( relation, mname, fname, attribute)) prefix.append(d)
def test_field_relation_domain(self): "Test domain of relation fields" pool = Pool() for mname, model in pool.iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.items(): if not field.domain: continue if hasattr(field, 'get_target'): Target = field.get_target() else: continue if not issubclass(Target, ModelStorage): continue with self.subTest(model=mname, field=fname): domain = PYSONDecoder({}).decode(PYSONEncoder().encode( field.domain)) Target.search(domain, limit=1)
def test_company_rule(self): "Test missing company rule" pool = Pool() Rule = pool.get('ir.rule') Company = pool.get('company.company') Employee = pool.get('company.employee') User = pool.get('res.user') to_check = defaultdict(set) for mname, model in pool.iterobject(): if (not isregisteredby(model, self.module) or model.__access__ or not (issubclass(model, ModelView) and issubclass(model, ModelStorage)) or issubclass(model, (Company, Employee, User))): continue for fname, field in model._fields.items(): if (field._type == 'many2one' and issubclass(field.get_target(), Company)): to_check[fname].add(mname) for fname, models in to_check.items(): rules = Rule.search([ ('rule_group', 'where', [ ('model.model', 'in', list(models)), ('global_p', '=', True), ('perm_read', '=', True), ]), ('domain', '=', PYSONEncoder(sort_keys=True).encode([ (fname, 'in', Eval('companies', [])) ])), ]) with_rules = {r.rule_group.model.model for r in rules} self.assertGreaterEqual( with_rules, models, msg='Models "%(models)s" are missing a global rule ' 'for field "%(field)s"' % { 'models': ', '.join(models - with_rules), 'field': fname, })
def test_depends(self): 'Test for missing depends' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.iteritems(): fields = set() fields |= get_eval_fields(field.domain) if hasattr(field, 'digits'): fields |= get_eval_fields(field.digits) if hasattr(field, 'add_remove'): fields |= get_eval_fields(field.add_remove) fields.discard(fname) fields.discard('context') fields.discard('_user') depends = set(field.depends) assert fields <= depends, ( 'Missing depends %s in "%s"."%s"' % ( list(fields - depends), mname, fname)) assert depends <= set(model._fields), ( 'Unknown depends %s in "%s"."%s"' % ( list(depends - set(model._fields)), mname, fname))
def test_missing_depends(self): 'Test for missing depends' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.items(): fields = set() fields |= get_eval_fields(field.domain) if hasattr(field, 'digits'): fields |= get_eval_fields(field.digits) if hasattr(field, 'add_remove'): fields |= get_eval_fields(field.add_remove) if hasattr(field, 'size'): fields |= get_eval_fields(field.size) fields.discard(fname) fields.discard('context') fields.discard('_user') # XXX PR https://github.com/coopengo/coog/pull/3458 implies # that parent fields are added to depends. depends = set(d for d in field.depends if not d.startswith('_parent')) self.assertLessEqual(fields, depends, msg='Missing depends %s in "%s"."%s"' % (list(fields - depends), mname, fname)) self.assertLessEqual( depends, set(model._fields), msg='Unknown depends %s in "%s"."%s"' % (list(depends - set(model._fields)), mname, fname)) if issubclass(model, ModelView): for bname, button in model._buttons.items(): depends = set(button.get('depends', [])) self.assertLessEqual( depends, set(model._fields), msg='Unknown depends %s in button "%s"."%s"' % (list(depends - set(model._fields)), mname, bname))
def test_workflow_transitions(self): 'Test all workflow transitions exist' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue if not issubclass(model, Workflow): continue field = getattr(model, model._transition_state) if isinstance(field.selection, (tuple, list)): values = field.selection else: # instance method may not return all the possible values if is_instance_method(model, field.selection): continue values = getattr(model, field.selection)() states = set(dict(values)) transition_states = set(chain(*model._transitions)) assert transition_states <= states, ( ('Unknown transition states "%(states)s" ' 'in model "%(model)s". ') % { 'states': list(transition_states - states), 'model': model.__name__, })
def test_depends(self): 'Test for missing depends' with Transaction().start(DB_NAME, USER, context=CONTEXT): for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for fname, field in model._fields.iteritems(): fields = set() fields |= get_eval_fields(field.domain) if hasattr(field, 'digits'): fields |= get_eval_fields(field.digits) if hasattr(field, 'add_remove'): fields |= get_eval_fields(field.add_remove) fields.discard(fname) fields.discard('context') fields.discard('_user') depends = set(field.depends) assert fields <= depends, ( 'Missing depends %s in "%s"."%s"' % ( list(fields - depends), mname, fname)) assert depends <= set(model._fields), ( 'Unknown depends %s in "%s"."%s"' % ( list(depends - set(model._fields)), mname, fname))
def test_field_methods(self): 'Test field methods' for mname, model in Pool().iterobject(): if not isregisteredby(model, self.module): continue for attr in dir(model): for prefixes in [['default_'], ['on_change_', 'on_change_with_'], ['order_'], ['domain_'], ['autocomplete_']]: if attr == 'on_change_with': continue # TODO those method should be renamed if attr == 'default_get': continue if mname == 'ir.rule' and attr == 'domain_get': continue # Skip if it is a field if attr in model._fields: continue if isinstance(getattr(model, attr), (TranslatedSelection, TranslatedDict)): continue fnames = [attr[len(prefix):] for prefix in prefixes if attr.startswith(prefix)] if not fnames: continue assert any(f in model._fields for f in fnames), ( 'Field method "%s"."%s" for unknown field' % ( mname, attr)) if attr.startswith('default_'): getattr(model, attr)() elif attr.startswith('order_'): tables = {None: (model.__table__(), None)} getattr(model, attr)(tables)