From 7721443a625b2efd0744ad347c62795e5ba6bb91 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Tue, 7 Oct 2008 20:41:15 -0600 Subject: Moved ipalib/tests/ into tests/test_ipalib/ --- ipalib/tests/__init__.py | 22 - ipalib/tests/test_backend.py | 37 -- ipalib/tests/test_cli.py | 138 ----- ipalib/tests/test_config.py | 101 ---- ipalib/tests/test_crud.py | 168 ------ ipalib/tests/test_errors.py | 274 --------- ipalib/tests/test_frontend.py | 1080 ----------------------------------- ipalib/tests/test_ipa_types.py | 371 ------------ ipalib/tests/test_plugable.py | 896 ----------------------------- ipalib/tests/test_tstutil.py | 148 ----- ipalib/tests/test_util.py | 49 -- ipalib/tests/tstutil.py | 147 ----- tests/test_ipalib/__init__.py | 22 + tests/test_ipalib/test_backend.py | 37 ++ tests/test_ipalib/test_cli.py | 138 +++++ tests/test_ipalib/test_config.py | 101 ++++ tests/test_ipalib/test_crud.py | 168 ++++++ tests/test_ipalib/test_errors.py | 274 +++++++++ tests/test_ipalib/test_frontend.py | 1080 +++++++++++++++++++++++++++++++++++ tests/test_ipalib/test_ipa_types.py | 371 ++++++++++++ tests/test_ipalib/test_plugable.py | 896 +++++++++++++++++++++++++++++ tests/test_ipalib/test_tstutil.py | 148 +++++ tests/test_ipalib/test_util.py | 49 ++ tests/test_ipalib/tstutil.py | 147 +++++ 24 files changed, 3431 insertions(+), 3431 deletions(-) delete mode 100644 ipalib/tests/__init__.py delete mode 100644 ipalib/tests/test_backend.py delete mode 100644 ipalib/tests/test_cli.py delete mode 100644 ipalib/tests/test_config.py delete mode 100644 ipalib/tests/test_crud.py delete mode 100644 ipalib/tests/test_errors.py delete mode 100644 ipalib/tests/test_frontend.py delete mode 100644 ipalib/tests/test_ipa_types.py delete mode 100644 ipalib/tests/test_plugable.py delete mode 100644 ipalib/tests/test_tstutil.py delete mode 100644 ipalib/tests/test_util.py delete mode 100644 ipalib/tests/tstutil.py create mode 100644 tests/test_ipalib/__init__.py create mode 100644 tests/test_ipalib/test_backend.py create mode 100644 tests/test_ipalib/test_cli.py create mode 100644 tests/test_ipalib/test_config.py create mode 100644 tests/test_ipalib/test_crud.py create mode 100644 tests/test_ipalib/test_errors.py create mode 100644 tests/test_ipalib/test_frontend.py create mode 100644 tests/test_ipalib/test_ipa_types.py create mode 100644 tests/test_ipalib/test_plugable.py create mode 100644 tests/test_ipalib/test_tstutil.py create mode 100644 tests/test_ipalib/test_util.py create mode 100644 tests/test_ipalib/tstutil.py diff --git a/ipalib/tests/__init__.py b/ipalib/tests/__init__.py deleted file mode 100644 index d3658c45..00000000 --- a/ipalib/tests/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib` package. -""" diff --git a/ipalib/tests/test_backend.py b/ipalib/tests/test_backend.py deleted file mode 100644 index 967e9fdf..00000000 --- a/ipalib/tests/test_backend.py +++ /dev/null @@ -1,37 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.backend` module. -""" - -from ipalib import backend, plugable, errors -from tstutil import ClassChecker - - -class test_Backend(ClassChecker): - """ - Test the `backend.Backend` class. - """ - - _cls = backend.Backend - - def test_class(self): - assert self.cls.__bases__ == (plugable.Plugin,) - assert self.cls.__proxy__ is False diff --git a/ipalib/tests/test_cli.py b/ipalib/tests/test_cli.py deleted file mode 100644 index 90c66d41..00000000 --- a/ipalib/tests/test_cli.py +++ /dev/null @@ -1,138 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.cli` module. -""" - -from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker -from ipalib import cli, plugable - - -def test_to_cli(): - """ - Tests the `cli.to_cli` function. - """ - f = cli.to_cli - assert f('initialize') == 'initialize' - assert f('user_add') == 'user-add' - - -def test_from_cli(): - """ - Tests the `cli.from_cli` function. - """ - f = cli.from_cli - assert f('initialize') == 'initialize' - assert f('user-add') == 'user_add' - - -def get_cmd_name(i): - return 'cmd_%d' % i - -class DummyCommand(object): - def __init__(self, name): - self.__name = name - - def __get_name(self): - return self.__name - name = property(__get_name) - -class DummyAPI(object): - def __init__(self, cnt): - self.__cmd = plugable.NameSpace(self.__cmd_iter(cnt)) - - def __get_cmd(self): - return self.__cmd - Command = property(__get_cmd) - - def __cmd_iter(self, cnt): - for i in xrange(cnt): - yield DummyCommand(get_cmd_name(i)) - - def finalize(self): - pass - - def register(self, *args, **kw): - pass - - - - - -class test_CLI(ClassChecker): - """ - Tests the `cli.CLI` class. - """ - _cls = cli.CLI - - def test_class(self): - assert type(self.cls.api) is property - - def test_api(self): - """ - Tests the `cli.CLI.api` property. - """ - api = 'the plugable.API instance' - o = self.cls(api) - assert read_only(o, 'api') is api - - def dont_parse(self): - """ - Tests the `cli.CLI.parse` method. - """ - o = self.cls(None) - args = ['hello', 'naughty', 'nurse'] - kw = dict( - first_name='Naughty', - last_name='Nurse', - ) - opts = ['--%s=%s' % (k.replace('_', '-'), v) for (k, v) in kw.items()] - assert o.parse(args + []) == (args, {}) - assert o.parse(opts + []) == ([], kw) - assert o.parse(args + opts) == (args, kw) - assert o.parse(opts + args) == (args, kw) - - def test_mcl(self): - """ - Tests the `cli.CLI.mcl` (Max Command Length) property . - """ - cnt = 100 - api = DummyAPI(cnt) - len(api.Command) == cnt - o = self.cls(api) - assert o.mcl is None - o.build_map() - assert o.mcl == 6 # len('cmd_99') - - def test_dict(self): - """ - Tests the `cli.CLI.__contains__` and `cli.CLI.__getitem__` methods. - """ - cnt = 25 - api = DummyAPI(cnt) - assert len(api.Command) == cnt - o = self.cls(api) - o.build_map() - for cmd in api.Command(): - key = cli.to_cli(cmd.name) - assert key in o - assert o[key] is cmd - assert cmd.name not in o - raises(KeyError, getitem, o, cmd.name) diff --git a/ipalib/tests/test_config.py b/ipalib/tests/test_config.py deleted file mode 100644 index de7d4c22..00000000 --- a/ipalib/tests/test_config.py +++ /dev/null @@ -1,101 +0,0 @@ -# Authors: -# Martin Nagy -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.config` module. -""" - -import types - -from tstutil import raises -from ipalib import config - - -def test_generate_env(): - """ - Test the `config.generate_env` function - """ - - # Make sure we don't overwrite any properties - env = dict( - query_dns = False, - server = ('first', 'second'), - realm = 'myrealm', - ) - d = config.generate_env(env) - assert d['query_dns'] == False - - # Make sure the servers is overwrote properly (that it is still LazyProp) - iter = d['server'].get_value() - assert iter.next() == 'first' - assert iter.next() == 'second' - - -def test_LazyProp(): - """ - Test the `config.LazyProp` class - """ - - def dummy(): - return 1 - - # Basic sanity testing with no initial value - prop = config.LazyProp(dummy) - assert prop.get_value() == 1 - prop.set_value(2) - assert prop.get_value() == 2 - - # Basic sanity testing with initial value - prop = config.LazyProp(dummy, 3) - assert prop.get_value() == 3 - prop.set_value(4) - assert prop.get_value() == 4 - - -def test_LazyIter(): - """ - Test the `config.LazyIter` class - """ - - def dummy(): - yield 1 - yield 2 - - # Basic sanity testing with no initial value - prop = config.LazyIter(dummy) - iter = prop.get_value() - assert iter.next() == 1 - assert iter.next() == 2 - raises(StopIteration, iter.next) - - # Basic sanity testing with initial value - prop = config.LazyIter(dummy, 0) - iter = prop.get_value() - assert iter.next() == 0 - assert iter.next() == 1 - assert iter.next() == 2 - raises(StopIteration, iter.next) - - -def test_read_config(): - """ - Test the `config.read_config` class - """ - - raises(AssertionError, config.read_config, 1) diff --git a/ipalib/tests/test_crud.py b/ipalib/tests/test_crud.py deleted file mode 100644 index 9355f237..00000000 --- a/ipalib/tests/test_crud.py +++ /dev/null @@ -1,168 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.crud` module. -""" - -from tstutil import read_only, raises, ClassChecker -from ipalib import crud, frontend, plugable, config - -def get_api(): - api = plugable.API( - frontend.Object, - frontend.Method, - frontend.Property, - ) - api.env.update(config.generate_env()) - class user(frontend.Object): - takes_params = ( - 'givenname', - 'sn', - frontend.Param('uid', primary_key=True), - 'initials', - ) - api.register(user) - return api - - -class test_Add(ClassChecker): - """ - Test the `crud.Add` class. - """ - - _cls = crud.Add - - def test_class(self): - assert self.cls.__bases__ == (frontend.Method,) - - def test_options_args(self): - """ - Test `crud.Add.get_args` and `crud.Add.get_options` methods. - """ - api = get_api() - class user_add(self.cls): - pass - api.register(user_add) - api.finalize() - assert list(api.Method.user_add.args) == ['uid'] - assert list(api.Method.user_add.options) == \ - ['givenname', 'sn', 'initials'] - for param in api.Method.user_add.options(): - assert param.required is True - - -class test_Get(ClassChecker): - """ - Test the `crud.Get` class. - """ - - _cls = crud.Get - - def test_class(self): - assert self.cls.__bases__ == (frontend.Method,) - - def test_options_args(self): - """ - Test `crud.Get.get_args` and `crud.Get.get_options` methods. - """ - api = get_api() - class user_get(self.cls): - pass - api.register(user_get) - api.finalize() - assert list(api.Method.user_get.args) == ['uid'] - assert list(api.Method.user_get.options) == [] - - -class test_Del(ClassChecker): - """ - Test the `crud.Del` class. - """ - - _cls = crud.Del - - def test_class(self): - assert self.cls.__bases__ == (frontend.Method,) - - def test_options_args(self): - """ - Test `crud.Del.get_args` and `crud.Del.get_options` methods. - """ - api = get_api() - class user_del(self.cls): - pass - api.register(user_del) - api.finalize() - assert list(api.Method.user_del.args) == ['uid'] - assert list(api.Method.user_del.options) == [] - - -class test_Mod(ClassChecker): - """ - Test the `crud.Mod` class. - """ - - _cls = crud.Mod - - def test_class(self): - assert self.cls.__bases__ == (frontend.Method,) - - def test_options_args(self): - """ - Test `crud.Mod.get_args` and `crud.Mod.get_options` methods. - """ - api = get_api() - class user_mod(self.cls): - pass - api.register(user_mod) - api.finalize() - assert list(api.Method.user_mod.args) == ['uid'] - assert api.Method.user_mod.args[0].required is True - assert list(api.Method.user_mod.options) == \ - ['givenname', 'sn', 'initials'] - for param in api.Method.user_mod.options(): - assert param.required is False - - -class test_Find(ClassChecker): - """ - Test the `crud.Find` class. - """ - - _cls = crud.Find - - def test_class(self): - assert self.cls.__bases__ == (frontend.Method,) - - def test_options_args(self): - """ - Test `crud.Find.get_args` and `crud.Find.get_options` methods. - """ - api = get_api() - class user_find(self.cls): - pass - api.register(user_find) - api.finalize() - assert list(api.Method.user_find.args) == ['uid'] - assert api.Method.user_find.args[0].required is True - assert list(api.Method.user_find.options) == \ - ['givenname', 'sn', 'initials'] - for param in api.Method.user_find.options(): - assert param.required is False diff --git a/ipalib/tests/test_errors.py b/ipalib/tests/test_errors.py deleted file mode 100644 index 7d2df4df..00000000 --- a/ipalib/tests/test_errors.py +++ /dev/null @@ -1,274 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.errors` module. -""" - -from tstutil import raises, ClassChecker -from ipalib import errors - - -type_format = '%s: need a %r; got %r' - - -def check_TypeError(f, value, type_, name, **kw): - e = raises(TypeError, f, value, type_, name, **kw) - assert e.value is value - assert e.type is type_ - assert e.name is name - assert str(e) == type_format % (name, type_, value) - - -def test_raise_TypeError(): - """ - Tests the `errors.raise_TypeError` function. - """ - f = errors.raise_TypeError - value = 'Hello.' - type_ = unicode - name = 'message' - - check_TypeError(f, value, type_, name) - - # name not an str - fail_name = 42 - e = raises(AssertionError, f, value, type_, fail_name) - assert str(e) == type_format % ('name', str, fail_name), str(e) - - # type_ not a type: - fail_type = unicode() - e = raises(AssertionError, f, value, fail_type, name) - assert str(e) == type_format % ('type_', type, fail_type) - - # type(value) is type_: - fail_value = u'How are you?' - e = raises(AssertionError, f, fail_value, type_, name) - assert str(e) == 'value: %r is a %r' % (fail_value, type_) - - -def test_check_type(): - """ - Tests the `errors.check_type` function. - """ - f = errors.check_type - value = 'How are you?' - type_ = str - name = 'greeting' - - # Should pass: - assert value is f(value, type_, name) - assert None is f(None, type_, name, allow_none=True) - - # Should raise TypeError - check_TypeError(f, None, type_, name) - check_TypeError(f, value, basestring, name) - check_TypeError(f, value, unicode, name) - - # name not an str - fail_name = unicode(name) - e = raises(AssertionError, f, value, type_, fail_name) - assert str(e) == type_format % ('name', str, fail_name) - - # type_ not a type: - fail_type = 42 - e = raises(AssertionError, f, value, fail_type, name) - assert str(e) == type_format % ('type_', type, fail_type) - - # allow_none not a bool: - fail_bool = 0 - e = raises(AssertionError, f, value, type_, name, allow_none=fail_bool) - assert str(e) == type_format % ('allow_none', bool, fail_bool) - - -def test_check_isinstance(): - """ - Tests the `errors.check_isinstance` function. - """ - f = errors.check_isinstance - value = 'How are you?' - type_ = str - name = 'greeting' - - # Should pass: - assert value is f(value, type_, name) - assert value is f(value, basestring, name) - assert None is f(None, type_, name, allow_none=True) - - # Should raise TypeError - check_TypeError(f, None, type_, name) - check_TypeError(f, value, unicode, name) - - # name not an str - fail_name = unicode(name) - e = raises(AssertionError, f, value, type_, fail_name) - assert str(e) == type_format % ('name', str, fail_name) - - # type_ not a type: - fail_type = 42 - e = raises(AssertionError, f, value, fail_type, name) - assert str(e) == type_format % ('type_', type, fail_type) - - # allow_none not a bool: - fail_bool = 0 - e = raises(AssertionError, f, value, type_, name, allow_none=fail_bool) - assert str(e) == type_format % ('allow_none', bool, fail_bool) - - -class test_IPAError(ClassChecker): - """ - Tests the `errors.IPAError` exception. - """ - _cls = errors.IPAError - - def test_class(self): - assert self.cls.__bases__ == (Exception,) - - def test_init(self): - """ - Tests the `errors.IPAError.__init__` method. - """ - args = ('one fish', 'two fish') - e = self.cls(*args) - assert e.args == args - assert self.cls().args == tuple() - - def test_str(self): - """ - Tests the `errors.IPAError.__str__` method. - """ - f = 'The %s color is %s.' - class custom_error(self.cls): - format = f - for args in [('sexiest', 'red'), ('most-batman-like', 'black')]: - e = custom_error(*args) - assert e.args == args - assert str(e) == f % args - - -class test_ValidationError(ClassChecker): - """ - Tests the `errors.ValidationError` exception. - """ - _cls = errors.ValidationError - - def test_class(self): - assert self.cls.__bases__ == (errors.IPAError,) - - def test_init(self): - """ - Tests the `errors.ValidationError.__init__` method. - """ - name = 'login' - value = 'Whatever' - error = 'Must be lowercase.' - for index in (None, 3): - e = self.cls(name, value, error, index=index) - assert e.name is name - assert e.value is value - assert e.error is error - assert e.index is index - assert str(e) == 'invalid %r value %r: %s' % (name, value, error) - # Check that index default is None: - assert self.cls(name, value, error).index is None - # Check non str name raises AssertionError: - raises(AssertionError, self.cls, unicode(name), value, error) - # Check non int index raises AssertionError: - raises(AssertionError, self.cls, name, value, error, index=5.0) - # Check negative index raises AssertionError: - raises(AssertionError, self.cls, name, value, error, index=-2) - - -class test_ConversionError(ClassChecker): - """ - Tests the `errors.ConversionError` exception. - """ - _cls = errors.ConversionError - - def test_class(self): - assert self.cls.__bases__ == (errors.ValidationError,) - - def test_init(self): - """ - Tests the `errors.ConversionError.__init__` method. - """ - name = 'some_arg' - value = '42.0' - class type_(object): - conversion_error = 'Not an integer' - for index in (None, 7): - e = self.cls(name, value, type_, index=index) - assert e.name is name - assert e.value is value - assert e.type is type_ - assert e.error is type_.conversion_error - assert e.index is index - assert str(e) == 'invalid %r value %r: %s' % (name, value, - type_.conversion_error) - # Check that index default is None: - assert self.cls(name, value, type_).index is None - - -class test_RuleError(ClassChecker): - """ - Tests the `errors.RuleError` exception. - """ - _cls = errors.RuleError - - def test_class(self): - assert self.cls.__bases__ == (errors.ValidationError,) - - def test_init(self): - """ - Tests the `errors.RuleError.__init__` method. - """ - name = 'whatever' - value = 'The smallest weird number.' - def my_rule(value): - return 'Value is bad.' - error = my_rule(value) - for index in (None, 42): - e = self.cls(name, value, error, my_rule, index=index) - assert e.name is name - assert e.value is value - assert e.error is error - assert e.rule is my_rule - # Check that index default is None: - assert self.cls(name, value, error, my_rule).index is None - - -class test_RequirementError(ClassChecker): - """ - Tests the `errors.RequirementError` exception. - """ - _cls = errors.RequirementError - - def test_class(self): - assert self.cls.__bases__ == (errors.ValidationError,) - - def test_init(self): - """ - Tests the `errors.RequirementError.__init__` method. - """ - name = 'givenname' - e = self.cls(name) - assert e.name is name - assert e.value is None - assert e.error == 'Required' - assert e.index is None diff --git a/ipalib/tests/test_frontend.py b/ipalib/tests/test_frontend.py deleted file mode 100644 index c70cc00d..00000000 --- a/ipalib/tests/test_frontend.py +++ /dev/null @@ -1,1080 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.frontend` module. -""" - -from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker -from tstutil import check_TypeError -from ipalib import frontend, backend, plugable, errors, ipa_types, config - - -def test_RULE_FLAG(): - assert frontend.RULE_FLAG == 'validation_rule' - - -def test_rule(): - """ - Tests the `frontend.rule` function. - """ - flag = frontend.RULE_FLAG - rule = frontend.rule - def my_func(): - pass - assert not hasattr(my_func, flag) - rule(my_func) - assert getattr(my_func, flag) is True - @rule - def my_func2(): - pass - assert getattr(my_func2, flag) is True - - -def test_is_rule(): - """ - Tests the `frontend.is_rule` function. - """ - is_rule = frontend.is_rule - flag = frontend.RULE_FLAG - - class no_call(object): - def __init__(self, value): - if value is not None: - assert value in (True, False) - setattr(self, flag, value) - - class call(no_call): - def __call__(self): - pass - - assert is_rule(call(True)) - assert not is_rule(no_call(True)) - assert not is_rule(call(False)) - assert not is_rule(call(None)) - - -class test_DefaultFrom(ClassChecker): - """ - Tests the `frontend.DefaultFrom` class. - """ - _cls = frontend.DefaultFrom - - def test_class(self): - assert self.cls.__bases__ == (plugable.ReadOnly,) - - def test_init(self): - """ - Tests the `frontend.DefaultFrom.__init__` method. - """ - def callback(*args): - return args - keys = ('givenname', 'sn') - o = self.cls(callback, *keys) - assert read_only(o, 'callback') is callback - assert read_only(o, 'keys') == keys - lam = lambda first, last: first[0] + last - o = self.cls(lam) - assert read_only(o, 'keys') == ('first', 'last') - - def test_call(self): - """ - Tests the `frontend.DefaultFrom.__call__` method. - """ - def callback(givenname, sn): - return givenname[0] + sn[0] - keys = ('givenname', 'sn') - o = self.cls(callback, *keys) - kw = dict( - givenname='John', - sn='Public', - hello='world', - ) - assert o(**kw) == 'JP' - assert o() is None - for key in ('givenname', 'sn'): - kw_copy = dict(kw) - del kw_copy[key] - assert o(**kw_copy) is None - o = self.cls(lambda first, last: first[0] + last) - assert o(first='john', last='doe') == 'jdoe' - assert o(first='', last='doe') is None - assert o(one='john', two='doe') is None - - -def test_parse_param_spec(): - """ - Test the `frontend.parse_param_spec` function. - """ - f = frontend.parse_param_spec - - assert f('name') == ('name', dict(required=True, multivalue=False)) - assert f('name?') == ('name', dict(required=False, multivalue=False)) - assert f('name*') == ('name', dict(required=False, multivalue=True)) - assert f('name+') == ('name', dict(required=True, multivalue=True)) - - -class test_Param(ClassChecker): - """ - Test the `frontend.Param` class. - """ - _cls = frontend.Param - - def test_class(self): - assert self.cls.__bases__ == (plugable.ReadOnly,) - - def test_init(self): - """ - Test the `frontend.Param.__init__` method. - """ - name = 'sn' - o = self.cls(name) - assert o.__islocked__() is True - - # Test default values - assert read_only(o, 'name') is name - assert isinstance(read_only(o, 'type'), ipa_types.Unicode) - assert read_only(o, 'doc') == '' - assert read_only(o, 'required') is True - assert read_only(o, 'multivalue') is False - assert read_only(o, 'default') is None - assert read_only(o, 'default_from') is None - assert read_only(o, 'rules') == tuple() - assert len(read_only(o, 'all_rules')) == 1 - assert read_only(o, 'primary_key') is False - - # Test all kw args: - t = ipa_types.Int() - assert self.cls(name, type=t).type is t - assert self.cls(name, doc='the doc').doc == 'the doc' - assert self.cls(name, required=False).required is False - assert self.cls(name, multivalue=True).multivalue is True - assert self.cls(name, default=u'Hello').default == u'Hello' - df = frontend.DefaultFrom(lambda f, l: f + l, - 'first', 'last', - ) - lam = lambda first, last: first + last - for cb in (df, lam): - o = self.cls(name, default_from=cb) - assert type(o.default_from) is frontend.DefaultFrom - assert o.default_from.keys == ('first', 'last') - assert o.default_from.callback('butt', 'erfly') == 'butterfly' - rules = (lambda whatever: 'Not okay!',) - o = self.cls(name, rules=rules) - assert o.rules is rules - assert o.all_rules[1:] == rules - assert self.cls(name, primary_key=True).primary_key is True - - # Test default type_: - o = self.cls(name) - assert isinstance(o.type, ipa_types.Unicode) - - # Test param spec parsing: - o = self.cls('name?') - assert o.name == 'name' - assert o.required is False - assert o.multivalue is False - - o = self.cls('name*') - assert o.name == 'name' - assert o.required is False - assert o.multivalue is True - - o = self.cls('name+') - assert o.name == 'name' - assert o.required is True - assert o.multivalue is True - - e = raises(TypeError, self.cls, name, whatever=True, another=False) - assert str(e) == \ - 'Param.__init__() takes no such kwargs: another, whatever' - - def test_clone(self): - """ - Test the `frontend.Param.__clone__` method. - """ - def compare(o, kw): - for (k, v) in kw.iteritems(): - assert getattr(o, k) == v, (k, v, getattr(o, k)) - default = dict( - required=False, - multivalue=False, - default=None, - default_from=None, - rules=tuple(), - ) - name = 'hair_color?' - type_ = ipa_types.Int() - o = self.cls(name, type=type_) - compare(o, default) - - override = dict(multivalue=True, default=42) - d = dict(default) - d.update(override) - clone = o.__clone__(**override) - assert clone.name == 'hair_color' - assert clone.type is o.type - compare(clone, d) - - def test_convert(self): - """ - Test the `frontend.Param.convert` method. - """ - name = 'some_number' - type_ = ipa_types.Int() - okay = (7, 7L, 7.0, ' 7 ') - fail = ('7.0', '7L', 'whatever', object) - none = (None, '', u'', tuple(), []) - - # Scenario 1: multivalue=False - o = self.cls(name, type=type_) - for n in none: - assert o.convert(n) is None - for value in okay: - new = o.convert(value) - assert new == 7 - assert type(new) is int - for value in fail: - e = raises(errors.ConversionError, o.convert, value) - assert e.name is name - assert e.value is value - assert e.error is type_.conversion_error - assert e.index is None - - # Scenario 2: multivalue=True - o = self.cls(name, type=type_, multivalue=True) - for n in none: - assert o.convert(n) is None - for value in okay: - assert o.convert((value,)) == (7,) - assert o.convert([value]) == (7,) - assert o.convert(okay) == tuple(int(v) for v in okay) - cnt = 5 - for value in fail: - for i in xrange(cnt): - others = list(7 for x in xrange(cnt)) - others[i] = value - for v in [tuple(others), list(others)]: - e = raises(errors.ConversionError, o.convert, v) - assert e.name is name - assert e.value is value - assert e.error is type_.conversion_error - assert e.index == i - - def test_normalize(self): - """ - Test the `frontend.Param.normalize` method. - """ - name = 'sn' - callback = lambda value: value.lower() - values = (None, u'Hello', (u'Hello',), 'hello', ['hello']) - none = (None, '', u'', tuple(), []) - - # Scenario 1: multivalue=False, normalize=None - o = self.cls(name) - for v in values: - # When normalize=None, value is returned, no type checking: - assert o.normalize(v) is v - - # Scenario 2: multivalue=False, normalize=callback - o = self.cls(name, normalize=callback) - for v in (u'Hello', u'hello', 'Hello'): # Okay - assert o.normalize(v) == 'hello' - for v in [None, 42, (u'Hello',)]: # Not basestring - assert o.normalize(v) is v - for n in none: - assert o.normalize(n) is None - - # Scenario 3: multivalue=True, normalize=None - o = self.cls(name, multivalue=True) - for v in values: - # When normalize=None, value is returned, no type checking: - assert o.normalize(v) is v - - # Scenario 4: multivalue=True, normalize=callback - o = self.cls(name, multivalue=True, normalize=callback) - assert o.normalize([]) is None - assert o.normalize(tuple()) is None - for value in [(u'Hello',), (u'hello',), 'Hello', ['Hello']]: # Okay - assert o.normalize(value) == (u'hello',) - fail = 42 # Not basestring - for v in [[fail], (u'hello', fail)]: # Non basestring member - assert o.normalize(v) == tuple(v) - for n in none: - assert o.normalize(n) is None - - def test_validate(self): - """ - Tests the `frontend.Param.validate` method. - """ - name = 'sn' - type_ = ipa_types.Unicode() - def case_rule(value): - if not value.islower(): - return 'Must be lower case' - my_rules = (case_rule,) - okay = u'whatever' - fail_case = u'Whatever' - fail_type = 'whatever' - - # Scenario 1: multivalue=False - o = self.cls(name, type=type_, rules=my_rules) - assert o.rules == my_rules - assert o.all_rules == (type_.validate, case_rule) - o.validate(okay) - e = raises(errors.RuleError, o.validate, fail_case) - assert e.name is name - assert e.value is fail_case - assert e.error == 'Must be lower case' - assert e.rule is case_rule - assert e.index is None - check_TypeError(fail_type, unicode, 'value', o.validate, fail_type) - - ## Scenario 2: multivalue=True - o = self.cls(name, type=type_, multivalue=True, rules=my_rules) - o.validate((okay,)) - cnt = 5 - for i in xrange(cnt): - others = list(okay for x in xrange(cnt)) - others[i] = fail_case - value = tuple(others) - e = raises(errors.RuleError, o.validate, value) - assert e.name is name - assert e.value is fail_case - assert e.error == 'Must be lower case' - assert e.rule is case_rule - assert e.index == i - for not_tuple in (okay, [okay]): - check_TypeError(not_tuple, tuple, 'value', o.validate, not_tuple) - for has_str in [(fail_type,), (okay, fail_type)]: - check_TypeError(fail_type, unicode, 'value', o.validate, has_str) - - def test_get_default(self): - """ - Tests the `frontend.Param.get_default` method. - """ - name = 'greeting' - default = u'Hello, world!' - default_from = frontend.DefaultFrom( - lambda first, last: u'Hello, %s %s!' % (first, last), - 'first', 'last' - ) - - # Scenario 1: multivalue=False - o = self.cls(name, - default=default, - default_from=default_from, - ) - assert o.default is default - assert o.default_from is default_from - assert o.get_default() == default - assert o.get_default(first='John', last='Doe') == 'Hello, John Doe!' - - # Scenario 2: multivalue=True - default = (default,) - o = self.cls(name, - default=default, - default_from=default_from, - multivalue=True, - ) - assert o.default is default - assert o.default_from is default_from - assert o.get_default() == default - assert o.get_default(first='John', last='Doe') == ('Hello, John Doe!',) - - def test_get_value(self): - """ - Tests the `frontend.Param.get_values` method. - """ - name = 'status' - values = (u'Active', u'Inactive') - o = self.cls(name, type=ipa_types.Unicode()) - assert o.get_values() == tuple() - o = self.cls(name, type=ipa_types.Enum(*values)) - assert o.get_values() == values - - -def test_create_param(): - """ - Test the `frontend.create_param` function. - """ - f = frontend.create_param - for name in ['arg', 'arg?', 'arg*', 'arg+']: - o = f(name) - assert type(o) is frontend.Param - assert type(o.type) is ipa_types.Unicode - assert o.name == 'arg' - assert f(o) is o - o = f('arg') - assert o.required is True - assert o.multivalue is False - o = f('arg?') - assert o.required is False - assert o.multivalue is False - o = f('arg*') - assert o.required is False - assert o.multivalue is True - o = f('arg+') - assert o.required is True - assert o.multivalue is True - - -class test_Command(ClassChecker): - """ - Tests the `frontend.Command` class. - """ - _cls = frontend.Command - - def get_subcls(self): - class Rule(object): - def __init__(self, name): - self.name = name - - def __call__(self, value): - if value != self.name: - return 'must equal %s' % self.name - - default_from = frontend.DefaultFrom( - lambda arg: arg, - 'default_from' - ) - normalize = lambda value: value.lower() - - class example(self.cls): - takes_options = ( - frontend.Param('option0', - normalize=normalize, - default_from=default_from, - rules=(Rule('option0'),) - ), - frontend.Param('option1', - normalize=normalize, - default_from=default_from, - rules=(Rule('option1'),), - required=True, - ), - ) - return example - - def get_instance(self, args=tuple(), options=tuple()): - """ - Helper method used to test args and options. - """ - class example(self.cls): - takes_args = args - takes_options = options - o = example() - o.finalize() - return o - - def test_class(self): - assert self.cls.__bases__ == (plugable.Plugin,) - assert self.cls.takes_options == tuple() - assert self.cls.takes_args == tuple() - - def test_get_args(self): - """ - Tests the `frontend.Command.get_args` method. - """ - assert list(self.cls().get_args()) == [] - args = ('login', 'stuff') - o = self.get_instance(args=args) - assert o.get_args() is args - - def test_get_options(self): - """ - Tests the `frontend.Command.get_options` method. - """ - assert list(self.cls().get_options()) == [] - options = ('verbose', 'debug') - o = self.get_instance(options=options) - assert o.get_options() is options - - def test_args(self): - """ - Tests the ``Command.args`` instance attribute. - """ - assert 'args' in self.cls.__public__ # Public - assert self.cls().args is None - o = self.cls() - o.finalize() - assert type(o.args) is plugable.NameSpace - assert len(o.args) == 0 - args = ('destination', 'source?') - ns = self.get_instance(args=args).args - assert type(ns) is plugable.NameSpace - assert len(ns) == len(args) - assert list(ns) == ['destination', 'source'] - assert type(ns.destination) is frontend.Param - assert type(ns.source) is frontend.Param - assert ns.destination.required is True - assert ns.destination.multivalue is False - assert ns.source.required is False - assert ns.source.multivalue is False - - # Test TypeError: - e = raises(TypeError, self.get_instance, args=(u'whatever',)) - assert str(e) == \ - 'create_param() takes %r or %r; got %r' % (str, frontend.Param, u'whatever') - - # Test ValueError, required after optional: - e = raises(ValueError, self.get_instance, args=('arg1?', 'arg2')) - assert str(e) == 'arg2: required argument after optional' - - # Test ValueError, scalar after multivalue: - e = raises(ValueError, self.get_instance, args=('arg1+', 'arg2')) - assert str(e) == 'arg2: only final argument can be multivalue' - - def test_max_args(self): - """ - Test the ``Command.max_args`` instance attribute. - """ - o = self.get_instance() - assert o.max_args == 0 - o = self.get_instance(args=('one?',)) - assert o.max_args == 1 - o = self.get_instance(args=('one', 'two?')) - assert o.max_args == 2 - o = self.get_instance(args=('one', 'multi+',)) - assert o.max_args is None - o = self.get_instance(args=('one', 'multi*',)) - assert o.max_args is None - - def test_options(self): - """ - Tests the ``Command.options`` instance attribute. - """ - assert 'options' in self.cls.__public__ # Public - assert self.cls().options is None - o = self.cls() - o.finalize() - assert type(o.options) is plugable.NameSpace - assert len(o.options) == 0 - options = ('target', 'files*') - ns = self.get_instance(options=options).options - assert type(ns) is plugable.NameSpace - assert len(ns) == len(options) - assert list(ns) == ['target', 'files'] - assert type(ns.target) is frontend.Param - assert type(ns.files) is frontend.Param - assert ns.target.required is True - assert ns.target.multivalue is False - assert ns.files.required is False - assert ns.files.multivalue is True - - def test_convert(self): - """ - Tests the `frontend.Command.convert` method. - """ - assert 'convert' in self.cls.__public__ # Public - kw = dict( - option0='option0', - option1='option1', - ) - expected = dict(kw) - expected.update(dict(option0=u'option0', option1=u'option1')) - o = self.subcls() - o.finalize() - for (key, value) in o.convert(**kw).iteritems(): - v = expected[key] - assert value == v - assert type(value) is type(v) - - def test_normalize(self): - """ - Tests the `frontend.Command.normalize` method. - """ - assert 'normalize' in self.cls.__public__ # Public - kw = dict( - option0=u'OPTION0', - option1=u'OPTION1', - ) - norm = dict((k, v.lower()) for (k, v) in kw.items()) - sub = self.subcls() - sub.finalize() - assert sub.normalize(**kw) == norm - - def test_get_default(self): - """ - Tests the `frontend.Command.get_default` method. - """ - assert 'get_default' in self.cls.__public__ # Public - no_fill = dict( - option0='value0', - option1='value1', - whatever='hello world', - ) - fill = dict( - default_from='the default', - ) - default = dict( - option0='the default', - option1='the default', - ) - sub = self.subcls() - sub.finalize() - assert sub.get_default(**no_fill) == {} - assert sub.get_default(**fill) == default - - def test_validate(self): - """ - Tests the `frontend.Command.validate` method. - """ - assert 'validate' in self.cls.__public__ # Public - - sub = self.subcls() - sub.finalize() - - # Check with valid args - okay = dict( - option0=u'option0', - option1=u'option1', - another_option='some value', - ) - sub.validate(**okay) - - # Check with an invalid arg - fail = dict(okay) - fail['option0'] = u'whatever' - e = raises(errors.RuleError, sub.validate, **fail) - assert e.name == 'option0' - assert e.value == u'whatever' - assert e.error == 'must equal option0' - assert e.rule.__class__.__name__ == 'Rule' - assert e.index is None - - # Check with a missing required arg - fail = dict(okay) - fail.pop('option1') - e = raises(errors.RequirementError, sub.validate, **fail) - assert e.name == 'option1' - assert e.value is None - assert e.index is None - - def test_execute(self): - """ - Tests the `frontend.Command.execute` method. - """ - assert 'execute' in self.cls.__public__ # Public - - def test_args_to_kw(self): - """ - Test the `frontend.Command.args_to_kw` method. - """ - assert 'args_to_kw' in self.cls.__public__ # Public - o = self.get_instance(args=('one', 'two?')) - assert o.args_to_kw(1) == dict(one=1) - assert o.args_to_kw(1, 2) == dict(one=1, two=2) - - o = self.get_instance(args=('one', 'two*')) - assert o.args_to_kw(1) == dict(one=1) - assert o.args_to_kw(1, 2) == dict(one=1, two=(2,)) - assert o.args_to_kw(1, 2, 3) == dict(one=1, two=(2, 3)) - - o = self.get_instance(args=('one', 'two+')) - assert o.args_to_kw(1) == dict(one=1) - assert o.args_to_kw(1, 2) == dict(one=1, two=(2,)) - assert o.args_to_kw(1, 2, 3) == dict(one=1, two=(2, 3)) - - o = self.get_instance() - e = raises(errors.ArgumentError, o.args_to_kw, 1) - assert str(e) == 'example takes no arguments' - - o = self.get_instance(args=('one?',)) - e = raises(errors.ArgumentError, o.args_to_kw, 1, 2) - assert str(e) == 'example takes at most 1 argument' - - o = self.get_instance(args=('one', 'two?')) - e = raises(errors.ArgumentError, o.args_to_kw, 1, 2, 3) - assert str(e) == 'example takes at most 2 arguments' - - def test_kw_to_args(self): - """ - Tests the `frontend.Command.kw_to_args` method. - """ - assert 'kw_to_args' in self.cls.__public__ # Public - o = self.get_instance(args=('one', 'two?')) - assert o.kw_to_args() == (None, None) - assert o.kw_to_args(whatever='hello') == (None, None) - assert o.kw_to_args(one='the one') == ('the one', None) - assert o.kw_to_args(two='the two') == (None, 'the two') - assert o.kw_to_args(whatever='hello', two='Two', one='One') == \ - ('One', 'Two') - - def test_run(self): - """ - Test the `frontend.Command.run` method. - """ - class my_cmd(self.cls): - def execute(self, *args, **kw): - return ('execute', args, kw) - - def forward(self, *args, **kw): - return ('forward', args, kw) - - args = ('Hello,', 'world,') - kw = dict(how_are='you', on_this='fine day?') - - # Test in server context: - api = plugable.API(self.cls) - api.env.update(dict(server_context=True)) - api.finalize() - o = my_cmd() - o.set_api(api) - assert o.run.im_func is self.cls.run.im_func - assert ('execute', args, kw) == o.run(*args, **kw) - assert o.run.im_func is my_cmd.execute.im_func - - # Test in non-server context - api = plugable.API(self.cls) - api.env.update(dict(server_context=False)) - api.finalize() - o = my_cmd() - o.set_api(api) - assert o.run.im_func is self.cls.run.im_func - assert ('forward', args, kw) == o.run(*args, **kw) - assert o.run.im_func is my_cmd.forward.im_func - - -class test_Object(ClassChecker): - """ - Test the `frontend.Object` class. - """ - _cls = frontend.Object - - def test_class(self): - assert self.cls.__bases__ == (plugable.Plugin,) - assert self.cls.backend is None - assert self.cls.methods is None - assert self.cls.properties is None - assert self.cls.params is None - assert self.cls.params_minus_pk is None - assert self.cls.takes_params == tuple() - - def test_init(self): - """ - Test the `frontend.Object.__init__` method. - """ - o = self.cls() - assert o.backend is None - assert o.methods is None - assert o.properties is None - assert o.params is None - assert o.params_minus_pk is None - assert o.properties is None - - def test_set_api(self): - """ - Test the `frontend.Object.set_api` method. - """ - # Setup for test: - class DummyAttribute(object): - def __init__(self, obj_name, attr_name, name=None): - self.obj_name = obj_name - self.attr_name = attr_name - if name is None: - self.name = '%s_%s' % (obj_name, attr_name) - else: - self.name = name - self.param = frontend.create_param(attr_name) - - def __clone__(self, attr_name): - return self.__class__( - self.obj_name, - self.attr_name, - getattr(self, attr_name) - ) - - def get_attributes(cnt, format): - for name in ['other', 'user', 'another']: - for i in xrange(cnt): - yield DummyAttribute(name, format % i) - - cnt = 10 - formats = dict( - methods='method_%d', - properties='property_%d', - ) - - - _d = dict( - Method=plugable.NameSpace( - get_attributes(cnt, formats['methods']) - ), - Property=plugable.NameSpace( - get_attributes(cnt, formats['properties']) - ), - ) - api = plugable.MagicDict(_d) - assert len(api.Method) == cnt * 3 - assert len(api.Property) == cnt * 3 - - class user(self.cls): - pass - - # Actually perform test: - o = user() - o.set_api(api) - assert read_only(o, 'api') is api - for name in ['methods', 'properties']: - namespace = getattr(o, name) - assert isinstance(namespace, plugable.NameSpace) - assert len(namespace) == cnt - f = formats[name] - for i in xrange(cnt): - attr_name = f % i - attr = namespace[attr_name] - assert isinstance(attr, DummyAttribute) - assert attr is getattr(namespace, attr_name) - assert attr.obj_name == 'user' - assert attr.attr_name == attr_name - assert attr.name == attr_name - - # Test params instance attribute - o = self.cls() - o.set_api(api) - ns = o.params - assert type(ns) is plugable.NameSpace - assert len(ns) == 0 - class example(self.cls): - takes_params = ('banana', 'apple') - o = example() - o.set_api(api) - ns = o.params - assert type(ns) is plugable.NameSpace - assert len(ns) == 2, repr(ns) - assert list(ns) == ['banana', 'apple'] - for p in ns(): - assert type(p) is frontend.Param - assert p.required is True - assert p.multivalue is False - - def test_primary_key(self): - """ - Test the `frontend.Object.primary_key` attribute. - """ - api = plugable.API( - frontend.Method, - frontend.Property, - ) - api.env.update(config.generate_env()) - api.finalize() - - # Test with no primary keys: - class example1(self.cls): - takes_params = ( - 'one', - 'two', - ) - o = example1() - o.set_api(api) - assert o.primary_key is None - assert o.params_minus_pk is None - - # Test with 1 primary key: - class example2(self.cls): - takes_params = ( - 'one', - 'two', - frontend.Param('three', - primary_key=True, - ), - 'four', - ) - o = example2() - o.set_api(api) - pk = o.primary_key - assert isinstance(pk, frontend.Param) - assert pk.name == 'three' - assert pk.primary_key is True - assert o.params[2] is o.primary_key - assert isinstance(o.params_minus_pk, plugable.NameSpace) - assert list(o.params_minus_pk) == ['one', 'two', 'four'] - - # Test with multiple primary_key: - class example3(self.cls): - takes_params = ( - frontend.Param('one', primary_key=True), - frontend.Param('two', primary_key=True), - 'three', - frontend.Param('four', primary_key=True), - ) - o = example3() - e = raises(ValueError, o.set_api, api) - assert str(e) == \ - 'example3 (Object) has multiple primary keys: one, two, four' - - def test_backend(self): - """ - Test the `frontend.Object.backend` attribute. - """ - api = plugable.API( - frontend.Object, - frontend.Method, - frontend.Property, - backend.Backend, - ) - api.env.update(config.generate_env()) - class ldap(backend.Backend): - whatever = 'It worked!' - api.register(ldap) - class user(frontend.Object): - backend_name = 'ldap' - api.register(user) - api.finalize() - b = api.Object.user.backend - assert isinstance(b, ldap) - assert b.whatever == 'It worked!' - - -class test_Attribute(ClassChecker): - """ - Tests the `frontend.Attribute` class. - """ - _cls = frontend.Attribute - - def test_class(self): - assert self.cls.__bases__ == (plugable.Plugin,) - assert type(self.cls.obj) is property - assert type(self.cls.obj_name) is property - assert type(self.cls.attr_name) is property - - def test_init(self): - """ - Tests the `frontend.Attribute.__init__` method. - """ - class user_add(self.cls): - pass - o = user_add() - assert read_only(o, 'obj') is None - assert read_only(o, 'obj_name') == 'user' - assert read_only(o, 'attr_name') == 'add' - - def test_set_api(self): - """ - Tests the `frontend.Attribute.set_api` method. - """ - user_obj = 'The user frontend.Object instance' - class api(object): - Object = dict(user=user_obj) - class user_add(self.cls): - pass - o = user_add() - assert read_only(o, 'api') is None - assert read_only(o, 'obj') is None - o.set_api(api) - assert read_only(o, 'api') is api - assert read_only(o, 'obj') is user_obj - - -class test_Method(ClassChecker): - """ - Test the `frontend.Method` class. - """ - _cls = frontend.Method - - def test_class(self): - assert self.cls.__bases__ == (frontend.Attribute, frontend.Command) - assert self.cls.implements(frontend.Command) - assert self.cls.implements(frontend.Attribute) - - def test_init(self): - """ - Test the `frontend.Method.__init__` method. - """ - class user_add(self.cls): - pass - o = user_add() - assert o.name == 'user_add' - assert o.obj_name == 'user' - assert o.attr_name == 'add' - assert frontend.Command.implemented_by(o) - assert frontend.Attribute.implemented_by(o) - - -class test_Property(ClassChecker): - """ - Tests the `frontend.Property` class. - """ - _cls = frontend.Property - - def get_subcls(self): - class user_givenname(self.cls): - 'User first name' - - @frontend.rule - def rule0_lowercase(self, value): - if not value.islower(): - return 'Must be lowercase' - return user_givenname - - def test_class(self): - assert self.cls.__bases__ == (frontend.Attribute,) - assert isinstance(self.cls.type, ipa_types.Unicode) - assert self.cls.required is False - assert self.cls.multivalue is False - assert self.cls.default is None - assert self.cls.default_from is None - assert self.cls.normalize is None - - def test_init(self): - """ - Tests the `frontend.Property.__init__` method. - """ - o = self.subcls() - assert len(o.rules) == 1 - assert o.rules[0].__name__ == 'rule0_lowercase' - param = o.param - assert isinstance(param, frontend.Param) - assert param.name == 'givenname' - assert param.doc == 'User first name' - - -class test_Application(ClassChecker): - """ - Tests the `frontend.Application` class. - """ - _cls = frontend.Application - - def test_class(self): - assert self.cls.__bases__ == (frontend.Command,) - assert type(self.cls.application) is property - - def test_application(self): - """ - Tests the `frontend.Application.application` property. - """ - assert 'application' in self.cls.__public__ # Public - assert 'set_application' in self.cls.__public__ # Public - app = 'The external application' - class example(self.cls): - 'A subclass' - for o in (self.cls(), example()): - assert read_only(o, 'application') is None - e = raises(TypeError, o.set_application, None) - assert str(e) == ( - '%s.application cannot be None' % o.__class__.__name__ - ) - o.set_application(app) - assert read_only(o, 'application') is app - e = raises(AttributeError, o.set_application, app) - assert str(e) == ( - '%s.application can only be set once' % o.__class__.__name__ - ) - assert read_only(o, 'application') is app diff --git a/ipalib/tests/test_ipa_types.py b/ipalib/tests/test_ipa_types.py deleted file mode 100644 index b8e996a7..00000000 --- a/ipalib/tests/test_ipa_types.py +++ /dev/null @@ -1,371 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.ipa_types` module. -""" - -from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker -from ipalib import ipa_types, errors, plugable - - -def test_check_min_max(): - """ - Tests the `ipa_types.check_min_max` function. - """ - f = ipa_types.check_min_max - okay = [ - (None, -5), - (-20, None), - (-20, -5), - ] - for (l, h) in okay: - assert f(l, h, 'low', 'high') is None - fail_type = [ - '10', - 10.0, - 10L, - True, - False, - object, - ] - for value in fail_type: - e = raises(TypeError, f, value, None, 'low', 'high') - assert str(e) == 'low must be an int or None, got: %r' % value - e = raises(TypeError, f, None, value, 'low', 'high') - assert str(e) == 'high must be an int or None, got: %r' % value - fail_value = [ - (10, 5), - (-5, -10), - (5, -10), - ] - for (l, h) in fail_value: - e = raises(ValueError, f, l, h, 'low', 'high') - assert str(e) == 'low > high: low=%r, high=%r' % (l, h) - - -class test_Type(ClassChecker): - """ - Tests the `ipa_types.Type` class. - """ - _cls = ipa_types.Type - - def test_class(self): - assert self.cls.__bases__ == (plugable.ReadOnly,) - - def test_init(self): - okay = (bool, int, float, unicode) - for t in okay: - o = self.cls(t) - assert o.__islocked__() is True - assert read_only(o, 'type') is t - assert read_only(o, 'name') is 'Type' - - type_errors = (None, True, 8, 8.0, u'hello') - for t in type_errors: - e = raises(TypeError, self.cls, t) - assert str(e) == '%r is not %r' % (type(t), type) - - value_errors = (long, complex, str, tuple, list, dict, set, frozenset) - for t in value_errors: - e = raises(ValueError, self.cls, t) - assert str(e) == 'not an allowed type: %r' % t - - def test_validate(self): - o = self.cls(unicode) - for value in (None, u'Hello', 'Hello', 42, False): - assert o.validate(value) is None - - -class test_Bool(ClassChecker): - _cls = ipa_types.Bool - - def test_class(self): - assert self.cls.__bases__ == (ipa_types.Type,) - - def test_init(self): - o = self.cls() - assert o.__islocked__() is True - assert read_only(o, 'type') is bool - assert read_only(o, 'name') == 'Bool' - assert read_only(o, 'true') == 'Yes' - assert read_only(o, 'false') == 'No' - - keys = ('true', 'false') - val = 'some value' - for key in keys: - # Check that kwarg sets appropriate attribute: - o = self.cls(**{key: val}) - assert read_only(o, key) is val - # Check that None raises TypeError: - e = raises(TypeError, self.cls, **{key: None}) - assert str(e) == '`%s` cannot be None' % key - - # Check that ValueError is raise if true == false: - e = raises(ValueError, self.cls, true=1L, false=1.0) - assert str(e) == 'cannot be equal: true=1L, false=1.0' - - def test_call(self): - o = self.cls() - assert o(True) is True - assert o('Yes') is True - assert o(False) is False - assert o('No') is False - for value in (0, 1, 'True', 'False', 'yes', 'no'): - # value is not be converted, so None is returned - assert o(value) is None - - -class test_Int(ClassChecker): - _cls = ipa_types.Int - - def test_class(self): - assert self.cls.__bases__ == (ipa_types.Type,) - - def test_init(self): - o = self.cls() - assert o.__islocked__() is True - assert read_only(o, 'type') is int - assert read_only(o, 'name') == 'Int' - assert read_only(o, 'min_value') is None - assert read_only(o, 'max_value') is None - - okay = [ - (None, -5), - (-20, None), - (-20, -5), - ] - for (l, h) in okay: - o = self.cls(min_value=l, max_value=h) - assert o.min_value is l - assert o.max_value is h - - fail_type = [ - '10', - 10.0, - 10L, - True, - False, - object, - ] - for value in fail_type: - e = raises(TypeError, self.cls, min_value=value) - assert str(e) == ( - 'min_value must be an int or None, got: %r' % value - ) - e = raises(TypeError, self.cls, max_value=value) - assert str(e) == ( - 'max_value must be an int or None, got: %r' % value - ) - - fail_value = [ - (10, 5), - (5, -5), - (-5, -10), - ] - for (l, h) in fail_value: - e = raises(ValueError, self.cls, min_value=l, max_value=h) - assert str(e) == ( - 'min_value > max_value: min_value=%d, max_value=%d' % (l, h) - ) - - def test_call(self): - o = self.cls() - - # Test calling with None - e = raises(TypeError, o, None) - assert str(e) == 'value cannot be None' - - # Test with values that can be converted: - okay = [ - 3, - '3', - ' 3 ', - 3L, - 3.0, - ] - for value in okay: - assert o(value) == 3 - - # Test with values that cannot be converted: - fail = [ - object, - '3.0', - '3L', - 'whatever', - ] - for value in fail: - assert o(value) is None - - - def test_validate(self): - o = self.cls(min_value=2, max_value=7) - assert o.validate(2) is None - assert o.validate(5) is None - assert o.validate(7) is None - assert o.validate(1) == 'Cannot be smaller than 2' - assert o.validate(8) == 'Cannot be larger than 7' - for val in ['5', 5.0, 5L, None, True, False, object]: - assert o.validate(val) == 'Must be an integer' - - -class test_Unicode(ClassChecker): - _cls = ipa_types.Unicode - - def test_class(self): - assert self.cls.__bases__ == (ipa_types.Type,) - - def test_init(self): - o = self.cls() - assert o.__islocked__() is True - assert read_only(o, 'type') is unicode - assert read_only(o, 'name') == 'Unicode' - assert read_only(o, 'min_length') is None - assert read_only(o, 'max_length') is None - assert read_only(o, 'pattern') is None - assert read_only(o, 'regex') is None - - # Test min_length, max_length: - okay = ( - (0, 1), - (8, 8), - ) - for (l, h) in okay: - o = self.cls(min_length=l, max_length=h) - assert o.min_length == l - assert o.max_length == h - - fail_type = [ - '10', - 10.0, - 10L, - True, - False, - object, - ] - for value in fail_type: - e = raises(TypeError, self.cls, min_length=value) - assert str(e) == ( - 'min_length must be an int or None, got: %r' % value - ) - e = raises(TypeError, self.cls, max_length=value) - assert str(e) == ( - 'max_length must be an int or None, got: %r' % value - ) - - fail_value = [ - (10, 5), - (5, -5), - (0, -10), - ] - for (l, h) in fail_value: - e = raises(ValueError, self.cls, min_length=l, max_length=h) - assert str(e) == ( - 'min_length > max_length: min_length=%d, max_length=%d' % (l, h) - ) - - for (key, lower) in [('min_length', 0), ('max_length', 1)]: - value = lower - 1 - kw = {key: value} - e = raises(ValueError, self.cls, **kw) - assert str(e) == '%s must be >= %d, got: %d' % (key, lower, value) - - # Test pattern: - okay = [ - '(hello|world)', - u'(take the blue pill|take the red pill)', - ] - for value in okay: - o = self.cls(pattern=value) - assert o.pattern is value - assert o.regex is not None - - fail = [ - 42, - True, - False, - object, - ] - for value in fail: - e = raises(TypeError, self.cls, pattern=value) - assert str(e) == ( - 'pattern must be a basestring or None, got: %r' % value - ) - - # Test regex: - pat = '^(hello|world)$' - o = self.cls(pattern=pat) - for value in ('hello', 'world'): - m = o.regex.match(value) - assert m.group(1) == value - for value in ('hello beautiful', 'world!'): - assert o.regex.match(value) is None - - def test_validate(self): - pat = '^a_*b$' - o = self.cls(min_length=3, max_length=4, pattern=pat) - assert o.validate(u'a_b') is None - assert o.validate(u'a__b') is None - assert o.validate('a_b') == 'Must be a string' - assert o.validate(u'ab') == 'Must be at least 3 characters long' - assert o.validate(u'a___b') == 'Can be at most 4 characters long' - assert o.validate(u'a-b') == 'Must match %r' % pat - assert o.validate(u'a--b') == 'Must match %r' % pat - - -class test_Enum(ClassChecker): - _cls = ipa_types.Enum - - def test_class(self): - assert self.cls.__bases__ == (ipa_types.Type,) - - def test_init(self): - for t in (unicode, int, float): - values = (t(1), t(2), t(3)) - o = self.cls(*values) - assert o.__islocked__() is True - assert read_only(o, 'type') is t - assert read_only(o, 'name') is 'Enum' - assert read_only(o, 'values') == values - assert read_only(o, 'frozenset') == frozenset(values) - - # Check that ValueError is raised when no values are given: - e = raises(ValueError, self.cls) - assert str(e) == 'Enum requires at least one value' - - # Check that TypeError is raised when type of first value is not - # allowed: - e = raises(TypeError, self.cls, 'hello') - assert str(e) == '%r: %r not unicode, int, nor float' % ('hello', str) - #self.cls('hello') - - # Check that TypeError is raised when subsequent values aren't same - # type as first: - e = raises(TypeError, self.cls, u'hello', 'world') - assert str(e) == '%r: %r is not %r' % ('world', str, unicode) - - def test_validate(self): - values = (u'hello', u'naughty', u'nurse') - o = self.cls(*values) - for value in values: - assert o.validate(value) is None - assert o.validate(str(value)) == 'Incorrect type' - for value in (u'one fish', u'two fish'): - assert o.validate(value) == 'Invalid value' - assert o.validate(str(value)) == 'Incorrect type' diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py deleted file mode 100644 index fd3c3c88..00000000 --- a/ipalib/tests/test_plugable.py +++ /dev/null @@ -1,896 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.plugable` module. -""" - -from tstutil import raises, no_set, no_del, read_only -from tstutil import getitem, setitem, delitem -from tstutil import ClassChecker -from ipalib import plugable, errors - - -class test_ReadOnly(ClassChecker): - """ - Test the `plugable.ReadOnly` class - """ - _cls = plugable.ReadOnly - - def test_class(self): - assert self.cls.__bases__ == (object,) - assert callable(self.cls.__lock__) - assert callable(self.cls.__islocked__) - - def test_lock(self): - """ - Test the `plugable.ReadOnly.__lock__` method. - """ - o = self.cls() - assert o._ReadOnly__locked is False - o.__lock__() - assert o._ReadOnly__locked is True - e = raises(AssertionError, o.__lock__) # Can only be locked once - assert str(e) == '__lock__() can only be called once' - assert o._ReadOnly__locked is True # This should still be True - - def test_lock(self): - """ - Test the `plugable.ReadOnly.__islocked__` method. - """ - o = self.cls() - assert o.__islocked__() is False - o.__lock__() - assert o.__islocked__() is True - - def test_setattr(self): - """ - Test the `plugable.ReadOnly.__setattr__` method. - """ - o = self.cls() - o.attr1 = 'Hello, world!' - assert o.attr1 == 'Hello, world!' - o.__lock__() - for name in ('attr1', 'attr2'): - e = raises(AttributeError, setattr, o, name, 'whatever') - assert str(e) == 'read-only: cannot set ReadOnly.%s' % name - assert o.attr1 == 'Hello, world!' - - def test_delattr(self): - """ - Test the `plugable.ReadOnly.__delattr__` method. - """ - o = self.cls() - o.attr1 = 'Hello, world!' - o.attr2 = 'How are you?' - assert o.attr1 == 'Hello, world!' - assert o.attr2 == 'How are you?' - del o.attr1 - assert not hasattr(o, 'attr1') - o.__lock__() - e = raises(AttributeError, delattr, o, 'attr2') - assert str(e) == 'read-only: cannot del ReadOnly.attr2' - assert o.attr2 == 'How are you?' - - -def test_lock(): - """ - Tests the `plugable.lock` function. - """ - f = plugable.lock - - # Test on a ReadOnly instance: - o = plugable.ReadOnly() - assert not o.__islocked__() - assert f(o) is o - assert o.__islocked__() - - # Test on something not subclassed from ReadOnly: - class not_subclass(object): - def __lock__(self): - pass - def __islocked__(self): - return True - o = not_subclass() - raises(ValueError, f, o) - - # Test that it checks __islocked__(): - class subclass(plugable.ReadOnly): - def __islocked__(self): - return False - o = subclass() - raises(AssertionError, f, o) - - -class test_SetProxy(ClassChecker): - """ - Tests the `plugable.SetProxy` class. - """ - _cls = plugable.SetProxy - - def test_class(self): - assert self.cls.__bases__ == (plugable.ReadOnly,) - - def test_init(self): - okay = (set, frozenset, dict) - fail = (list, tuple) - for t in okay: - self.cls(t()) - raises(TypeError, self.cls, t) - for t in fail: - raises(TypeError, self.cls, t()) - raises(TypeError, self.cls, t) - - def test_SetProxy(self): - def get_key(i): - return 'key_%d' % i - - cnt = 10 - target = set() - proxy = self.cls(target) - for i in xrange(cnt): - key = get_key(i) - - # Check initial state - assert len(proxy) == len(target) - assert list(proxy) == sorted(target) - assert key not in proxy - assert key not in target - - # Add and test again - target.add(key) - assert len(proxy) == len(target) - assert list(proxy) == sorted(target) - assert key in proxy - assert key in target - - -class test_DictProxy(ClassChecker): - """ - Tests the `plugable.DictProxy` class. - """ - _cls = plugable.DictProxy - - def test_class(self): - assert self.cls.__bases__ == (plugable.SetProxy,) - - def test_init(self): - self.cls(dict()) - raises(TypeError, self.cls, dict) - fail = (set, frozenset, list, tuple) - for t in fail: - raises(TypeError, self.cls, t()) - raises(TypeError, self.cls, t) - - def test_DictProxy(self): - def get_kv(i): - return ( - 'key_%d' % i, - 'val_%d' % i, - ) - cnt = 10 - target = dict() - proxy = self.cls(target) - for i in xrange(cnt): - (key, val) = get_kv(i) - - # Check initial state - assert len(proxy) == len(target) - assert list(proxy) == sorted(target) - assert list(proxy()) == [target[k] for k in sorted(target)] - assert key not in proxy - raises(KeyError, getitem, proxy, key) - - # Add and test again - target[key] = val - assert len(proxy) == len(target) - assert list(proxy) == sorted(target) - assert list(proxy()) == [target[k] for k in sorted(target)] - - # Verify TypeError is raised trying to set/del via proxy - raises(TypeError, setitem, proxy, key, val) - raises(TypeError, delitem, proxy, key) - - -class test_MagicDict(ClassChecker): - """ - Tests the `plugable.MagicDict` class. - """ - _cls = plugable.MagicDict - - def test_class(self): - assert self.cls.__bases__ == (plugable.DictProxy,) - for non_dict in ('hello', 69, object): - raises(TypeError, self.cls, non_dict) - - def test_MagicDict(self): - cnt = 10 - keys = [] - d = dict() - dictproxy = self.cls(d) - for i in xrange(cnt): - key = 'key_%d' % i - val = 'val_%d' % i - keys.append(key) - - # Test thet key does not yet exist - assert len(dictproxy) == i - assert key not in dictproxy - assert not hasattr(dictproxy, key) - raises(KeyError, getitem, dictproxy, key) - raises(AttributeError, getattr, dictproxy, key) - - # Test that items/attributes cannot be set on dictproxy: - raises(TypeError, setitem, dictproxy, key, val) - raises(AttributeError, setattr, dictproxy, key, val) - - # Test that additions in d are reflected in dictproxy: - d[key] = val - assert len(dictproxy) == i + 1 - assert key in dictproxy - assert hasattr(dictproxy, key) - assert dictproxy[key] is val - assert read_only(dictproxy, key) is val - - # Test __iter__ - assert list(dictproxy) == keys - - for key in keys: - # Test that items cannot be deleted through dictproxy: - raises(TypeError, delitem, dictproxy, key) - raises(AttributeError, delattr, dictproxy, key) - - # Test that deletions in d are reflected in dictproxy - del d[key] - assert len(dictproxy) == len(d) - assert key not in dictproxy - raises(KeyError, getitem, dictproxy, key) - raises(AttributeError, getattr, dictproxy, key) - - -class test_Plugin(ClassChecker): - """ - Tests the `plugable.Plugin` class. - """ - _cls = plugable.Plugin - - def test_class(self): - assert self.cls.__bases__ == (plugable.ReadOnly,) - assert self.cls.__public__ == frozenset() - assert type(self.cls.name) is property - assert type(self.cls.doc) is property - assert type(self.cls.api) is property - - def test_name(self): - """ - Tests the `plugable.Plugin.name` property. - """ - assert read_only(self.cls(), 'name') == 'Plugin' - - class some_subclass(self.cls): - pass - assert read_only(some_subclass(), 'name') == 'some_subclass' - - def test_doc(self): - """ - Tests the `plugable.Plugin.doc` property. - """ - class some_subclass(self.cls): - 'here is the doc string' - assert read_only(some_subclass(), 'doc') == 'here is the doc string' - - def test_implements(self): - """ - Tests the `plugable.Plugin.implements` classmethod. - """ - class example(self.cls): - __public__ = frozenset(( - 'some_method', - 'some_property', - )) - class superset(self.cls): - __public__ = frozenset(( - 'some_method', - 'some_property', - 'another_property', - )) - class subset(self.cls): - __public__ = frozenset(( - 'some_property', - )) - class any_object(object): - __public__ = frozenset(( - 'some_method', - 'some_property', - )) - - for ex in (example, example()): - # Test using str: - assert ex.implements('some_method') - assert not ex.implements('another_method') - - # Test using frozenset: - assert ex.implements(frozenset(['some_method'])) - assert not ex.implements( - frozenset(['some_method', 'another_method']) - ) - - # Test using another object/class with __public__ frozenset: - assert ex.implements(example) - assert ex.implements(example()) - - assert ex.implements(subset) - assert not subset.implements(ex) - - assert not ex.implements(superset) - assert superset.implements(ex) - - assert ex.implements(any_object) - assert ex.implements(any_object()) - - def test_implemented_by(self): - """ - Tests the `plugable.Plugin.implemented_by` classmethod. - """ - class base(self.cls): - __public__ = frozenset(( - 'attr0', - 'attr1', - 'attr2', - )) - - class okay(base): - def attr0(self): - pass - def __get_attr1(self): - assert False # Make sure property isn't accesed on instance - attr1 = property(__get_attr1) - attr2 = 'hello world' - another_attr = 'whatever' - - class fail(base): - def __init__(self): - # Check that class, not instance is inspected: - self.attr2 = 'hello world' - def attr0(self): - pass - def __get_attr1(self): - assert False # Make sure property isn't accesed on instance - attr1 = property(__get_attr1) - another_attr = 'whatever' - - # Test that AssertionError is raised trying to pass something not - # subclass nor instance of base: - raises(AssertionError, base.implemented_by, object) - - # Test on subclass with needed attributes: - assert base.implemented_by(okay) is True - assert base.implemented_by(okay()) is True - - # Test on subclass *without* needed attributes: - assert base.implemented_by(fail) is False - assert base.implemented_by(fail()) is False - - def test_set_api(self): - """ - Tests the `plugable.Plugin.set_api` method. - """ - api = 'the api instance' - o = self.cls() - assert o.api is None - e = raises(AssertionError, o.set_api, None) - assert str(e) == 'set_api() argument cannot be None' - o.set_api(api) - assert o.api is api - e = raises(AssertionError, o.set_api, api) - assert str(e) == 'set_api() can only be called once' - - def test_finalize(self): - """ - Tests the `plugable.Plugin.finalize` method. - """ - o = self.cls() - assert not o.__islocked__() - o.finalize() - assert o.__islocked__() - - -class test_PluginProxy(ClassChecker): - """ - Tests the `plugable.PluginProxy` class. - """ - _cls = plugable.PluginProxy - - def test_class(self): - assert self.cls.__bases__ == (plugable.SetProxy,) - - def test_proxy(self): - # Setup: - class base(object): - __public__ = frozenset(( - 'public_0', - 'public_1', - '__call__', - )) - - def public_0(self): - return 'public_0' - - def public_1(self): - return 'public_1' - - def __call__(self, caller): - return 'ya called it, %s.' % caller - - def private_0(self): - return 'private_0' - - def private_1(self): - return 'private_1' - - class plugin(base): - name = 'user_add' - attr_name = 'add' - doc = 'add a new user' - - # Test that TypeError is raised when base is not a class: - raises(TypeError, self.cls, base(), None) - - # Test that ValueError is raised when target is not instance of base: - raises(ValueError, self.cls, base, object()) - - # Test with correct arguments: - i = plugin() - p = self.cls(base, i) - assert read_only(p, 'name') is plugin.name - assert read_only(p, 'doc') == plugin.doc - assert list(p) == sorted(base.__public__) - - # Test normal methods: - for n in xrange(2): - pub = 'public_%d' % n - priv = 'private_%d' % n - assert getattr(i, pub)() == pub - assert getattr(p, pub)() == pub - assert hasattr(p, pub) - assert getattr(i, priv)() == priv - assert not hasattr(p, priv) - - # Test __call__: - value = 'ya called it, dude.' - assert i('dude') == value - assert p('dude') == value - assert callable(p) - - # Test name_attr='name' kw arg - i = plugin() - p = self.cls(base, i, 'attr_name') - assert read_only(p, 'name') == 'add' - - def test_implements(self): - """ - Tests the `plugable.PluginProxy.implements` method. - """ - class base(object): - __public__ = frozenset() - name = 'base' - doc = 'doc' - @classmethod - def implements(cls, arg): - return arg + 7 - - class sub(base): - @classmethod - def implements(cls, arg): - """ - Defined to make sure base.implements() is called, not - target.implements() - """ - return arg - - o = sub() - p = self.cls(base, o) - assert p.implements(3) == 10 - - def test_clone(self): - """ - Tests the `plugable.PluginProxy.__clone__` method. - """ - class base(object): - __public__ = frozenset() - class sub(base): - name = 'some_name' - doc = 'doc' - label = 'another_name' - - p = self.cls(base, sub()) - assert read_only(p, 'name') == 'some_name' - c = p.__clone__('label') - assert isinstance(c, self.cls) - assert c is not p - assert read_only(c, 'name') == 'another_name' - - -def test_check_name(): - """ - Tests the `plugable.check_name` function. - """ - f = plugable.check_name - okay = [ - 'user_add', - 'stuff2junk', - 'sixty9', - ] - nope = [ - '_user_add', - '__user_add', - 'user_add_', - 'user_add__', - '_user_add_', - '__user_add__', - '60nine', - ] - for name in okay: - assert name is f(name) - e = raises(TypeError, f, unicode(name)) - assert str(e) == errors.TYPE_FORMAT % ('name', str, unicode(name)) - for name in nope: - raises(errors.NameSpaceError, f, name) - for name in okay: - raises(errors.NameSpaceError, f, name.upper()) - -class DummyMember(object): - def __init__(self, i): - assert type(i) is int - self.name = 'member_%02d' % i - - -class test_NameSpace(ClassChecker): - """ - Tests the `plugable.NameSpace` class. - """ - _cls = plugable.NameSpace - - def test_class(self): - assert self.cls.__bases__ == (plugable.ReadOnly,) - - def test_init(self): - """ - Tests the `plugable.NameSpace.__init__` method. - """ - o = self.cls(tuple()) - assert list(o) == [] - assert list(o()) == [] - for cnt in (10, 25): - members = tuple(DummyMember(cnt - i) for i in xrange(cnt)) - for sort in (True, False): - o = self.cls(members, sort=sort) - if sort: - ordered = tuple(sorted(members, key=lambda m: m.name)) - else: - ordered = members - names = tuple(m.name for m in ordered) - assert o.__todict__() == dict((o.name, o) for o in ordered) - - # Test __len__: - assert len(o) == cnt - - # Test __contains__: - for name in names: - assert name in o - assert ('member_00') not in o - - # Test __iter__, __call__: - assert tuple(o) == names - assert tuple(o()) == ordered - - # Test __getitem__, getattr: - for (i, member) in enumerate(ordered): - assert o[i] is member - name = member.name - assert o[name] is member - assert read_only(o, name) is member - - # Test negative indexes: - for i in xrange(1, cnt + 1): - assert o[-i] is ordered[-i] - - # Test slices: - assert o[2:cnt-5] == ordered[2:cnt-5] - assert o[::3] == ordered[::3] - - # Test __repr__: - assert repr(o) == \ - 'NameSpace(<%d members>, sort=%r)' % (cnt, sort) - - -def test_Environment(): - """ - Tests the `plugable.Environment` class. - """ - # This has to be the same as iter_cnt - control_cnt = 0 - class prop_class: - def __init__(self, val): - self._val = val - def get_value(self): - return self._val - - class iter_class(prop_class): - # Increment this for each time iter_class yields - iter_cnt = 0 - def get_value(self): - for item in self._val: - self.__class__.iter_cnt += 1 - yield item - - # Tests for basic functionality - basic_tests = ( - ('a', 1), - ('b', 'basic_foo'), - ('c', ('basic_bar', 'basic_baz')), - ) - # Tests with prop classes - prop_tests = ( - ('d', prop_class(2), 2), - ('e', prop_class('prop_foo'), 'prop_foo'), - ('f', prop_class(('prop_bar', 'prop_baz')), ('prop_bar', 'prop_baz')), - ) - # Tests with iter classes - iter_tests = ( - ('g', iter_class((3, 4, 5)), (3, 4, 5)), - ('h', iter_class(('iter_foo', 'iter_bar', 'iter_baz')), - ('iter_foo', 'iter_bar', 'iter_baz') - ), - ) - - # Set all the values - env = plugable.Environment() - for name, val in basic_tests: - env[name] = val - for name, val, dummy in prop_tests: - env[name] = val - for name, val, dummy in iter_tests: - env[name] = val - - # Test if the values are correct - for name, val in basic_tests: - assert env[name] == val - for name, dummy, val in prop_tests: - assert env[name] == val - # Test if the get_value() function is called only when needed - for name, dummy, correct_values in iter_tests: - values_in_env = [] - for val in env[name]: - control_cnt += 1 - assert iter_class.iter_cnt == control_cnt - values_in_env.append(val) - assert tuple(values_in_env) == correct_values - - # Test __setattr__() - env.spam = 'ham' - assert env.spam == 'ham' - - # Test if we throw AttributeError exception when trying to overwrite - # existing value, or delete it - raises(AttributeError, setitem, env, 'a', 1) - raises(AttributeError, setattr, env, 'a', 1) - raises(AttributeError, delitem, env, 'a') - raises(AttributeError, delattr, env, 'a') - raises(AttributeError, plugable.Environment.update, env, dict(a=1000)) - # This should be silently ignored - env.update(dict(a=1000), True) - assert env.a != 1000 - -def test_Registrar(): - class Base1(object): - pass - class Base2(object): - pass - class Base3(object): - pass - class plugin1(Base1): - pass - class plugin2(Base2): - pass - class plugin3(Base3): - pass - - # Test creation of Registrar: - r = plugable.Registrar(Base1, Base2) - - # Test __iter__: - assert list(r) == ['Base1', 'Base2'] - - # Test __hasitem__, __getitem__: - for base in [Base1, Base2]: - name = base.__name__ - assert name in r - assert r[name] is base - magic = getattr(r, name) - assert type(magic) is plugable.MagicDict - assert len(magic) == 0 - - # Check that TypeError is raised trying to register something that isn't - # a class: - raises(TypeError, r, plugin1()) - - # Check that SubclassError is raised trying to register a class that is - # not a subclass of an allowed base: - raises(errors.SubclassError, r, plugin3) - - # Check that registration works - r(plugin1) - assert len(r.Base1) == 1 - assert r.Base1['plugin1'] is plugin1 - assert r.Base1.plugin1 is plugin1 - - # Check that DuplicateError is raised trying to register exact class - # again: - raises(errors.DuplicateError, r, plugin1) - - # Check that OverrideError is raised trying to register class with same - # name and same base: - orig1 = plugin1 - class base1_extended(Base1): - pass - class plugin1(base1_extended): - pass - raises(errors.OverrideError, r, plugin1) - - # Check that overriding works - r(plugin1, override=True) - assert len(r.Base1) == 1 - assert r.Base1.plugin1 is plugin1 - assert r.Base1.plugin1 is not orig1 - - # Check that MissingOverrideError is raised trying to override a name - # not yet registerd: - raises(errors.MissingOverrideError, r, plugin2, override=True) - - # Test that another plugin can be registered: - assert len(r.Base2) == 0 - r(plugin2) - assert len(r.Base2) == 1 - assert r.Base2.plugin2 is plugin2 - - # Setup to test more registration: - class plugin1a(Base1): - pass - r(plugin1a) - - class plugin1b(Base1): - pass - r(plugin1b) - - class plugin2a(Base2): - pass - r(plugin2a) - - class plugin2b(Base2): - pass - r(plugin2b) - - # Again test __hasitem__, __getitem__: - for base in [Base1, Base2]: - name = base.__name__ - assert name in r - assert r[name] is base - magic = getattr(r, name) - assert len(magic) == 3 - for key in magic: - klass = magic[key] - assert getattr(magic, key) is klass - assert issubclass(klass, base) - - - -def test_API(): - assert issubclass(plugable.API, plugable.ReadOnly) - - # Setup the test bases, create the API: - class base0(plugable.Plugin): - __public__ = frozenset(( - 'method', - )) - - def method(self, n): - return n - - class base1(plugable.Plugin): - __public__ = frozenset(( - 'method', - )) - - def method(self, n): - return n + 1 - - api = plugable.API(base0, base1) - r = api.register - assert isinstance(r, plugable.Registrar) - assert read_only(api, 'register') is r - - class base0_plugin0(base0): - pass - r(base0_plugin0) - - class base0_plugin1(base0): - pass - r(base0_plugin1) - - class base0_plugin2(base0): - pass - r(base0_plugin2) - - class base1_plugin0(base1): - pass - r(base1_plugin0) - - class base1_plugin1(base1): - pass - r(base1_plugin1) - - class base1_plugin2(base1): - pass - r(base1_plugin2) - - # Test API instance: - api.finalize() - - def get_base(b): - return 'base%d' % b - - def get_plugin(b, p): - return 'base%d_plugin%d' % (b, p) - - for b in xrange(2): - base_name = get_base(b) - ns = getattr(api, base_name) - assert isinstance(ns, plugable.NameSpace) - assert read_only(api, base_name) is ns - assert len(ns) == 3 - for p in xrange(3): - plugin_name = get_plugin(b, p) - proxy = ns[plugin_name] - assert isinstance(proxy, plugable.PluginProxy) - assert proxy.name == plugin_name - assert read_only(ns, plugin_name) is proxy - assert read_only(proxy, 'method')(7) == 7 + b - - # Test that calling finilize again raises AssertionError: - raises(AssertionError, api.finalize) - - # Test with base class that doesn't request a proxy - class NoProxy(plugable.Plugin): - __proxy__ = False - api = plugable.API(NoProxy) - class plugin0(NoProxy): - pass - api.register(plugin0) - class plugin1(NoProxy): - pass - api.register(plugin1) - api.finalize() - names = ['plugin0', 'plugin1'] - assert list(api.NoProxy) == names - for name in names: - plugin = api.NoProxy[name] - assert getattr(api.NoProxy, name) is plugin - assert isinstance(plugin, plugable.Plugin) - assert not isinstance(plugin, plugable.PluginProxy) diff --git a/ipalib/tests/test_tstutil.py b/ipalib/tests/test_tstutil.py deleted file mode 100644 index 5916f9d2..00000000 --- a/ipalib/tests/test_tstutil.py +++ /dev/null @@ -1,148 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for test-helper `tests.tstutil` module. -""" - -import tstutil - - -class Prop(object): - def __init__(self, *ops): - self.__ops = frozenset(ops) - self.__prop = 'prop value' - - def __get_prop(self): - if 'get' not in self.__ops: - raise AttributeError('get prop') - return self.__prop - - def __set_prop(self, value): - if 'set' not in self.__ops: - raise AttributeError('set prop') - self.__prop = value - - def __del_prop(self): - if 'del' not in self.__ops: - raise AttributeError('del prop') - self.__prop = None - - prop = property(__get_prop, __set_prop, __del_prop) - - -def test_yes_raised(): - f = tstutil.raises - - class SomeError(Exception): - pass - - class AnotherError(Exception): - pass - - def callback1(): - 'raises correct exception' - raise SomeError() - - def callback2(): - 'raises wrong exception' - raise AnotherError() - - def callback3(): - 'raises no exception' - - f(SomeError, callback1) - - raised = False - try: - f(SomeError, callback2) - except AnotherError: - raised = True - assert raised - - raised = False - try: - f(SomeError, callback3) - except tstutil.ExceptionNotRaised: - raised = True - assert raised - - -def test_no_set(): - # Tests that it works when prop cannot be set: - tstutil.no_set(Prop('get', 'del'), 'prop') - - # Tests that ExceptionNotRaised is raised when prop *can* be set: - raised = False - try: - tstutil.no_set(Prop('set'), 'prop') - except tstutil.ExceptionNotRaised: - raised = True - assert raised - - -def test_no_del(): - # Tests that it works when prop cannot be deleted: - tstutil.no_del(Prop('get', 'set'), 'prop') - - # Tests that ExceptionNotRaised is raised when prop *can* be set: - raised = False - try: - tstutil.no_del(Prop('del'), 'prop') - except tstutil.ExceptionNotRaised: - raised = True - assert raised - - -def test_read_only(): - # Test that it works when prop is read only: - assert tstutil.read_only(Prop('get'), 'prop') == 'prop value' - - # Test that ExceptionNotRaised is raised when prop can be set: - raised = False - try: - tstutil.read_only(Prop('get', 'set'), 'prop') - except tstutil.ExceptionNotRaised: - raised = True - assert raised - - # Test that ExceptionNotRaised is raised when prop can be deleted: - raised = False - try: - tstutil.read_only(Prop('get', 'del'), 'prop') - except tstutil.ExceptionNotRaised: - raised = True - assert raised - - # Test that ExceptionNotRaised is raised when prop can be both set and - # deleted: - raised = False - try: - tstutil.read_only(Prop('get', 'del'), 'prop') - except tstutil.ExceptionNotRaised: - raised = True - assert raised - - # Test that AttributeError is raised when prop can't be read: - raised = False - try: - tstutil.read_only(Prop(), 'prop') - except AttributeError: - raised = True - assert raised diff --git a/ipalib/tests/test_util.py b/ipalib/tests/test_util.py deleted file mode 100644 index f8ee0bf4..00000000 --- a/ipalib/tests/test_util.py +++ /dev/null @@ -1,49 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Unit tests for `ipalib.util` module. -""" - -from tstutil import raises -from ipalib import util - - -def test_xmlrpc_marshal(): - """ - Test the `util.xmlrpc_marshal` function. - """ - f = util.xmlrpc_marshal - assert f() == ({},) - assert f('one', 'two') == ({}, 'one', 'two') - assert f(one=1, two=2) == (dict(one=1, two=2),) - assert f('one', 'two', three=3, four=4) == \ - (dict(three=3, four=4), 'one', 'two') - - -def test_xmlrpc_unmarshal(): - """ - Test the `util.xmlrpc_unmarshal` function. - """ - f = util.xmlrpc_unmarshal - assert f() == (tuple(), {}) - assert f({}, 'one', 'two') == (('one', 'two'), {}) - assert f(dict(one=1, two=2)) == (tuple(), dict(one=1, two=2)) - assert f(dict(three=3, four=4), 'one', 'two') == \ - (('one', 'two'), dict(three=3, four=4)) diff --git a/ipalib/tests/tstutil.py b/ipalib/tests/tstutil.py deleted file mode 100644 index 743716a0..00000000 --- a/ipalib/tests/tstutil.py +++ /dev/null @@ -1,147 +0,0 @@ -# Authors: -# Jason Gerard DeRose -# -# Copyright (C) 2008 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 only -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -Utility functions for the unit tests. -""" - -import inspect -from ipalib import errors - -class ExceptionNotRaised(Exception): - """ - Exception raised when an *expected* exception is *not* raised during a - unit test. - """ - msg = 'expected %s' - - def __init__(self, expected): - self.expected = expected - - def __str__(self): - return self.msg % self.expected.__name__ - - -def raises(exception, callback, *args, **kw): - """ - Tests that the expected exception is raised; raises ExceptionNotRaised - if test fails. - """ - raised = False - try: - callback(*args, **kw) - except exception, e: - raised = True - if not raised: - raise ExceptionNotRaised(exception) - return e - - -def getitem(obj, key): - """ - Works like getattr but for dictionary interface. Use this in combination - with raises() to test that, for example, KeyError is raised. - """ - return obj[key] - - -def setitem(obj, key, value): - """ - Works like setattr but for dictionary interface. Use this in combination - with raises() to test that, for example, TypeError is raised. - """ - obj[key] = value - - -def delitem(obj, key): - """ - Works like delattr but for dictionary interface. Use this in combination - with raises() to test that, for example, TypeError is raised. - """ - del obj[key] - - -def no_set(obj, name, value='some_new_obj'): - """ - Tests that attribute cannot be set. - """ - raises(AttributeError, setattr, obj, name, value) - - -def no_del(obj, name): - """ - Tests that attribute cannot be deleted. - """ - raises(AttributeError, delattr, obj, name) - - -def read_only(obj, name, value='some_new_obj'): - """ - Tests that attribute is read-only. Returns attribute. - """ - # Test that it cannot be set: - no_set(obj, name, value) - - # Test that it cannot be deleted: - no_del(obj, name) - - # Return the attribute - return getattr(obj, name) - - -def is_prop(prop): - return type(prop) is property - - -class ClassChecker(object): - __cls = None - __subcls = None - - def __get_cls(self): - if self.__cls is None: - self.__cls = self._cls - assert inspect.isclass(self.__cls) - return self.__cls - cls = property(__get_cls) - - def __get_subcls(self): - if self.__subcls is None: - self.__subcls = self.get_subcls() - assert inspect.isclass(self.__subcls) - return self.__subcls - subcls = property(__get_subcls) - - def get_subcls(self): - raise NotImplementedError( - self.__class__.__name__, - 'get_subcls()' - ) - - -def check_TypeError(value, type_, name, callback, *args, **kw): - """ - Tests a standard TypeError raised with `errors.raise_TypeError`. - """ - e = raises(TypeError, callback, *args, **kw) - assert e.value is value - assert e.type is type_ - assert e.name == name - assert type(e.name) is str - assert str(e) == errors.TYPE_FORMAT % (name, type_, value) - return e diff --git a/tests/test_ipalib/__init__.py b/tests/test_ipalib/__init__.py new file mode 100644 index 00000000..d3658c45 --- /dev/null +++ b/tests/test_ipalib/__init__.py @@ -0,0 +1,22 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib` package. +""" diff --git a/tests/test_ipalib/test_backend.py b/tests/test_ipalib/test_backend.py new file mode 100644 index 00000000..967e9fdf --- /dev/null +++ b/tests/test_ipalib/test_backend.py @@ -0,0 +1,37 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.backend` module. +""" + +from ipalib import backend, plugable, errors +from tstutil import ClassChecker + + +class test_Backend(ClassChecker): + """ + Test the `backend.Backend` class. + """ + + _cls = backend.Backend + + def test_class(self): + assert self.cls.__bases__ == (plugable.Plugin,) + assert self.cls.__proxy__ is False diff --git a/tests/test_ipalib/test_cli.py b/tests/test_ipalib/test_cli.py new file mode 100644 index 00000000..90c66d41 --- /dev/null +++ b/tests/test_ipalib/test_cli.py @@ -0,0 +1,138 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.cli` module. +""" + +from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker +from ipalib import cli, plugable + + +def test_to_cli(): + """ + Tests the `cli.to_cli` function. + """ + f = cli.to_cli + assert f('initialize') == 'initialize' + assert f('user_add') == 'user-add' + + +def test_from_cli(): + """ + Tests the `cli.from_cli` function. + """ + f = cli.from_cli + assert f('initialize') == 'initialize' + assert f('user-add') == 'user_add' + + +def get_cmd_name(i): + return 'cmd_%d' % i + +class DummyCommand(object): + def __init__(self, name): + self.__name = name + + def __get_name(self): + return self.__name + name = property(__get_name) + +class DummyAPI(object): + def __init__(self, cnt): + self.__cmd = plugable.NameSpace(self.__cmd_iter(cnt)) + + def __get_cmd(self): + return self.__cmd + Command = property(__get_cmd) + + def __cmd_iter(self, cnt): + for i in xrange(cnt): + yield DummyCommand(get_cmd_name(i)) + + def finalize(self): + pass + + def register(self, *args, **kw): + pass + + + + + +class test_CLI(ClassChecker): + """ + Tests the `cli.CLI` class. + """ + _cls = cli.CLI + + def test_class(self): + assert type(self.cls.api) is property + + def test_api(self): + """ + Tests the `cli.CLI.api` property. + """ + api = 'the plugable.API instance' + o = self.cls(api) + assert read_only(o, 'api') is api + + def dont_parse(self): + """ + Tests the `cli.CLI.parse` method. + """ + o = self.cls(None) + args = ['hello', 'naughty', 'nurse'] + kw = dict( + first_name='Naughty', + last_name='Nurse', + ) + opts = ['--%s=%s' % (k.replace('_', '-'), v) for (k, v) in kw.items()] + assert o.parse(args + []) == (args, {}) + assert o.parse(opts + []) == ([], kw) + assert o.parse(args + opts) == (args, kw) + assert o.parse(opts + args) == (args, kw) + + def test_mcl(self): + """ + Tests the `cli.CLI.mcl` (Max Command Length) property . + """ + cnt = 100 + api = DummyAPI(cnt) + len(api.Command) == cnt + o = self.cls(api) + assert o.mcl is None + o.build_map() + assert o.mcl == 6 # len('cmd_99') + + def test_dict(self): + """ + Tests the `cli.CLI.__contains__` and `cli.CLI.__getitem__` methods. + """ + cnt = 25 + api = DummyAPI(cnt) + assert len(api.Command) == cnt + o = self.cls(api) + o.build_map() + for cmd in api.Command(): + key = cli.to_cli(cmd.name) + assert key in o + assert o[key] is cmd + assert cmd.name not in o + raises(KeyError, getitem, o, cmd.name) diff --git a/tests/test_ipalib/test_config.py b/tests/test_ipalib/test_config.py new file mode 100644 index 00000000..de7d4c22 --- /dev/null +++ b/tests/test_ipalib/test_config.py @@ -0,0 +1,101 @@ +# Authors: +# Martin Nagy +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.config` module. +""" + +import types + +from tstutil import raises +from ipalib import config + + +def test_generate_env(): + """ + Test the `config.generate_env` function + """ + + # Make sure we don't overwrite any properties + env = dict( + query_dns = False, + server = ('first', 'second'), + realm = 'myrealm', + ) + d = config.generate_env(env) + assert d['query_dns'] == False + + # Make sure the servers is overwrote properly (that it is still LazyProp) + iter = d['server'].get_value() + assert iter.next() == 'first' + assert iter.next() == 'second' + + +def test_LazyProp(): + """ + Test the `config.LazyProp` class + """ + + def dummy(): + return 1 + + # Basic sanity testing with no initial value + prop = config.LazyProp(dummy) + assert prop.get_value() == 1 + prop.set_value(2) + assert prop.get_value() == 2 + + # Basic sanity testing with initial value + prop = config.LazyProp(dummy, 3) + assert prop.get_value() == 3 + prop.set_value(4) + assert prop.get_value() == 4 + + +def test_LazyIter(): + """ + Test the `config.LazyIter` class + """ + + def dummy(): + yield 1 + yield 2 + + # Basic sanity testing with no initial value + prop = config.LazyIter(dummy) + iter = prop.get_value() + assert iter.next() == 1 + assert iter.next() == 2 + raises(StopIteration, iter.next) + + # Basic sanity testing with initial value + prop = config.LazyIter(dummy, 0) + iter = prop.get_value() + assert iter.next() == 0 + assert iter.next() == 1 + assert iter.next() == 2 + raises(StopIteration, iter.next) + + +def test_read_config(): + """ + Test the `config.read_config` class + """ + + raises(AssertionError, config.read_config, 1) diff --git a/tests/test_ipalib/test_crud.py b/tests/test_ipalib/test_crud.py new file mode 100644 index 00000000..9355f237 --- /dev/null +++ b/tests/test_ipalib/test_crud.py @@ -0,0 +1,168 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.crud` module. +""" + +from tstutil import read_only, raises, ClassChecker +from ipalib import crud, frontend, plugable, config + +def get_api(): + api = plugable.API( + frontend.Object, + frontend.Method, + frontend.Property, + ) + api.env.update(config.generate_env()) + class user(frontend.Object): + takes_params = ( + 'givenname', + 'sn', + frontend.Param('uid', primary_key=True), + 'initials', + ) + api.register(user) + return api + + +class test_Add(ClassChecker): + """ + Test the `crud.Add` class. + """ + + _cls = crud.Add + + def test_class(self): + assert self.cls.__bases__ == (frontend.Method,) + + def test_options_args(self): + """ + Test `crud.Add.get_args` and `crud.Add.get_options` methods. + """ + api = get_api() + class user_add(self.cls): + pass + api.register(user_add) + api.finalize() + assert list(api.Method.user_add.args) == ['uid'] + assert list(api.Method.user_add.options) == \ + ['givenname', 'sn', 'initials'] + for param in api.Method.user_add.options(): + assert param.required is True + + +class test_Get(ClassChecker): + """ + Test the `crud.Get` class. + """ + + _cls = crud.Get + + def test_class(self): + assert self.cls.__bases__ == (frontend.Method,) + + def test_options_args(self): + """ + Test `crud.Get.get_args` and `crud.Get.get_options` methods. + """ + api = get_api() + class user_get(self.cls): + pass + api.register(user_get) + api.finalize() + assert list(api.Method.user_get.args) == ['uid'] + assert list(api.Method.user_get.options) == [] + + +class test_Del(ClassChecker): + """ + Test the `crud.Del` class. + """ + + _cls = crud.Del + + def test_class(self): + assert self.cls.__bases__ == (frontend.Method,) + + def test_options_args(self): + """ + Test `crud.Del.get_args` and `crud.Del.get_options` methods. + """ + api = get_api() + class user_del(self.cls): + pass + api.register(user_del) + api.finalize() + assert list(api.Method.user_del.args) == ['uid'] + assert list(api.Method.user_del.options) == [] + + +class test_Mod(ClassChecker): + """ + Test the `crud.Mod` class. + """ + + _cls = crud.Mod + + def test_class(self): + assert self.cls.__bases__ == (frontend.Method,) + + def test_options_args(self): + """ + Test `crud.Mod.get_args` and `crud.Mod.get_options` methods. + """ + api = get_api() + class user_mod(self.cls): + pass + api.register(user_mod) + api.finalize() + assert list(api.Method.user_mod.args) == ['uid'] + assert api.Method.user_mod.args[0].required is True + assert list(api.Method.user_mod.options) == \ + ['givenname', 'sn', 'initials'] + for param in api.Method.user_mod.options(): + assert param.required is False + + +class test_Find(ClassChecker): + """ + Test the `crud.Find` class. + """ + + _cls = crud.Find + + def test_class(self): + assert self.cls.__bases__ == (frontend.Method,) + + def test_options_args(self): + """ + Test `crud.Find.get_args` and `crud.Find.get_options` methods. + """ + api = get_api() + class user_find(self.cls): + pass + api.register(user_find) + api.finalize() + assert list(api.Method.user_find.args) == ['uid'] + assert api.Method.user_find.args[0].required is True + assert list(api.Method.user_find.options) == \ + ['givenname', 'sn', 'initials'] + for param in api.Method.user_find.options(): + assert param.required is False diff --git a/tests/test_ipalib/test_errors.py b/tests/test_ipalib/test_errors.py new file mode 100644 index 00000000..7d2df4df --- /dev/null +++ b/tests/test_ipalib/test_errors.py @@ -0,0 +1,274 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.errors` module. +""" + +from tstutil import raises, ClassChecker +from ipalib import errors + + +type_format = '%s: need a %r; got %r' + + +def check_TypeError(f, value, type_, name, **kw): + e = raises(TypeError, f, value, type_, name, **kw) + assert e.value is value + assert e.type is type_ + assert e.name is name + assert str(e) == type_format % (name, type_, value) + + +def test_raise_TypeError(): + """ + Tests the `errors.raise_TypeError` function. + """ + f = errors.raise_TypeError + value = 'Hello.' + type_ = unicode + name = 'message' + + check_TypeError(f, value, type_, name) + + # name not an str + fail_name = 42 + e = raises(AssertionError, f, value, type_, fail_name) + assert str(e) == type_format % ('name', str, fail_name), str(e) + + # type_ not a type: + fail_type = unicode() + e = raises(AssertionError, f, value, fail_type, name) + assert str(e) == type_format % ('type_', type, fail_type) + + # type(value) is type_: + fail_value = u'How are you?' + e = raises(AssertionError, f, fail_value, type_, name) + assert str(e) == 'value: %r is a %r' % (fail_value, type_) + + +def test_check_type(): + """ + Tests the `errors.check_type` function. + """ + f = errors.check_type + value = 'How are you?' + type_ = str + name = 'greeting' + + # Should pass: + assert value is f(value, type_, name) + assert None is f(None, type_, name, allow_none=True) + + # Should raise TypeError + check_TypeError(f, None, type_, name) + check_TypeError(f, value, basestring, name) + check_TypeError(f, value, unicode, name) + + # name not an str + fail_name = unicode(name) + e = raises(AssertionError, f, value, type_, fail_name) + assert str(e) == type_format % ('name', str, fail_name) + + # type_ not a type: + fail_type = 42 + e = raises(AssertionError, f, value, fail_type, name) + assert str(e) == type_format % ('type_', type, fail_type) + + # allow_none not a bool: + fail_bool = 0 + e = raises(AssertionError, f, value, type_, name, allow_none=fail_bool) + assert str(e) == type_format % ('allow_none', bool, fail_bool) + + +def test_check_isinstance(): + """ + Tests the `errors.check_isinstance` function. + """ + f = errors.check_isinstance + value = 'How are you?' + type_ = str + name = 'greeting' + + # Should pass: + assert value is f(value, type_, name) + assert value is f(value, basestring, name) + assert None is f(None, type_, name, allow_none=True) + + # Should raise TypeError + check_TypeError(f, None, type_, name) + check_TypeError(f, value, unicode, name) + + # name not an str + fail_name = unicode(name) + e = raises(AssertionError, f, value, type_, fail_name) + assert str(e) == type_format % ('name', str, fail_name) + + # type_ not a type: + fail_type = 42 + e = raises(AssertionError, f, value, fail_type, name) + assert str(e) == type_format % ('type_', type, fail_type) + + # allow_none not a bool: + fail_bool = 0 + e = raises(AssertionError, f, value, type_, name, allow_none=fail_bool) + assert str(e) == type_format % ('allow_none', bool, fail_bool) + + +class test_IPAError(ClassChecker): + """ + Tests the `errors.IPAError` exception. + """ + _cls = errors.IPAError + + def test_class(self): + assert self.cls.__bases__ == (Exception,) + + def test_init(self): + """ + Tests the `errors.IPAError.__init__` method. + """ + args = ('one fish', 'two fish') + e = self.cls(*args) + assert e.args == args + assert self.cls().args == tuple() + + def test_str(self): + """ + Tests the `errors.IPAError.__str__` method. + """ + f = 'The %s color is %s.' + class custom_error(self.cls): + format = f + for args in [('sexiest', 'red'), ('most-batman-like', 'black')]: + e = custom_error(*args) + assert e.args == args + assert str(e) == f % args + + +class test_ValidationError(ClassChecker): + """ + Tests the `errors.ValidationError` exception. + """ + _cls = errors.ValidationError + + def test_class(self): + assert self.cls.__bases__ == (errors.IPAError,) + + def test_init(self): + """ + Tests the `errors.ValidationError.__init__` method. + """ + name = 'login' + value = 'Whatever' + error = 'Must be lowercase.' + for index in (None, 3): + e = self.cls(name, value, error, index=index) + assert e.name is name + assert e.value is value + assert e.error is error + assert e.index is index + assert str(e) == 'invalid %r value %r: %s' % (name, value, error) + # Check that index default is None: + assert self.cls(name, value, error).index is None + # Check non str name raises AssertionError: + raises(AssertionError, self.cls, unicode(name), value, error) + # Check non int index raises AssertionError: + raises(AssertionError, self.cls, name, value, error, index=5.0) + # Check negative index raises AssertionError: + raises(AssertionError, self.cls, name, value, error, index=-2) + + +class test_ConversionError(ClassChecker): + """ + Tests the `errors.ConversionError` exception. + """ + _cls = errors.ConversionError + + def test_class(self): + assert self.cls.__bases__ == (errors.ValidationError,) + + def test_init(self): + """ + Tests the `errors.ConversionError.__init__` method. + """ + name = 'some_arg' + value = '42.0' + class type_(object): + conversion_error = 'Not an integer' + for index in (None, 7): + e = self.cls(name, value, type_, index=index) + assert e.name is name + assert e.value is value + assert e.type is type_ + assert e.error is type_.conversion_error + assert e.index is index + assert str(e) == 'invalid %r value %r: %s' % (name, value, + type_.conversion_error) + # Check that index default is None: + assert self.cls(name, value, type_).index is None + + +class test_RuleError(ClassChecker): + """ + Tests the `errors.RuleError` exception. + """ + _cls = errors.RuleError + + def test_class(self): + assert self.cls.__bases__ == (errors.ValidationError,) + + def test_init(self): + """ + Tests the `errors.RuleError.__init__` method. + """ + name = 'whatever' + value = 'The smallest weird number.' + def my_rule(value): + return 'Value is bad.' + error = my_rule(value) + for index in (None, 42): + e = self.cls(name, value, error, my_rule, index=index) + assert e.name is name + assert e.value is value + assert e.error is error + assert e.rule is my_rule + # Check that index default is None: + assert self.cls(name, value, error, my_rule).index is None + + +class test_RequirementError(ClassChecker): + """ + Tests the `errors.RequirementError` exception. + """ + _cls = errors.RequirementError + + def test_class(self): + assert self.cls.__bases__ == (errors.ValidationError,) + + def test_init(self): + """ + Tests the `errors.RequirementError.__init__` method. + """ + name = 'givenname' + e = self.cls(name) + assert e.name is name + assert e.value is None + assert e.error == 'Required' + assert e.index is None diff --git a/tests/test_ipalib/test_frontend.py b/tests/test_ipalib/test_frontend.py new file mode 100644 index 00000000..c70cc00d --- /dev/null +++ b/tests/test_ipalib/test_frontend.py @@ -0,0 +1,1080 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.frontend` module. +""" + +from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker +from tstutil import check_TypeError +from ipalib import frontend, backend, plugable, errors, ipa_types, config + + +def test_RULE_FLAG(): + assert frontend.RULE_FLAG == 'validation_rule' + + +def test_rule(): + """ + Tests the `frontend.rule` function. + """ + flag = frontend.RULE_FLAG + rule = frontend.rule + def my_func(): + pass + assert not hasattr(my_func, flag) + rule(my_func) + assert getattr(my_func, flag) is True + @rule + def my_func2(): + pass + assert getattr(my_func2, flag) is True + + +def test_is_rule(): + """ + Tests the `frontend.is_rule` function. + """ + is_rule = frontend.is_rule + flag = frontend.RULE_FLAG + + class no_call(object): + def __init__(self, value): + if value is not None: + assert value in (True, False) + setattr(self, flag, value) + + class call(no_call): + def __call__(self): + pass + + assert is_rule(call(True)) + assert not is_rule(no_call(True)) + assert not is_rule(call(False)) + assert not is_rule(call(None)) + + +class test_DefaultFrom(ClassChecker): + """ + Tests the `frontend.DefaultFrom` class. + """ + _cls = frontend.DefaultFrom + + def test_class(self): + assert self.cls.__bases__ == (plugable.ReadOnly,) + + def test_init(self): + """ + Tests the `frontend.DefaultFrom.__init__` method. + """ + def callback(*args): + return args + keys = ('givenname', 'sn') + o = self.cls(callback, *keys) + assert read_only(o, 'callback') is callback + assert read_only(o, 'keys') == keys + lam = lambda first, last: first[0] + last + o = self.cls(lam) + assert read_only(o, 'keys') == ('first', 'last') + + def test_call(self): + """ + Tests the `frontend.DefaultFrom.__call__` method. + """ + def callback(givenname, sn): + return givenname[0] + sn[0] + keys = ('givenname', 'sn') + o = self.cls(callback, *keys) + kw = dict( + givenname='John', + sn='Public', + hello='world', + ) + assert o(**kw) == 'JP' + assert o() is None + for key in ('givenname', 'sn'): + kw_copy = dict(kw) + del kw_copy[key] + assert o(**kw_copy) is None + o = self.cls(lambda first, last: first[0] + last) + assert o(first='john', last='doe') == 'jdoe' + assert o(first='', last='doe') is None + assert o(one='john', two='doe') is None + + +def test_parse_param_spec(): + """ + Test the `frontend.parse_param_spec` function. + """ + f = frontend.parse_param_spec + + assert f('name') == ('name', dict(required=True, multivalue=False)) + assert f('name?') == ('name', dict(required=False, multivalue=False)) + assert f('name*') == ('name', dict(required=False, multivalue=True)) + assert f('name+') == ('name', dict(required=True, multivalue=True)) + + +class test_Param(ClassChecker): + """ + Test the `frontend.Param` class. + """ + _cls = frontend.Param + + def test_class(self): + assert self.cls.__bases__ == (plugable.ReadOnly,) + + def test_init(self): + """ + Test the `frontend.Param.__init__` method. + """ + name = 'sn' + o = self.cls(name) + assert o.__islocked__() is True + + # Test default values + assert read_only(o, 'name') is name + assert isinstance(read_only(o, 'type'), ipa_types.Unicode) + assert read_only(o, 'doc') == '' + assert read_only(o, 'required') is True + assert read_only(o, 'multivalue') is False + assert read_only(o, 'default') is None + assert read_only(o, 'default_from') is None + assert read_only(o, 'rules') == tuple() + assert len(read_only(o, 'all_rules')) == 1 + assert read_only(o, 'primary_key') is False + + # Test all kw args: + t = ipa_types.Int() + assert self.cls(name, type=t).type is t + assert self.cls(name, doc='the doc').doc == 'the doc' + assert self.cls(name, required=False).required is False + assert self.cls(name, multivalue=True).multivalue is True + assert self.cls(name, default=u'Hello').default == u'Hello' + df = frontend.DefaultFrom(lambda f, l: f + l, + 'first', 'last', + ) + lam = lambda first, last: first + last + for cb in (df, lam): + o = self.cls(name, default_from=cb) + assert type(o.default_from) is frontend.DefaultFrom + assert o.default_from.keys == ('first', 'last') + assert o.default_from.callback('butt', 'erfly') == 'butterfly' + rules = (lambda whatever: 'Not okay!',) + o = self.cls(name, rules=rules) + assert o.rules is rules + assert o.all_rules[1:] == rules + assert self.cls(name, primary_key=True).primary_key is True + + # Test default type_: + o = self.cls(name) + assert isinstance(o.type, ipa_types.Unicode) + + # Test param spec parsing: + o = self.cls('name?') + assert o.name == 'name' + assert o.required is False + assert o.multivalue is False + + o = self.cls('name*') + assert o.name == 'name' + assert o.required is False + assert o.multivalue is True + + o = self.cls('name+') + assert o.name == 'name' + assert o.required is True + assert o.multivalue is True + + e = raises(TypeError, self.cls, name, whatever=True, another=False) + assert str(e) == \ + 'Param.__init__() takes no such kwargs: another, whatever' + + def test_clone(self): + """ + Test the `frontend.Param.__clone__` method. + """ + def compare(o, kw): + for (k, v) in kw.iteritems(): + assert getattr(o, k) == v, (k, v, getattr(o, k)) + default = dict( + required=False, + multivalue=False, + default=None, + default_from=None, + rules=tuple(), + ) + name = 'hair_color?' + type_ = ipa_types.Int() + o = self.cls(name, type=type_) + compare(o, default) + + override = dict(multivalue=True, default=42) + d = dict(default) + d.update(override) + clone = o.__clone__(**override) + assert clone.name == 'hair_color' + assert clone.type is o.type + compare(clone, d) + + def test_convert(self): + """ + Test the `frontend.Param.convert` method. + """ + name = 'some_number' + type_ = ipa_types.Int() + okay = (7, 7L, 7.0, ' 7 ') + fail = ('7.0', '7L', 'whatever', object) + none = (None, '', u'', tuple(), []) + + # Scenario 1: multivalue=False + o = self.cls(name, type=type_) + for n in none: + assert o.convert(n) is None + for value in okay: + new = o.convert(value) + assert new == 7 + assert type(new) is int + for value in fail: + e = raises(errors.ConversionError, o.convert, value) + assert e.name is name + assert e.value is value + assert e.error is type_.conversion_error + assert e.index is None + + # Scenario 2: multivalue=True + o = self.cls(name, type=type_, multivalue=True) + for n in none: + assert o.convert(n) is None + for value in okay: + assert o.convert((value,)) == (7,) + assert o.convert([value]) == (7,) + assert o.convert(okay) == tuple(int(v) for v in okay) + cnt = 5 + for value in fail: + for i in xrange(cnt): + others = list(7 for x in xrange(cnt)) + others[i] = value + for v in [tuple(others), list(others)]: + e = raises(errors.ConversionError, o.convert, v) + assert e.name is name + assert e.value is value + assert e.error is type_.conversion_error + assert e.index == i + + def test_normalize(self): + """ + Test the `frontend.Param.normalize` method. + """ + name = 'sn' + callback = lambda value: value.lower() + values = (None, u'Hello', (u'Hello',), 'hello', ['hello']) + none = (None, '', u'', tuple(), []) + + # Scenario 1: multivalue=False, normalize=None + o = self.cls(name) + for v in values: + # When normalize=None, value is returned, no type checking: + assert o.normalize(v) is v + + # Scenario 2: multivalue=False, normalize=callback + o = self.cls(name, normalize=callback) + for v in (u'Hello', u'hello', 'Hello'): # Okay + assert o.normalize(v) == 'hello' + for v in [None, 42, (u'Hello',)]: # Not basestring + assert o.normalize(v) is v + for n in none: + assert o.normalize(n) is None + + # Scenario 3: multivalue=True, normalize=None + o = self.cls(name, multivalue=True) + for v in values: + # When normalize=None, value is returned, no type checking: + assert o.normalize(v) is v + + # Scenario 4: multivalue=True, normalize=callback + o = self.cls(name, multivalue=True, normalize=callback) + assert o.normalize([]) is None + assert o.normalize(tuple()) is None + for value in [(u'Hello',), (u'hello',), 'Hello', ['Hello']]: # Okay + assert o.normalize(value) == (u'hello',) + fail = 42 # Not basestring + for v in [[fail], (u'hello', fail)]: # Non basestring member + assert o.normalize(v) == tuple(v) + for n in none: + assert o.normalize(n) is None + + def test_validate(self): + """ + Tests the `frontend.Param.validate` method. + """ + name = 'sn' + type_ = ipa_types.Unicode() + def case_rule(value): + if not value.islower(): + return 'Must be lower case' + my_rules = (case_rule,) + okay = u'whatever' + fail_case = u'Whatever' + fail_type = 'whatever' + + # Scenario 1: multivalue=False + o = self.cls(name, type=type_, rules=my_rules) + assert o.rules == my_rules + assert o.all_rules == (type_.validate, case_rule) + o.validate(okay) + e = raises(errors.RuleError, o.validate, fail_case) + assert e.name is name + assert e.value is fail_case + assert e.error == 'Must be lower case' + assert e.rule is case_rule + assert e.index is None + check_TypeError(fail_type, unicode, 'value', o.validate, fail_type) + + ## Scenario 2: multivalue=True + o = self.cls(name, type=type_, multivalue=True, rules=my_rules) + o.validate((okay,)) + cnt = 5 + for i in xrange(cnt): + others = list(okay for x in xrange(cnt)) + others[i] = fail_case + value = tuple(others) + e = raises(errors.RuleError, o.validate, value) + assert e.name is name + assert e.value is fail_case + assert e.error == 'Must be lower case' + assert e.rule is case_rule + assert e.index == i + for not_tuple in (okay, [okay]): + check_TypeError(not_tuple, tuple, 'value', o.validate, not_tuple) + for has_str in [(fail_type,), (okay, fail_type)]: + check_TypeError(fail_type, unicode, 'value', o.validate, has_str) + + def test_get_default(self): + """ + Tests the `frontend.Param.get_default` method. + """ + name = 'greeting' + default = u'Hello, world!' + default_from = frontend.DefaultFrom( + lambda first, last: u'Hello, %s %s!' % (first, last), + 'first', 'last' + ) + + # Scenario 1: multivalue=False + o = self.cls(name, + default=default, + default_from=default_from, + ) + assert o.default is default + assert o.default_from is default_from + assert o.get_default() == default + assert o.get_default(first='John', last='Doe') == 'Hello, John Doe!' + + # Scenario 2: multivalue=True + default = (default,) + o = self.cls(name, + default=default, + default_from=default_from, + multivalue=True, + ) + assert o.default is default + assert o.default_from is default_from + assert o.get_default() == default + assert o.get_default(first='John', last='Doe') == ('Hello, John Doe!',) + + def test_get_value(self): + """ + Tests the `frontend.Param.get_values` method. + """ + name = 'status' + values = (u'Active', u'Inactive') + o = self.cls(name, type=ipa_types.Unicode()) + assert o.get_values() == tuple() + o = self.cls(name, type=ipa_types.Enum(*values)) + assert o.get_values() == values + + +def test_create_param(): + """ + Test the `frontend.create_param` function. + """ + f = frontend.create_param + for name in ['arg', 'arg?', 'arg*', 'arg+']: + o = f(name) + assert type(o) is frontend.Param + assert type(o.type) is ipa_types.Unicode + assert o.name == 'arg' + assert f(o) is o + o = f('arg') + assert o.required is True + assert o.multivalue is False + o = f('arg?') + assert o.required is False + assert o.multivalue is False + o = f('arg*') + assert o.required is False + assert o.multivalue is True + o = f('arg+') + assert o.required is True + assert o.multivalue is True + + +class test_Command(ClassChecker): + """ + Tests the `frontend.Command` class. + """ + _cls = frontend.Command + + def get_subcls(self): + class Rule(object): + def __init__(self, name): + self.name = name + + def __call__(self, value): + if value != self.name: + return 'must equal %s' % self.name + + default_from = frontend.DefaultFrom( + lambda arg: arg, + 'default_from' + ) + normalize = lambda value: value.lower() + + class example(self.cls): + takes_options = ( + frontend.Param('option0', + normalize=normalize, + default_from=default_from, + rules=(Rule('option0'),) + ), + frontend.Param('option1', + normalize=normalize, + default_from=default_from, + rules=(Rule('option1'),), + required=True, + ), + ) + return example + + def get_instance(self, args=tuple(), options=tuple()): + """ + Helper method used to test args and options. + """ + class example(self.cls): + takes_args = args + takes_options = options + o = example() + o.finalize() + return o + + def test_class(self): + assert self.cls.__bases__ == (plugable.Plugin,) + assert self.cls.takes_options == tuple() + assert self.cls.takes_args == tuple() + + def test_get_args(self): + """ + Tests the `frontend.Command.get_args` method. + """ + assert list(self.cls().get_args()) == [] + args = ('login', 'stuff') + o = self.get_instance(args=args) + assert o.get_args() is args + + def test_get_options(self): + """ + Tests the `frontend.Command.get_options` method. + """ + assert list(self.cls().get_options()) == [] + options = ('verbose', 'debug') + o = self.get_instance(options=options) + assert o.get_options() is options + + def test_args(self): + """ + Tests the ``Command.args`` instance attribute. + """ + assert 'args' in self.cls.__public__ # Public + assert self.cls().args is None + o = self.cls() + o.finalize() + assert type(o.args) is plugable.NameSpace + assert len(o.args) == 0 + args = ('destination', 'source?') + ns = self.get_instance(args=args).args + assert type(ns) is plugable.NameSpace + assert len(ns) == len(args) + assert list(ns) == ['destination', 'source'] + assert type(ns.destination) is frontend.Param + assert type(ns.source) is frontend.Param + assert ns.destination.required is True + assert ns.destination.multivalue is False + assert ns.source.required is False + assert ns.source.multivalue is False + + # Test TypeError: + e = raises(TypeError, self.get_instance, args=(u'whatever',)) + assert str(e) == \ + 'create_param() takes %r or %r; got %r' % (str, frontend.Param, u'whatever') + + # Test ValueError, required after optional: + e = raises(ValueError, self.get_instance, args=('arg1?', 'arg2')) + assert str(e) == 'arg2: required argument after optional' + + # Test ValueError, scalar after multivalue: + e = raises(ValueError, self.get_instance, args=('arg1+', 'arg2')) + assert str(e) == 'arg2: only final argument can be multivalue' + + def test_max_args(self): + """ + Test the ``Command.max_args`` instance attribute. + """ + o = self.get_instance() + assert o.max_args == 0 + o = self.get_instance(args=('one?',)) + assert o.max_args == 1 + o = self.get_instance(args=('one', 'two?')) + assert o.max_args == 2 + o = self.get_instance(args=('one', 'multi+',)) + assert o.max_args is None + o = self.get_instance(args=('one', 'multi*',)) + assert o.max_args is None + + def test_options(self): + """ + Tests the ``Command.options`` instance attribute. + """ + assert 'options' in self.cls.__public__ # Public + assert self.cls().options is None + o = self.cls() + o.finalize() + assert type(o.options) is plugable.NameSpace + assert len(o.options) == 0 + options = ('target', 'files*') + ns = self.get_instance(options=options).options + assert type(ns) is plugable.NameSpace + assert len(ns) == len(options) + assert list(ns) == ['target', 'files'] + assert type(ns.target) is frontend.Param + assert type(ns.files) is frontend.Param + assert ns.target.required is True + assert ns.target.multivalue is False + assert ns.files.required is False + assert ns.files.multivalue is True + + def test_convert(self): + """ + Tests the `frontend.Command.convert` method. + """ + assert 'convert' in self.cls.__public__ # Public + kw = dict( + option0='option0', + option1='option1', + ) + expected = dict(kw) + expected.update(dict(option0=u'option0', option1=u'option1')) + o = self.subcls() + o.finalize() + for (key, value) in o.convert(**kw).iteritems(): + v = expected[key] + assert value == v + assert type(value) is type(v) + + def test_normalize(self): + """ + Tests the `frontend.Command.normalize` method. + """ + assert 'normalize' in self.cls.__public__ # Public + kw = dict( + option0=u'OPTION0', + option1=u'OPTION1', + ) + norm = dict((k, v.lower()) for (k, v) in kw.items()) + sub = self.subcls() + sub.finalize() + assert sub.normalize(**kw) == norm + + def test_get_default(self): + """ + Tests the `frontend.Command.get_default` method. + """ + assert 'get_default' in self.cls.__public__ # Public + no_fill = dict( + option0='value0', + option1='value1', + whatever='hello world', + ) + fill = dict( + default_from='the default', + ) + default = dict( + option0='the default', + option1='the default', + ) + sub = self.subcls() + sub.finalize() + assert sub.get_default(**no_fill) == {} + assert sub.get_default(**fill) == default + + def test_validate(self): + """ + Tests the `frontend.Command.validate` method. + """ + assert 'validate' in self.cls.__public__ # Public + + sub = self.subcls() + sub.finalize() + + # Check with valid args + okay = dict( + option0=u'option0', + option1=u'option1', + another_option='some value', + ) + sub.validate(**okay) + + # Check with an invalid arg + fail = dict(okay) + fail['option0'] = u'whatever' + e = raises(errors.RuleError, sub.validate, **fail) + assert e.name == 'option0' + assert e.value == u'whatever' + assert e.error == 'must equal option0' + assert e.rule.__class__.__name__ == 'Rule' + assert e.index is None + + # Check with a missing required arg + fail = dict(okay) + fail.pop('option1') + e = raises(errors.RequirementError, sub.validate, **fail) + assert e.name == 'option1' + assert e.value is None + assert e.index is None + + def test_execute(self): + """ + Tests the `frontend.Command.execute` method. + """ + assert 'execute' in self.cls.__public__ # Public + + def test_args_to_kw(self): + """ + Test the `frontend.Command.args_to_kw` method. + """ + assert 'args_to_kw' in self.cls.__public__ # Public + o = self.get_instance(args=('one', 'two?')) + assert o.args_to_kw(1) == dict(one=1) + assert o.args_to_kw(1, 2) == dict(one=1, two=2) + + o = self.get_instance(args=('one', 'two*')) + assert o.args_to_kw(1) == dict(one=1) + assert o.args_to_kw(1, 2) == dict(one=1, two=(2,)) + assert o.args_to_kw(1, 2, 3) == dict(one=1, two=(2, 3)) + + o = self.get_instance(args=('one', 'two+')) + assert o.args_to_kw(1) == dict(one=1) + assert o.args_to_kw(1, 2) == dict(one=1, two=(2,)) + assert o.args_to_kw(1, 2, 3) == dict(one=1, two=(2, 3)) + + o = self.get_instance() + e = raises(errors.ArgumentError, o.args_to_kw, 1) + assert str(e) == 'example takes no arguments' + + o = self.get_instance(args=('one?',)) + e = raises(errors.ArgumentError, o.args_to_kw, 1, 2) + assert str(e) == 'example takes at most 1 argument' + + o = self.get_instance(args=('one', 'two?')) + e = raises(errors.ArgumentError, o.args_to_kw, 1, 2, 3) + assert str(e) == 'example takes at most 2 arguments' + + def test_kw_to_args(self): + """ + Tests the `frontend.Command.kw_to_args` method. + """ + assert 'kw_to_args' in self.cls.__public__ # Public + o = self.get_instance(args=('one', 'two?')) + assert o.kw_to_args() == (None, None) + assert o.kw_to_args(whatever='hello') == (None, None) + assert o.kw_to_args(one='the one') == ('the one', None) + assert o.kw_to_args(two='the two') == (None, 'the two') + assert o.kw_to_args(whatever='hello', two='Two', one='One') == \ + ('One', 'Two') + + def test_run(self): + """ + Test the `frontend.Command.run` method. + """ + class my_cmd(self.cls): + def execute(self, *args, **kw): + return ('execute', args, kw) + + def forward(self, *args, **kw): + return ('forward', args, kw) + + args = ('Hello,', 'world,') + kw = dict(how_are='you', on_this='fine day?') + + # Test in server context: + api = plugable.API(self.cls) + api.env.update(dict(server_context=True)) + api.finalize() + o = my_cmd() + o.set_api(api) + assert o.run.im_func is self.cls.run.im_func + assert ('execute', args, kw) == o.run(*args, **kw) + assert o.run.im_func is my_cmd.execute.im_func + + # Test in non-server context + api = plugable.API(self.cls) + api.env.update(dict(server_context=False)) + api.finalize() + o = my_cmd() + o.set_api(api) + assert o.run.im_func is self.cls.run.im_func + assert ('forward', args, kw) == o.run(*args, **kw) + assert o.run.im_func is my_cmd.forward.im_func + + +class test_Object(ClassChecker): + """ + Test the `frontend.Object` class. + """ + _cls = frontend.Object + + def test_class(self): + assert self.cls.__bases__ == (plugable.Plugin,) + assert self.cls.backend is None + assert self.cls.methods is None + assert self.cls.properties is None + assert self.cls.params is None + assert self.cls.params_minus_pk is None + assert self.cls.takes_params == tuple() + + def test_init(self): + """ + Test the `frontend.Object.__init__` method. + """ + o = self.cls() + assert o.backend is None + assert o.methods is None + assert o.properties is None + assert o.params is None + assert o.params_minus_pk is None + assert o.properties is None + + def test_set_api(self): + """ + Test the `frontend.Object.set_api` method. + """ + # Setup for test: + class DummyAttribute(object): + def __init__(self, obj_name, attr_name, name=None): + self.obj_name = obj_name + self.attr_name = attr_name + if name is None: + self.name = '%s_%s' % (obj_name, attr_name) + else: + self.name = name + self.param = frontend.create_param(attr_name) + + def __clone__(self, attr_name): + return self.__class__( + self.obj_name, + self.attr_name, + getattr(self, attr_name) + ) + + def get_attributes(cnt, format): + for name in ['other', 'user', 'another']: + for i in xrange(cnt): + yield DummyAttribute(name, format % i) + + cnt = 10 + formats = dict( + methods='method_%d', + properties='property_%d', + ) + + + _d = dict( + Method=plugable.NameSpace( + get_attributes(cnt, formats['methods']) + ), + Property=plugable.NameSpace( + get_attributes(cnt, formats['properties']) + ), + ) + api = plugable.MagicDict(_d) + assert len(api.Method) == cnt * 3 + assert len(api.Property) == cnt * 3 + + class user(self.cls): + pass + + # Actually perform test: + o = user() + o.set_api(api) + assert read_only(o, 'api') is api + for name in ['methods', 'properties']: + namespace = getattr(o, name) + assert isinstance(namespace, plugable.NameSpace) + assert len(namespace) == cnt + f = formats[name] + for i in xrange(cnt): + attr_name = f % i + attr = namespace[attr_name] + assert isinstance(attr, DummyAttribute) + assert attr is getattr(namespace, attr_name) + assert attr.obj_name == 'user' + assert attr.attr_name == attr_name + assert attr.name == attr_name + + # Test params instance attribute + o = self.cls() + o.set_api(api) + ns = o.params + assert type(ns) is plugable.NameSpace + assert len(ns) == 0 + class example(self.cls): + takes_params = ('banana', 'apple') + o = example() + o.set_api(api) + ns = o.params + assert type(ns) is plugable.NameSpace + assert len(ns) == 2, repr(ns) + assert list(ns) == ['banana', 'apple'] + for p in ns(): + assert type(p) is frontend.Param + assert p.required is True + assert p.multivalue is False + + def test_primary_key(self): + """ + Test the `frontend.Object.primary_key` attribute. + """ + api = plugable.API( + frontend.Method, + frontend.Property, + ) + api.env.update(config.generate_env()) + api.finalize() + + # Test with no primary keys: + class example1(self.cls): + takes_params = ( + 'one', + 'two', + ) + o = example1() + o.set_api(api) + assert o.primary_key is None + assert o.params_minus_pk is None + + # Test with 1 primary key: + class example2(self.cls): + takes_params = ( + 'one', + 'two', + frontend.Param('three', + primary_key=True, + ), + 'four', + ) + o = example2() + o.set_api(api) + pk = o.primary_key + assert isinstance(pk, frontend.Param) + assert pk.name == 'three' + assert pk.primary_key is True + assert o.params[2] is o.primary_key + assert isinstance(o.params_minus_pk, plugable.NameSpace) + assert list(o.params_minus_pk) == ['one', 'two', 'four'] + + # Test with multiple primary_key: + class example3(self.cls): + takes_params = ( + frontend.Param('one', primary_key=True), + frontend.Param('two', primary_key=True), + 'three', + frontend.Param('four', primary_key=True), + ) + o = example3() + e = raises(ValueError, o.set_api, api) + assert str(e) == \ + 'example3 (Object) has multiple primary keys: one, two, four' + + def test_backend(self): + """ + Test the `frontend.Object.backend` attribute. + """ + api = plugable.API( + frontend.Object, + frontend.Method, + frontend.Property, + backend.Backend, + ) + api.env.update(config.generate_env()) + class ldap(backend.Backend): + whatever = 'It worked!' + api.register(ldap) + class user(frontend.Object): + backend_name = 'ldap' + api.register(user) + api.finalize() + b = api.Object.user.backend + assert isinstance(b, ldap) + assert b.whatever == 'It worked!' + + +class test_Attribute(ClassChecker): + """ + Tests the `frontend.Attribute` class. + """ + _cls = frontend.Attribute + + def test_class(self): + assert self.cls.__bases__ == (plugable.Plugin,) + assert type(self.cls.obj) is property + assert type(self.cls.obj_name) is property + assert type(self.cls.attr_name) is property + + def test_init(self): + """ + Tests the `frontend.Attribute.__init__` method. + """ + class user_add(self.cls): + pass + o = user_add() + assert read_only(o, 'obj') is None + assert read_only(o, 'obj_name') == 'user' + assert read_only(o, 'attr_name') == 'add' + + def test_set_api(self): + """ + Tests the `frontend.Attribute.set_api` method. + """ + user_obj = 'The user frontend.Object instance' + class api(object): + Object = dict(user=user_obj) + class user_add(self.cls): + pass + o = user_add() + assert read_only(o, 'api') is None + assert read_only(o, 'obj') is None + o.set_api(api) + assert read_only(o, 'api') is api + assert read_only(o, 'obj') is user_obj + + +class test_Method(ClassChecker): + """ + Test the `frontend.Method` class. + """ + _cls = frontend.Method + + def test_class(self): + assert self.cls.__bases__ == (frontend.Attribute, frontend.Command) + assert self.cls.implements(frontend.Command) + assert self.cls.implements(frontend.Attribute) + + def test_init(self): + """ + Test the `frontend.Method.__init__` method. + """ + class user_add(self.cls): + pass + o = user_add() + assert o.name == 'user_add' + assert o.obj_name == 'user' + assert o.attr_name == 'add' + assert frontend.Command.implemented_by(o) + assert frontend.Attribute.implemented_by(o) + + +class test_Property(ClassChecker): + """ + Tests the `frontend.Property` class. + """ + _cls = frontend.Property + + def get_subcls(self): + class user_givenname(self.cls): + 'User first name' + + @frontend.rule + def rule0_lowercase(self, value): + if not value.islower(): + return 'Must be lowercase' + return user_givenname + + def test_class(self): + assert self.cls.__bases__ == (frontend.Attribute,) + assert isinstance(self.cls.type, ipa_types.Unicode) + assert self.cls.required is False + assert self.cls.multivalue is False + assert self.cls.default is None + assert self.cls.default_from is None + assert self.cls.normalize is None + + def test_init(self): + """ + Tests the `frontend.Property.__init__` method. + """ + o = self.subcls() + assert len(o.rules) == 1 + assert o.rules[0].__name__ == 'rule0_lowercase' + param = o.param + assert isinstance(param, frontend.Param) + assert param.name == 'givenname' + assert param.doc == 'User first name' + + +class test_Application(ClassChecker): + """ + Tests the `frontend.Application` class. + """ + _cls = frontend.Application + + def test_class(self): + assert self.cls.__bases__ == (frontend.Command,) + assert type(self.cls.application) is property + + def test_application(self): + """ + Tests the `frontend.Application.application` property. + """ + assert 'application' in self.cls.__public__ # Public + assert 'set_application' in self.cls.__public__ # Public + app = 'The external application' + class example(self.cls): + 'A subclass' + for o in (self.cls(), example()): + assert read_only(o, 'application') is None + e = raises(TypeError, o.set_application, None) + assert str(e) == ( + '%s.application cannot be None' % o.__class__.__name__ + ) + o.set_application(app) + assert read_only(o, 'application') is app + e = raises(AttributeError, o.set_application, app) + assert str(e) == ( + '%s.application can only be set once' % o.__class__.__name__ + ) + assert read_only(o, 'application') is app diff --git a/tests/test_ipalib/test_ipa_types.py b/tests/test_ipalib/test_ipa_types.py new file mode 100644 index 00000000..b8e996a7 --- /dev/null +++ b/tests/test_ipalib/test_ipa_types.py @@ -0,0 +1,371 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.ipa_types` module. +""" + +from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker +from ipalib import ipa_types, errors, plugable + + +def test_check_min_max(): + """ + Tests the `ipa_types.check_min_max` function. + """ + f = ipa_types.check_min_max + okay = [ + (None, -5), + (-20, None), + (-20, -5), + ] + for (l, h) in okay: + assert f(l, h, 'low', 'high') is None + fail_type = [ + '10', + 10.0, + 10L, + True, + False, + object, + ] + for value in fail_type: + e = raises(TypeError, f, value, None, 'low', 'high') + assert str(e) == 'low must be an int or None, got: %r' % value + e = raises(TypeError, f, None, value, 'low', 'high') + assert str(e) == 'high must be an int or None, got: %r' % value + fail_value = [ + (10, 5), + (-5, -10), + (5, -10), + ] + for (l, h) in fail_value: + e = raises(ValueError, f, l, h, 'low', 'high') + assert str(e) == 'low > high: low=%r, high=%r' % (l, h) + + +class test_Type(ClassChecker): + """ + Tests the `ipa_types.Type` class. + """ + _cls = ipa_types.Type + + def test_class(self): + assert self.cls.__bases__ == (plugable.ReadOnly,) + + def test_init(self): + okay = (bool, int, float, unicode) + for t in okay: + o = self.cls(t) + assert o.__islocked__() is True + assert read_only(o, 'type') is t + assert read_only(o, 'name') is 'Type' + + type_errors = (None, True, 8, 8.0, u'hello') + for t in type_errors: + e = raises(TypeError, self.cls, t) + assert str(e) == '%r is not %r' % (type(t), type) + + value_errors = (long, complex, str, tuple, list, dict, set, frozenset) + for t in value_errors: + e = raises(ValueError, self.cls, t) + assert str(e) == 'not an allowed type: %r' % t + + def test_validate(self): + o = self.cls(unicode) + for value in (None, u'Hello', 'Hello', 42, False): + assert o.validate(value) is None + + +class test_Bool(ClassChecker): + _cls = ipa_types.Bool + + def test_class(self): + assert self.cls.__bases__ == (ipa_types.Type,) + + def test_init(self): + o = self.cls() + assert o.__islocked__() is True + assert read_only(o, 'type') is bool + assert read_only(o, 'name') == 'Bool' + assert read_only(o, 'true') == 'Yes' + assert read_only(o, 'false') == 'No' + + keys = ('true', 'false') + val = 'some value' + for key in keys: + # Check that kwarg sets appropriate attribute: + o = self.cls(**{key: val}) + assert read_only(o, key) is val + # Check that None raises TypeError: + e = raises(TypeError, self.cls, **{key: None}) + assert str(e) == '`%s` cannot be None' % key + + # Check that ValueError is raise if true == false: + e = raises(ValueError, self.cls, true=1L, false=1.0) + assert str(e) == 'cannot be equal: true=1L, false=1.0' + + def test_call(self): + o = self.cls() + assert o(True) is True + assert o('Yes') is True + assert o(False) is False + assert o('No') is False + for value in (0, 1, 'True', 'False', 'yes', 'no'): + # value is not be converted, so None is returned + assert o(value) is None + + +class test_Int(ClassChecker): + _cls = ipa_types.Int + + def test_class(self): + assert self.cls.__bases__ == (ipa_types.Type,) + + def test_init(self): + o = self.cls() + assert o.__islocked__() is True + assert read_only(o, 'type') is int + assert read_only(o, 'name') == 'Int' + assert read_only(o, 'min_value') is None + assert read_only(o, 'max_value') is None + + okay = [ + (None, -5), + (-20, None), + (-20, -5), + ] + for (l, h) in okay: + o = self.cls(min_value=l, max_value=h) + assert o.min_value is l + assert o.max_value is h + + fail_type = [ + '10', + 10.0, + 10L, + True, + False, + object, + ] + for value in fail_type: + e = raises(TypeError, self.cls, min_value=value) + assert str(e) == ( + 'min_value must be an int or None, got: %r' % value + ) + e = raises(TypeError, self.cls, max_value=value) + assert str(e) == ( + 'max_value must be an int or None, got: %r' % value + ) + + fail_value = [ + (10, 5), + (5, -5), + (-5, -10), + ] + for (l, h) in fail_value: + e = raises(ValueError, self.cls, min_value=l, max_value=h) + assert str(e) == ( + 'min_value > max_value: min_value=%d, max_value=%d' % (l, h) + ) + + def test_call(self): + o = self.cls() + + # Test calling with None + e = raises(TypeError, o, None) + assert str(e) == 'value cannot be None' + + # Test with values that can be converted: + okay = [ + 3, + '3', + ' 3 ', + 3L, + 3.0, + ] + for value in okay: + assert o(value) == 3 + + # Test with values that cannot be converted: + fail = [ + object, + '3.0', + '3L', + 'whatever', + ] + for value in fail: + assert o(value) is None + + + def test_validate(self): + o = self.cls(min_value=2, max_value=7) + assert o.validate(2) is None + assert o.validate(5) is None + assert o.validate(7) is None + assert o.validate(1) == 'Cannot be smaller than 2' + assert o.validate(8) == 'Cannot be larger than 7' + for val in ['5', 5.0, 5L, None, True, False, object]: + assert o.validate(val) == 'Must be an integer' + + +class test_Unicode(ClassChecker): + _cls = ipa_types.Unicode + + def test_class(self): + assert self.cls.__bases__ == (ipa_types.Type,) + + def test_init(self): + o = self.cls() + assert o.__islocked__() is True + assert read_only(o, 'type') is unicode + assert read_only(o, 'name') == 'Unicode' + assert read_only(o, 'min_length') is None + assert read_only(o, 'max_length') is None + assert read_only(o, 'pattern') is None + assert read_only(o, 'regex') is None + + # Test min_length, max_length: + okay = ( + (0, 1), + (8, 8), + ) + for (l, h) in okay: + o = self.cls(min_length=l, max_length=h) + assert o.min_length == l + assert o.max_length == h + + fail_type = [ + '10', + 10.0, + 10L, + True, + False, + object, + ] + for value in fail_type: + e = raises(TypeError, self.cls, min_length=value) + assert str(e) == ( + 'min_length must be an int or None, got: %r' % value + ) + e = raises(TypeError, self.cls, max_length=value) + assert str(e) == ( + 'max_length must be an int or None, got: %r' % value + ) + + fail_value = [ + (10, 5), + (5, -5), + (0, -10), + ] + for (l, h) in fail_value: + e = raises(ValueError, self.cls, min_length=l, max_length=h) + assert str(e) == ( + 'min_length > max_length: min_length=%d, max_length=%d' % (l, h) + ) + + for (key, lower) in [('min_length', 0), ('max_length', 1)]: + value = lower - 1 + kw = {key: value} + e = raises(ValueError, self.cls, **kw) + assert str(e) == '%s must be >= %d, got: %d' % (key, lower, value) + + # Test pattern: + okay = [ + '(hello|world)', + u'(take the blue pill|take the red pill)', + ] + for value in okay: + o = self.cls(pattern=value) + assert o.pattern is value + assert o.regex is not None + + fail = [ + 42, + True, + False, + object, + ] + for value in fail: + e = raises(TypeError, self.cls, pattern=value) + assert str(e) == ( + 'pattern must be a basestring or None, got: %r' % value + ) + + # Test regex: + pat = '^(hello|world)$' + o = self.cls(pattern=pat) + for value in ('hello', 'world'): + m = o.regex.match(value) + assert m.group(1) == value + for value in ('hello beautiful', 'world!'): + assert o.regex.match(value) is None + + def test_validate(self): + pat = '^a_*b$' + o = self.cls(min_length=3, max_length=4, pattern=pat) + assert o.validate(u'a_b') is None + assert o.validate(u'a__b') is None + assert o.validate('a_b') == 'Must be a string' + assert o.validate(u'ab') == 'Must be at least 3 characters long' + assert o.validate(u'a___b') == 'Can be at most 4 characters long' + assert o.validate(u'a-b') == 'Must match %r' % pat + assert o.validate(u'a--b') == 'Must match %r' % pat + + +class test_Enum(ClassChecker): + _cls = ipa_types.Enum + + def test_class(self): + assert self.cls.__bases__ == (ipa_types.Type,) + + def test_init(self): + for t in (unicode, int, float): + values = (t(1), t(2), t(3)) + o = self.cls(*values) + assert o.__islocked__() is True + assert read_only(o, 'type') is t + assert read_only(o, 'name') is 'Enum' + assert read_only(o, 'values') == values + assert read_only(o, 'frozenset') == frozenset(values) + + # Check that ValueError is raised when no values are given: + e = raises(ValueError, self.cls) + assert str(e) == 'Enum requires at least one value' + + # Check that TypeError is raised when type of first value is not + # allowed: + e = raises(TypeError, self.cls, 'hello') + assert str(e) == '%r: %r not unicode, int, nor float' % ('hello', str) + #self.cls('hello') + + # Check that TypeError is raised when subsequent values aren't same + # type as first: + e = raises(TypeError, self.cls, u'hello', 'world') + assert str(e) == '%r: %r is not %r' % ('world', str, unicode) + + def test_validate(self): + values = (u'hello', u'naughty', u'nurse') + o = self.cls(*values) + for value in values: + assert o.validate(value) is None + assert o.validate(str(value)) == 'Incorrect type' + for value in (u'one fish', u'two fish'): + assert o.validate(value) == 'Invalid value' + assert o.validate(str(value)) == 'Incorrect type' diff --git a/tests/test_ipalib/test_plugable.py b/tests/test_ipalib/test_plugable.py new file mode 100644 index 00000000..fd3c3c88 --- /dev/null +++ b/tests/test_ipalib/test_plugable.py @@ -0,0 +1,896 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.plugable` module. +""" + +from tstutil import raises, no_set, no_del, read_only +from tstutil import getitem, setitem, delitem +from tstutil import ClassChecker +from ipalib import plugable, errors + + +class test_ReadOnly(ClassChecker): + """ + Test the `plugable.ReadOnly` class + """ + _cls = plugable.ReadOnly + + def test_class(self): + assert self.cls.__bases__ == (object,) + assert callable(self.cls.__lock__) + assert callable(self.cls.__islocked__) + + def test_lock(self): + """ + Test the `plugable.ReadOnly.__lock__` method. + """ + o = self.cls() + assert o._ReadOnly__locked is False + o.__lock__() + assert o._ReadOnly__locked is True + e = raises(AssertionError, o.__lock__) # Can only be locked once + assert str(e) == '__lock__() can only be called once' + assert o._ReadOnly__locked is True # This should still be True + + def test_lock(self): + """ + Test the `plugable.ReadOnly.__islocked__` method. + """ + o = self.cls() + assert o.__islocked__() is False + o.__lock__() + assert o.__islocked__() is True + + def test_setattr(self): + """ + Test the `plugable.ReadOnly.__setattr__` method. + """ + o = self.cls() + o.attr1 = 'Hello, world!' + assert o.attr1 == 'Hello, world!' + o.__lock__() + for name in ('attr1', 'attr2'): + e = raises(AttributeError, setattr, o, name, 'whatever') + assert str(e) == 'read-only: cannot set ReadOnly.%s' % name + assert o.attr1 == 'Hello, world!' + + def test_delattr(self): + """ + Test the `plugable.ReadOnly.__delattr__` method. + """ + o = self.cls() + o.attr1 = 'Hello, world!' + o.attr2 = 'How are you?' + assert o.attr1 == 'Hello, world!' + assert o.attr2 == 'How are you?' + del o.attr1 + assert not hasattr(o, 'attr1') + o.__lock__() + e = raises(AttributeError, delattr, o, 'attr2') + assert str(e) == 'read-only: cannot del ReadOnly.attr2' + assert o.attr2 == 'How are you?' + + +def test_lock(): + """ + Tests the `plugable.lock` function. + """ + f = plugable.lock + + # Test on a ReadOnly instance: + o = plugable.ReadOnly() + assert not o.__islocked__() + assert f(o) is o + assert o.__islocked__() + + # Test on something not subclassed from ReadOnly: + class not_subclass(object): + def __lock__(self): + pass + def __islocked__(self): + return True + o = not_subclass() + raises(ValueError, f, o) + + # Test that it checks __islocked__(): + class subclass(plugable.ReadOnly): + def __islocked__(self): + return False + o = subclass() + raises(AssertionError, f, o) + + +class test_SetProxy(ClassChecker): + """ + Tests the `plugable.SetProxy` class. + """ + _cls = plugable.SetProxy + + def test_class(self): + assert self.cls.__bases__ == (plugable.ReadOnly,) + + def test_init(self): + okay = (set, frozenset, dict) + fail = (list, tuple) + for t in okay: + self.cls(t()) + raises(TypeError, self.cls, t) + for t in fail: + raises(TypeError, self.cls, t()) + raises(TypeError, self.cls, t) + + def test_SetProxy(self): + def get_key(i): + return 'key_%d' % i + + cnt = 10 + target = set() + proxy = self.cls(target) + for i in xrange(cnt): + key = get_key(i) + + # Check initial state + assert len(proxy) == len(target) + assert list(proxy) == sorted(target) + assert key not in proxy + assert key not in target + + # Add and test again + target.add(key) + assert len(proxy) == len(target) + assert list(proxy) == sorted(target) + assert key in proxy + assert key in target + + +class test_DictProxy(ClassChecker): + """ + Tests the `plugable.DictProxy` class. + """ + _cls = plugable.DictProxy + + def test_class(self): + assert self.cls.__bases__ == (plugable.SetProxy,) + + def test_init(self): + self.cls(dict()) + raises(TypeError, self.cls, dict) + fail = (set, frozenset, list, tuple) + for t in fail: + raises(TypeError, self.cls, t()) + raises(TypeError, self.cls, t) + + def test_DictProxy(self): + def get_kv(i): + return ( + 'key_%d' % i, + 'val_%d' % i, + ) + cnt = 10 + target = dict() + proxy = self.cls(target) + for i in xrange(cnt): + (key, val) = get_kv(i) + + # Check initial state + assert len(proxy) == len(target) + assert list(proxy) == sorted(target) + assert list(proxy()) == [target[k] for k in sorted(target)] + assert key not in proxy + raises(KeyError, getitem, proxy, key) + + # Add and test again + target[key] = val + assert len(proxy) == len(target) + assert list(proxy) == sorted(target) + assert list(proxy()) == [target[k] for k in sorted(target)] + + # Verify TypeError is raised trying to set/del via proxy + raises(TypeError, setitem, proxy, key, val) + raises(TypeError, delitem, proxy, key) + + +class test_MagicDict(ClassChecker): + """ + Tests the `plugable.MagicDict` class. + """ + _cls = plugable.MagicDict + + def test_class(self): + assert self.cls.__bases__ == (plugable.DictProxy,) + for non_dict in ('hello', 69, object): + raises(TypeError, self.cls, non_dict) + + def test_MagicDict(self): + cnt = 10 + keys = [] + d = dict() + dictproxy = self.cls(d) + for i in xrange(cnt): + key = 'key_%d' % i + val = 'val_%d' % i + keys.append(key) + + # Test thet key does not yet exist + assert len(dictproxy) == i + assert key not in dictproxy + assert not hasattr(dictproxy, key) + raises(KeyError, getitem, dictproxy, key) + raises(AttributeError, getattr, dictproxy, key) + + # Test that items/attributes cannot be set on dictproxy: + raises(TypeError, setitem, dictproxy, key, val) + raises(AttributeError, setattr, dictproxy, key, val) + + # Test that additions in d are reflected in dictproxy: + d[key] = val + assert len(dictproxy) == i + 1 + assert key in dictproxy + assert hasattr(dictproxy, key) + assert dictproxy[key] is val + assert read_only(dictproxy, key) is val + + # Test __iter__ + assert list(dictproxy) == keys + + for key in keys: + # Test that items cannot be deleted through dictproxy: + raises(TypeError, delitem, dictproxy, key) + raises(AttributeError, delattr, dictproxy, key) + + # Test that deletions in d are reflected in dictproxy + del d[key] + assert len(dictproxy) == len(d) + assert key not in dictproxy + raises(KeyError, getitem, dictproxy, key) + raises(AttributeError, getattr, dictproxy, key) + + +class test_Plugin(ClassChecker): + """ + Tests the `plugable.Plugin` class. + """ + _cls = plugable.Plugin + + def test_class(self): + assert self.cls.__bases__ == (plugable.ReadOnly,) + assert self.cls.__public__ == frozenset() + assert type(self.cls.name) is property + assert type(self.cls.doc) is property + assert type(self.cls.api) is property + + def test_name(self): + """ + Tests the `plugable.Plugin.name` property. + """ + assert read_only(self.cls(), 'name') == 'Plugin' + + class some_subclass(self.cls): + pass + assert read_only(some_subclass(), 'name') == 'some_subclass' + + def test_doc(self): + """ + Tests the `plugable.Plugin.doc` property. + """ + class some_subclass(self.cls): + 'here is the doc string' + assert read_only(some_subclass(), 'doc') == 'here is the doc string' + + def test_implements(self): + """ + Tests the `plugable.Plugin.implements` classmethod. + """ + class example(self.cls): + __public__ = frozenset(( + 'some_method', + 'some_property', + )) + class superset(self.cls): + __public__ = frozenset(( + 'some_method', + 'some_property', + 'another_property', + )) + class subset(self.cls): + __public__ = frozenset(( + 'some_property', + )) + class any_object(object): + __public__ = frozenset(( + 'some_method', + 'some_property', + )) + + for ex in (example, example()): + # Test using str: + assert ex.implements('some_method') + assert not ex.implements('another_method') + + # Test using frozenset: + assert ex.implements(frozenset(['some_method'])) + assert not ex.implements( + frozenset(['some_method', 'another_method']) + ) + + # Test using another object/class with __public__ frozenset: + assert ex.implements(example) + assert ex.implements(example()) + + assert ex.implements(subset) + assert not subset.implements(ex) + + assert not ex.implements(superset) + assert superset.implements(ex) + + assert ex.implements(any_object) + assert ex.implements(any_object()) + + def test_implemented_by(self): + """ + Tests the `plugable.Plugin.implemented_by` classmethod. + """ + class base(self.cls): + __public__ = frozenset(( + 'attr0', + 'attr1', + 'attr2', + )) + + class okay(base): + def attr0(self): + pass + def __get_attr1(self): + assert False # Make sure property isn't accesed on instance + attr1 = property(__get_attr1) + attr2 = 'hello world' + another_attr = 'whatever' + + class fail(base): + def __init__(self): + # Check that class, not instance is inspected: + self.attr2 = 'hello world' + def attr0(self): + pass + def __get_attr1(self): + assert False # Make sure property isn't accesed on instance + attr1 = property(__get_attr1) + another_attr = 'whatever' + + # Test that AssertionError is raised trying to pass something not + # subclass nor instance of base: + raises(AssertionError, base.implemented_by, object) + + # Test on subclass with needed attributes: + assert base.implemented_by(okay) is True + assert base.implemented_by(okay()) is True + + # Test on subclass *without* needed attributes: + assert base.implemented_by(fail) is False + assert base.implemented_by(fail()) is False + + def test_set_api(self): + """ + Tests the `plugable.Plugin.set_api` method. + """ + api = 'the api instance' + o = self.cls() + assert o.api is None + e = raises(AssertionError, o.set_api, None) + assert str(e) == 'set_api() argument cannot be None' + o.set_api(api) + assert o.api is api + e = raises(AssertionError, o.set_api, api) + assert str(e) == 'set_api() can only be called once' + + def test_finalize(self): + """ + Tests the `plugable.Plugin.finalize` method. + """ + o = self.cls() + assert not o.__islocked__() + o.finalize() + assert o.__islocked__() + + +class test_PluginProxy(ClassChecker): + """ + Tests the `plugable.PluginProxy` class. + """ + _cls = plugable.PluginProxy + + def test_class(self): + assert self.cls.__bases__ == (plugable.SetProxy,) + + def test_proxy(self): + # Setup: + class base(object): + __public__ = frozenset(( + 'public_0', + 'public_1', + '__call__', + )) + + def public_0(self): + return 'public_0' + + def public_1(self): + return 'public_1' + + def __call__(self, caller): + return 'ya called it, %s.' % caller + + def private_0(self): + return 'private_0' + + def private_1(self): + return 'private_1' + + class plugin(base): + name = 'user_add' + attr_name = 'add' + doc = 'add a new user' + + # Test that TypeError is raised when base is not a class: + raises(TypeError, self.cls, base(), None) + + # Test that ValueError is raised when target is not instance of base: + raises(ValueError, self.cls, base, object()) + + # Test with correct arguments: + i = plugin() + p = self.cls(base, i) + assert read_only(p, 'name') is plugin.name + assert read_only(p, 'doc') == plugin.doc + assert list(p) == sorted(base.__public__) + + # Test normal methods: + for n in xrange(2): + pub = 'public_%d' % n + priv = 'private_%d' % n + assert getattr(i, pub)() == pub + assert getattr(p, pub)() == pub + assert hasattr(p, pub) + assert getattr(i, priv)() == priv + assert not hasattr(p, priv) + + # Test __call__: + value = 'ya called it, dude.' + assert i('dude') == value + assert p('dude') == value + assert callable(p) + + # Test name_attr='name' kw arg + i = plugin() + p = self.cls(base, i, 'attr_name') + assert read_only(p, 'name') == 'add' + + def test_implements(self): + """ + Tests the `plugable.PluginProxy.implements` method. + """ + class base(object): + __public__ = frozenset() + name = 'base' + doc = 'doc' + @classmethod + def implements(cls, arg): + return arg + 7 + + class sub(base): + @classmethod + def implements(cls, arg): + """ + Defined to make sure base.implements() is called, not + target.implements() + """ + return arg + + o = sub() + p = self.cls(base, o) + assert p.implements(3) == 10 + + def test_clone(self): + """ + Tests the `plugable.PluginProxy.__clone__` method. + """ + class base(object): + __public__ = frozenset() + class sub(base): + name = 'some_name' + doc = 'doc' + label = 'another_name' + + p = self.cls(base, sub()) + assert read_only(p, 'name') == 'some_name' + c = p.__clone__('label') + assert isinstance(c, self.cls) + assert c is not p + assert read_only(c, 'name') == 'another_name' + + +def test_check_name(): + """ + Tests the `plugable.check_name` function. + """ + f = plugable.check_name + okay = [ + 'user_add', + 'stuff2junk', + 'sixty9', + ] + nope = [ + '_user_add', + '__user_add', + 'user_add_', + 'user_add__', + '_user_add_', + '__user_add__', + '60nine', + ] + for name in okay: + assert name is f(name) + e = raises(TypeError, f, unicode(name)) + assert str(e) == errors.TYPE_FORMAT % ('name', str, unicode(name)) + for name in nope: + raises(errors.NameSpaceError, f, name) + for name in okay: + raises(errors.NameSpaceError, f, name.upper()) + +class DummyMember(object): + def __init__(self, i): + assert type(i) is int + self.name = 'member_%02d' % i + + +class test_NameSpace(ClassChecker): + """ + Tests the `plugable.NameSpace` class. + """ + _cls = plugable.NameSpace + + def test_class(self): + assert self.cls.__bases__ == (plugable.ReadOnly,) + + def test_init(self): + """ + Tests the `plugable.NameSpace.__init__` method. + """ + o = self.cls(tuple()) + assert list(o) == [] + assert list(o()) == [] + for cnt in (10, 25): + members = tuple(DummyMember(cnt - i) for i in xrange(cnt)) + for sort in (True, False): + o = self.cls(members, sort=sort) + if sort: + ordered = tuple(sorted(members, key=lambda m: m.name)) + else: + ordered = members + names = tuple(m.name for m in ordered) + assert o.__todict__() == dict((o.name, o) for o in ordered) + + # Test __len__: + assert len(o) == cnt + + # Test __contains__: + for name in names: + assert name in o + assert ('member_00') not in o + + # Test __iter__, __call__: + assert tuple(o) == names + assert tuple(o()) == ordered + + # Test __getitem__, getattr: + for (i, member) in enumerate(ordered): + assert o[i] is member + name = member.name + assert o[name] is member + assert read_only(o, name) is member + + # Test negative indexes: + for i in xrange(1, cnt + 1): + assert o[-i] is ordered[-i] + + # Test slices: + assert o[2:cnt-5] == ordered[2:cnt-5] + assert o[::3] == ordered[::3] + + # Test __repr__: + assert repr(o) == \ + 'NameSpace(<%d members>, sort=%r)' % (cnt, sort) + + +def test_Environment(): + """ + Tests the `plugable.Environment` class. + """ + # This has to be the same as iter_cnt + control_cnt = 0 + class prop_class: + def __init__(self, val): + self._val = val + def get_value(self): + return self._val + + class iter_class(prop_class): + # Increment this for each time iter_class yields + iter_cnt = 0 + def get_value(self): + for item in self._val: + self.__class__.iter_cnt += 1 + yield item + + # Tests for basic functionality + basic_tests = ( + ('a', 1), + ('b', 'basic_foo'), + ('c', ('basic_bar', 'basic_baz')), + ) + # Tests with prop classes + prop_tests = ( + ('d', prop_class(2), 2), + ('e', prop_class('prop_foo'), 'prop_foo'), + ('f', prop_class(('prop_bar', 'prop_baz')), ('prop_bar', 'prop_baz')), + ) + # Tests with iter classes + iter_tests = ( + ('g', iter_class((3, 4, 5)), (3, 4, 5)), + ('h', iter_class(('iter_foo', 'iter_bar', 'iter_baz')), + ('iter_foo', 'iter_bar', 'iter_baz') + ), + ) + + # Set all the values + env = plugable.Environment() + for name, val in basic_tests: + env[name] = val + for name, val, dummy in prop_tests: + env[name] = val + for name, val, dummy in iter_tests: + env[name] = val + + # Test if the values are correct + for name, val in basic_tests: + assert env[name] == val + for name, dummy, val in prop_tests: + assert env[name] == val + # Test if the get_value() function is called only when needed + for name, dummy, correct_values in iter_tests: + values_in_env = [] + for val in env[name]: + control_cnt += 1 + assert iter_class.iter_cnt == control_cnt + values_in_env.append(val) + assert tuple(values_in_env) == correct_values + + # Test __setattr__() + env.spam = 'ham' + assert env.spam == 'ham' + + # Test if we throw AttributeError exception when trying to overwrite + # existing value, or delete it + raises(AttributeError, setitem, env, 'a', 1) + raises(AttributeError, setattr, env, 'a', 1) + raises(AttributeError, delitem, env, 'a') + raises(AttributeError, delattr, env, 'a') + raises(AttributeError, plugable.Environment.update, env, dict(a=1000)) + # This should be silently ignored + env.update(dict(a=1000), True) + assert env.a != 1000 + +def test_Registrar(): + class Base1(object): + pass + class Base2(object): + pass + class Base3(object): + pass + class plugin1(Base1): + pass + class plugin2(Base2): + pass + class plugin3(Base3): + pass + + # Test creation of Registrar: + r = plugable.Registrar(Base1, Base2) + + # Test __iter__: + assert list(r) == ['Base1', 'Base2'] + + # Test __hasitem__, __getitem__: + for base in [Base1, Base2]: + name = base.__name__ + assert name in r + assert r[name] is base + magic = getattr(r, name) + assert type(magic) is plugable.MagicDict + assert len(magic) == 0 + + # Check that TypeError is raised trying to register something that isn't + # a class: + raises(TypeError, r, plugin1()) + + # Check that SubclassError is raised trying to register a class that is + # not a subclass of an allowed base: + raises(errors.SubclassError, r, plugin3) + + # Check that registration works + r(plugin1) + assert len(r.Base1) == 1 + assert r.Base1['plugin1'] is plugin1 + assert r.Base1.plugin1 is plugin1 + + # Check that DuplicateError is raised trying to register exact class + # again: + raises(errors.DuplicateError, r, plugin1) + + # Check that OverrideError is raised trying to register class with same + # name and same base: + orig1 = plugin1 + class base1_extended(Base1): + pass + class plugin1(base1_extended): + pass + raises(errors.OverrideError, r, plugin1) + + # Check that overriding works + r(plugin1, override=True) + assert len(r.Base1) == 1 + assert r.Base1.plugin1 is plugin1 + assert r.Base1.plugin1 is not orig1 + + # Check that MissingOverrideError is raised trying to override a name + # not yet registerd: + raises(errors.MissingOverrideError, r, plugin2, override=True) + + # Test that another plugin can be registered: + assert len(r.Base2) == 0 + r(plugin2) + assert len(r.Base2) == 1 + assert r.Base2.plugin2 is plugin2 + + # Setup to test more registration: + class plugin1a(Base1): + pass + r(plugin1a) + + class plugin1b(Base1): + pass + r(plugin1b) + + class plugin2a(Base2): + pass + r(plugin2a) + + class plugin2b(Base2): + pass + r(plugin2b) + + # Again test __hasitem__, __getitem__: + for base in [Base1, Base2]: + name = base.__name__ + assert name in r + assert r[name] is base + magic = getattr(r, name) + assert len(magic) == 3 + for key in magic: + klass = magic[key] + assert getattr(magic, key) is klass + assert issubclass(klass, base) + + + +def test_API(): + assert issubclass(plugable.API, plugable.ReadOnly) + + # Setup the test bases, create the API: + class base0(plugable.Plugin): + __public__ = frozenset(( + 'method', + )) + + def method(self, n): + return n + + class base1(plugable.Plugin): + __public__ = frozenset(( + 'method', + )) + + def method(self, n): + return n + 1 + + api = plugable.API(base0, base1) + r = api.register + assert isinstance(r, plugable.Registrar) + assert read_only(api, 'register') is r + + class base0_plugin0(base0): + pass + r(base0_plugin0) + + class base0_plugin1(base0): + pass + r(base0_plugin1) + + class base0_plugin2(base0): + pass + r(base0_plugin2) + + class base1_plugin0(base1): + pass + r(base1_plugin0) + + class base1_plugin1(base1): + pass + r(base1_plugin1) + + class base1_plugin2(base1): + pass + r(base1_plugin2) + + # Test API instance: + api.finalize() + + def get_base(b): + return 'base%d' % b + + def get_plugin(b, p): + return 'base%d_plugin%d' % (b, p) + + for b in xrange(2): + base_name = get_base(b) + ns = getattr(api, base_name) + assert isinstance(ns, plugable.NameSpace) + assert read_only(api, base_name) is ns + assert len(ns) == 3 + for p in xrange(3): + plugin_name = get_plugin(b, p) + proxy = ns[plugin_name] + assert isinstance(proxy, plugable.PluginProxy) + assert proxy.name == plugin_name + assert read_only(ns, plugin_name) is proxy + assert read_only(proxy, 'method')(7) == 7 + b + + # Test that calling finilize again raises AssertionError: + raises(AssertionError, api.finalize) + + # Test with base class that doesn't request a proxy + class NoProxy(plugable.Plugin): + __proxy__ = False + api = plugable.API(NoProxy) + class plugin0(NoProxy): + pass + api.register(plugin0) + class plugin1(NoProxy): + pass + api.register(plugin1) + api.finalize() + names = ['plugin0', 'plugin1'] + assert list(api.NoProxy) == names + for name in names: + plugin = api.NoProxy[name] + assert getattr(api.NoProxy, name) is plugin + assert isinstance(plugin, plugable.Plugin) + assert not isinstance(plugin, plugable.PluginProxy) diff --git a/tests/test_ipalib/test_tstutil.py b/tests/test_ipalib/test_tstutil.py new file mode 100644 index 00000000..5916f9d2 --- /dev/null +++ b/tests/test_ipalib/test_tstutil.py @@ -0,0 +1,148 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for test-helper `tests.tstutil` module. +""" + +import tstutil + + +class Prop(object): + def __init__(self, *ops): + self.__ops = frozenset(ops) + self.__prop = 'prop value' + + def __get_prop(self): + if 'get' not in self.__ops: + raise AttributeError('get prop') + return self.__prop + + def __set_prop(self, value): + if 'set' not in self.__ops: + raise AttributeError('set prop') + self.__prop = value + + def __del_prop(self): + if 'del' not in self.__ops: + raise AttributeError('del prop') + self.__prop = None + + prop = property(__get_prop, __set_prop, __del_prop) + + +def test_yes_raised(): + f = tstutil.raises + + class SomeError(Exception): + pass + + class AnotherError(Exception): + pass + + def callback1(): + 'raises correct exception' + raise SomeError() + + def callback2(): + 'raises wrong exception' + raise AnotherError() + + def callback3(): + 'raises no exception' + + f(SomeError, callback1) + + raised = False + try: + f(SomeError, callback2) + except AnotherError: + raised = True + assert raised + + raised = False + try: + f(SomeError, callback3) + except tstutil.ExceptionNotRaised: + raised = True + assert raised + + +def test_no_set(): + # Tests that it works when prop cannot be set: + tstutil.no_set(Prop('get', 'del'), 'prop') + + # Tests that ExceptionNotRaised is raised when prop *can* be set: + raised = False + try: + tstutil.no_set(Prop('set'), 'prop') + except tstutil.ExceptionNotRaised: + raised = True + assert raised + + +def test_no_del(): + # Tests that it works when prop cannot be deleted: + tstutil.no_del(Prop('get', 'set'), 'prop') + + # Tests that ExceptionNotRaised is raised when prop *can* be set: + raised = False + try: + tstutil.no_del(Prop('del'), 'prop') + except tstutil.ExceptionNotRaised: + raised = True + assert raised + + +def test_read_only(): + # Test that it works when prop is read only: + assert tstutil.read_only(Prop('get'), 'prop') == 'prop value' + + # Test that ExceptionNotRaised is raised when prop can be set: + raised = False + try: + tstutil.read_only(Prop('get', 'set'), 'prop') + except tstutil.ExceptionNotRaised: + raised = True + assert raised + + # Test that ExceptionNotRaised is raised when prop can be deleted: + raised = False + try: + tstutil.read_only(Prop('get', 'del'), 'prop') + except tstutil.ExceptionNotRaised: + raised = True + assert raised + + # Test that ExceptionNotRaised is raised when prop can be both set and + # deleted: + raised = False + try: + tstutil.read_only(Prop('get', 'del'), 'prop') + except tstutil.ExceptionNotRaised: + raised = True + assert raised + + # Test that AttributeError is raised when prop can't be read: + raised = False + try: + tstutil.read_only(Prop(), 'prop') + except AttributeError: + raised = True + assert raised diff --git a/tests/test_ipalib/test_util.py b/tests/test_ipalib/test_util.py new file mode 100644 index 00000000..f8ee0bf4 --- /dev/null +++ b/tests/test_ipalib/test_util.py @@ -0,0 +1,49 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Unit tests for `ipalib.util` module. +""" + +from tstutil import raises +from ipalib import util + + +def test_xmlrpc_marshal(): + """ + Test the `util.xmlrpc_marshal` function. + """ + f = util.xmlrpc_marshal + assert f() == ({},) + assert f('one', 'two') == ({}, 'one', 'two') + assert f(one=1, two=2) == (dict(one=1, two=2),) + assert f('one', 'two', three=3, four=4) == \ + (dict(three=3, four=4), 'one', 'two') + + +def test_xmlrpc_unmarshal(): + """ + Test the `util.xmlrpc_unmarshal` function. + """ + f = util.xmlrpc_unmarshal + assert f() == (tuple(), {}) + assert f({}, 'one', 'two') == (('one', 'two'), {}) + assert f(dict(one=1, two=2)) == (tuple(), dict(one=1, two=2)) + assert f(dict(three=3, four=4), 'one', 'two') == \ + (('one', 'two'), dict(three=3, four=4)) diff --git a/tests/test_ipalib/tstutil.py b/tests/test_ipalib/tstutil.py new file mode 100644 index 00000000..743716a0 --- /dev/null +++ b/tests/test_ipalib/tstutil.py @@ -0,0 +1,147 @@ +# Authors: +# Jason Gerard DeRose +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Utility functions for the unit tests. +""" + +import inspect +from ipalib import errors + +class ExceptionNotRaised(Exception): + """ + Exception raised when an *expected* exception is *not* raised during a + unit test. + """ + msg = 'expected %s' + + def __init__(self, expected): + self.expected = expected + + def __str__(self): + return self.msg % self.expected.__name__ + + +def raises(exception, callback, *args, **kw): + """ + Tests that the expected exception is raised; raises ExceptionNotRaised + if test fails. + """ + raised = False + try: + callback(*args, **kw) + except exception, e: + raised = True + if not raised: + raise ExceptionNotRaised(exception) + return e + + +def getitem(obj, key): + """ + Works like getattr but for dictionary interface. Use this in combination + with raises() to test that, for example, KeyError is raised. + """ + return obj[key] + + +def setitem(obj, key, value): + """ + Works like setattr but for dictionary interface. Use this in combination + with raises() to test that, for example, TypeError is raised. + """ + obj[key] = value + + +def delitem(obj, key): + """ + Works like delattr but for dictionary interface. Use this in combination + with raises() to test that, for example, TypeError is raised. + """ + del obj[key] + + +def no_set(obj, name, value='some_new_obj'): + """ + Tests that attribute cannot be set. + """ + raises(AttributeError, setattr, obj, name, value) + + +def no_del(obj, name): + """ + Tests that attribute cannot be deleted. + """ + raises(AttributeError, delattr, obj, name) + + +def read_only(obj, name, value='some_new_obj'): + """ + Tests that attribute is read-only. Returns attribute. + """ + # Test that it cannot be set: + no_set(obj, name, value) + + # Test that it cannot be deleted: + no_del(obj, name) + + # Return the attribute + return getattr(obj, name) + + +def is_prop(prop): + return type(prop) is property + + +class ClassChecker(object): + __cls = None + __subcls = None + + def __get_cls(self): + if self.__cls is None: + self.__cls = self._cls + assert inspect.isclass(self.__cls) + return self.__cls + cls = property(__get_cls) + + def __get_subcls(self): + if self.__subcls is None: + self.__subcls = self.get_subcls() + assert inspect.isclass(self.__subcls) + return self.__subcls + subcls = property(__get_subcls) + + def get_subcls(self): + raise NotImplementedError( + self.__class__.__name__, + 'get_subcls()' + ) + + +def check_TypeError(value, type_, name, callback, *args, **kw): + """ + Tests a standard TypeError raised with `errors.raise_TypeError`. + """ + e = raises(TypeError, callback, *args, **kw) + assert e.value is value + assert e.type is type_ + assert e.name == name + assert type(e.name) is str + assert str(e) == errors.TYPE_FORMAT % (name, type_, value) + return e -- cgit