From deb8e3dfc899cfc7ee31f47c1ba7c4301c58fc51 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Tue, 7 Oct 2008 22:30:53 -0600 Subject: Renamed tests/tstutil.py to tests/util.py --- tests/util.py | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 tests/util.py (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py new file mode 100644 index 00000000..5656515c --- /dev/null +++ b/tests/util.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 + +""" +Common utility functions and classes for 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 From f80beb948bb8914df922e85ef20d9152ca47b527 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 24 Oct 2008 15:07:07 -0600 Subject: Added ipalib/constants.py; added Env._load_config() method along with comprehensive unit tests for same --- tests/util.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index 5656515c..a813903a 100644 --- a/tests/util.py +++ b/tests/util.py @@ -22,8 +22,51 @@ Common utility functions and classes for unit tests. """ import inspect +import os +from os import path +import tempfile +import shutil from ipalib import errors + +class TempDir(object): + def __init__(self): + self.__path = tempfile.mkdtemp(prefix='ipa.tests.') + assert self.path == self.__path + + def __get_path(self): + assert path.abspath(self.__path) == self.__path + assert self.__path.startswith('/tmp/ipa.tests.') + assert path.isdir(self.__path) and not path.islink(self.__path) + return self.__path + path = property(__get_path) + + def rmtree(self): + shutil.rmtree(self.path) + self.__path = None + + def makedirs(self, *parts): + d = self.join(*parts) + if not path.exists(d): + os.makedirs(d) + assert path.isdir(d) and not path.islink(d) + return d + + def touch(self, *parts): + d = self.makedirs(*parts[:-1]) + f = path.join(d, parts[-1]) + assert not path.exists(f) + open(f, 'w').close() + assert path.isfile(f) and not path.islink(f) + return f + + def join(self, *parts): + return path.join(self.path, *parts) + + def __del__(self): + self.rmtree() + + class ExceptionNotRaised(Exception): """ Exception raised when an *expected* exception is *not* raised during a -- cgit From 8ca44bcbfa2aec0c7c84205dc08c81f711a22c5d Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 24 Oct 2008 16:02:26 -0600 Subject: Added tests.util.TempHome class for created a tempdir and setting os.environ['HOME'] to it; updated various unit tests for Env so they are run using a tempdir for home --- tests/util.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index a813903a..1cbc4e31 100644 --- a/tests/util.py +++ b/tests/util.py @@ -42,8 +42,9 @@ class TempDir(object): path = property(__get_path) def rmtree(self): - shutil.rmtree(self.path) - self.__path = None + if self.__path is not None: + shutil.rmtree(self.path) + self.__path = None def makedirs(self, *parts): d = self.join(*parts) @@ -67,6 +68,17 @@ class TempDir(object): self.rmtree() +class TempHome(TempDir): + def __init__(self): + super(TempHome, self).__init__() + self.__home = os.environ['HOME'] + os.environ['HOME'] = self.path + + def rmtree(self): + os.environ['HOME'] = self.__home + super(TempHome, self).rmtree() + + class ExceptionNotRaised(Exception): """ Exception raised when an *expected* exception is *not* raised during a -- cgit From ac4efac3944d180cffd0ad9d63f631dc928e1d28 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 24 Oct 2008 20:02:14 -0600 Subject: Finished Env._finalize_core() and corresponding unit tests --- tests/util.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index 1cbc4e31..cc761ce7 100644 --- a/tests/util.py +++ b/tests/util.py @@ -61,6 +61,14 @@ class TempDir(object): assert path.isfile(f) and not path.islink(f) return f + def write(self, content, *parts): + d = self.makedirs(*parts[:-1]) + f = path.join(d, parts[-1]) + assert not path.exists(f) + open(f, 'w').write(content) + assert path.isfile(f) and not path.islink(f) + return f + def join(self, *parts): return path.join(self.path, *parts) -- cgit From 2fee6a3e20169f12b837647f0f71d6f28937490f Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Thu, 30 Oct 2008 01:34:46 -0600 Subject: Added tests.util.get_api() function to create a standard (api, home) tuple for unit testing --- tests/util.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index cc761ce7..aa0299fd 100644 --- a/tests/util.py +++ b/tests/util.py @@ -26,7 +26,8 @@ import os from os import path import tempfile import shutil -from ipalib import errors +import ipalib + class TempDir(object): @@ -206,5 +207,21 @@ def check_TypeError(value, type_, name, callback, *args, **kw): assert e.type is type_ assert e.name == name assert type(e.name) is str - assert str(e) == errors.TYPE_FORMAT % (name, type_, value) + assert str(e) == ipalib.errors.TYPE_FORMAT % (name, type_, value) return e + + +def get_api(**kw): + """ + Returns (api, home) tuple. + + This function returns a tuple containing an `ipalib.plugable.API` + instance and a `TempHome` instance. + """ + home = TempHome() + api = ipalib.get_standard_api() + api.env.mode = 'unit_test' + api.env.in_tree = True + for (key, value) in kw.iteritems(): + api.env[key] = value + return (api, home) -- cgit From c26a3c8542472a2d3931c7dc82edfd684354af6b Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 7 Nov 2008 02:26:38 -0700 Subject: Finished fist draft of plugin tutorial in ipalib/__init__.py docstring --- tests/util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index aa0299fd..d7aaae89 100644 --- a/tests/util.py +++ b/tests/util.py @@ -219,8 +219,7 @@ def get_api(**kw): instance and a `TempHome` instance. """ home = TempHome() - api = ipalib.get_standard_api() - api.env.mode = 'unit_test' + api = ipalib.get_standard_api(mode='unit_test') api.env.in_tree = True for (key, value) in kw.iteritems(): api.env[key] = value -- cgit From f3869d7b24f65ca04494ff756e092d7aedd67a5c Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Tue, 11 Nov 2008 15:24:18 -0700 Subject: Renamed ipalib.get_standard_api() to create_api() --- tests/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index d7aaae89..0efca5d4 100644 --- a/tests/util.py +++ b/tests/util.py @@ -219,7 +219,7 @@ def get_api(**kw): instance and a `TempHome` instance. """ home = TempHome() - api = ipalib.get_standard_api(mode='unit_test') + api = ipalib.create_api(mode='unit_test') api.env.in_tree = True for (key, value) in kw.iteritems(): api.env[key] = value -- cgit From 2d458a12339fbb7ef006ff7defc1e2f541e2f23f Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Mon, 24 Nov 2008 21:34:01 -0700 Subject: Stared some RPC-related error cleanup; started work on ipa_server.rcp.xmlrpc plugin --- tests/util.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index 0efca5d4..22b8a770 100644 --- a/tests/util.py +++ b/tests/util.py @@ -27,6 +27,7 @@ from os import path import tempfile import shutil import ipalib +from ipalib.plugable import Plugin @@ -198,6 +199,11 @@ class ClassChecker(object): ) + + + + + def check_TypeError(value, type_, name, callback, *args, **kw): """ Tests a standard TypeError raised with `errors.raise_TypeError`. @@ -224,3 +230,54 @@ def get_api(**kw): for (key, value) in kw.iteritems(): api.env[key] = value return (api, home) + + +def create_test_api(**kw): + """ + Returns (api, home) tuple. + + This function returns a tuple containing an `ipalib.plugable.API` + instance and a `TempHome` instance. + """ + home = TempHome() + api = ipalib.create_api(mode='unit_test') + api.env.in_tree = True + for (key, value) in kw.iteritems(): + api.env[key] = value + return (api, home) + + +class PluginTester(object): + __plugin = None + + def __get_plugin(self): + if self.__plugin is None: + self.__plugin = self._plugin + assert issubclass(self.__plugin, Plugin) + return self.__plugin + plugin = property(__get_plugin) + + def register(self, *plugins, **kw): + """ + Create a testing api and register ``self.plugin``. + + This method returns an (api, home) tuple. + + :param plugins: Additional \*plugins to register. + :param kw: Additional \**kw args to pass to `create_test_api`. + """ + (api, home) = create_test_api(**kw) + api.register(self.plugin) + for p in plugins: + api.register(p) + return (api, home) + + def finalize(self, *plugins, **kw): + (api, home) = self.register(*plugins, **kw) + api.finalize() + return (api, home) + + def instance(self, namespace, *plugins, **kw): + (api, home) = self.finalize(*plugins, **kw) + o = api[namespace][self.plugin.__name__] + return (o, api, home) -- cgit From fd43b39145382b96cd2e0d0da3d5dcbe0d3a4a2a Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Mon, 22 Dec 2008 23:09:35 -0700 Subject: Moved setting of run-time variables from Env.__init__() to Env._bootstrap() --- tests/util.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index 22b8a770..4a74d294 100644 --- a/tests/util.py +++ b/tests/util.py @@ -84,10 +84,6 @@ class TempHome(TempDir): self.__home = os.environ['HOME'] os.environ['HOME'] = self.path - def rmtree(self): - os.environ['HOME'] = self.__home - super(TempHome, self).rmtree() - class ExceptionNotRaised(Exception): """ -- cgit From c161784973fdedb146a4087d8692b157214c4db0 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Sun, 4 Jan 2009 00:46:21 -0700 Subject: Added request.ugettext() and request.ungettext() functions; added corresponding unit tests --- tests/util.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index 4a74d294..66236cbb 100644 --- a/tests/util.py +++ b/tests/util.py @@ -277,3 +277,38 @@ class PluginTester(object): (api, home) = self.finalize(*plugins, **kw) o = api[namespace][self.plugin.__name__] return (o, api, home) + + +class DummyUGettext(object): + __called = False + + def __init__(self): + self.translation = u'The translation' + + def __call__(self, message): + assert type(message) is str + assert self.__called is False + self.__called = True + self.message = message + return self.translation + + +class DummyUNGettext(object): + __called = False + + def __init__(self): + self.translation_singular = u'The singular translation' + self.translation_plural = u'The plural translation' + + def __call__(self, singular, plural, n): + assert type(singular) is str + assert type(plural) is str + assert type(n) is int + assert self.__called is False + self.__called = True + self.singular = singular + self.plural = plural + self.n = n + if n == 1: + return self.translation_singular + return self.translation_plural -- cgit From 2608838ef1f96b0c8d3ff3ed4310eaa63ba73031 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Sun, 4 Jan 2009 03:52:08 -0700 Subject: Quite a bit of work on new public errors and their unit tests --- tests/util.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index 66236cbb..b0961f45 100644 --- a/tests/util.py +++ b/tests/util.py @@ -99,6 +99,14 @@ class ExceptionNotRaised(Exception): return self.msg % self.expected.__name__ +def assert_equal(val1, val2): + """ + Assert ``val1`` and ``val2`` are the same type and of equal value. + """ + assert type(val1) is type(val2), '%r != %r' % (val1, val2) + assert val1 == val2, '%r != %r' % (val1, val2) + + def raises(exception, callback, *args, **kw): """ Tests that the expected exception is raised; raises ExceptionNotRaised -- cgit From c121d0064bb7a7bd1a289ae29ceb2dee314c2d2f Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Mon, 5 Jan 2009 01:20:14 -0700 Subject: New Param: Added Param.get_label() method for a way to retrieve translated message at request time --- tests/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index b0961f45..9ed10016 100644 --- a/tests/util.py +++ b/tests/util.py @@ -287,7 +287,7 @@ class PluginTester(object): return (o, api, home) -class DummyUGettext(object): +class dummy_ugettext(object): __called = False def __init__(self): @@ -301,7 +301,7 @@ class DummyUGettext(object): return self.translation -class DummyUNGettext(object): +class dummy_ungettext(object): __called = False def __init__(self): -- cgit From 6d6c0d81ddbfc56672f0595a5f631c5e846d8b2b Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Mon, 5 Jan 2009 02:20:09 -0700 Subject: New Param: decided on calling signature for rules; added unit tests for Bytes._rule_minlength, _rule_maxlength, and _rule_length --- tests/util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index 9ed10016..3033b82b 100644 --- a/tests/util.py +++ b/tests/util.py @@ -290,8 +290,11 @@ class PluginTester(object): class dummy_ugettext(object): __called = False - def __init__(self): - self.translation = u'The translation' + def __init__(self, translation=None): + if translation is None: + translation = u'The translation' + self.translation = translation + assert type(self.translation) is unicode def __call__(self, message): assert type(message) is str -- cgit From c2b0d03f82f16debcc55d34ac44197e0bc97e0e8 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Tue, 13 Jan 2009 01:07:33 -0700 Subject: New Param: updated Bytes and Str length rules to use new rule(_, value) calling signature; updated corresponding unit tests --- tests/util.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index 3033b82b..c1480840 100644 --- a/tests/util.py +++ b/tests/util.py @@ -297,12 +297,24 @@ class dummy_ugettext(object): assert type(self.translation) is unicode def __call__(self, message): - assert type(message) is str assert self.__called is False self.__called = True + assert type(message) is str + assert not hasattr(self, 'message') self.message = message + assert type(self.translation) is unicode return self.translation + def called(self): + return self.__called + + def reset(self): + assert type(self.translation) is unicode + assert type(self.message) is str + del self.message + assert self.__called is True + self.__called = False + class dummy_ungettext(object): __called = False -- cgit From 10747103fa3748677e6e1948977de1313fe25bc9 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Tue, 13 Jan 2009 02:17:16 -0700 Subject: New Param: implemented a base Param._convert_scalar() method; added Param.type_error attribute for ConversionError message --- tests/util.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index c1480840..af4c2393 100644 --- a/tests/util.py +++ b/tests/util.py @@ -28,6 +28,7 @@ import tempfile import shutil import ipalib from ipalib.plugable import Plugin +from ipalib.request import context @@ -202,6 +203,14 @@ class ClassChecker(object): 'get_subcls()' ) + def tearDown(self): + """ + nose tear-down fixture. + """ + for name in ('ugettext', 'ungettext'): + if hasattr(context, name): + delattr(context, name) + -- cgit From 55fba5420d8ea57931937728102094492ca73d86 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Mon, 19 Jan 2009 21:10:42 -0700 Subject: Added rpc.xmlclient backend plugin for forwarding; added corresponding unit tests --- tests/util.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'tests/util.py') diff --git a/tests/util.py b/tests/util.py index af4c2393..f5899dfa 100644 --- a/tests/util.py +++ b/tests/util.py @@ -344,3 +344,48 @@ class dummy_ungettext(object): if n == 1: return self.translation_singular return self.translation_plural + + +class DummyMethod(object): + def __init__(self, callback, name): + self.__callback = callback + self.__name = name + + def __call__(self, *args, **kw): + return self.__callback(self.__name, args, kw) + + +class DummyClass(object): + def __init__(self, *calls): + self.__calls = calls + self.__i = 0 + for (name, args, kw, result) in calls: + method = DummyMethod(self.__process, name) + setattr(self, name, method) + + def __process(self, name_, args_, kw_): + if self.__i >= len(self.__calls): + raise AssertionError( + 'extra call: %s, %r, %r' % (name, args, kw) + ) + (name, args, kw, result) = self.__calls[self.__i] + self.__i += 1 + i = self.__i + if name_ != name: + raise AssertionError( + 'call %d should be to method %r; got %r' % (i, name, name_) + ) + if args_ != args: + raise AssertionError( + 'call %d to %r should have args %r; got %r' % (i, name, args, args_) + ) + if kw_ != kw: + raise AssertionError( + 'call %d to %r should have kw %r, got %r' % (i, name, kw, kw_) + ) + if isinstance(result, Exception): + raise result + return result + + def _calledall(self): + return self.__i == len(self.__calls) -- cgit