From c81d5f36c289ef3eeb7f23e3995ce1aa9377e50f Mon Sep 17 00:00:00 2001 From: Endre Karlson Date: Mon, 18 Nov 2013 13:53:30 +0100 Subject: [PATCH] Switch to testr / testtools / fixtures, remove nose * This also fixes us up to hacking standards * Remove assertGreater* usage in favor of assertEqual py26.. * assertEquals > assertEqual Change-Id: I8083c96da09897d650ca6fffc0e340d9c057db8d --- .gitignore | 1 + .testr.conf | 4 + designate.sublime-project | 1 - designate/openstack/common/fileutils.py | 139 ++++++++ .../openstack/common/fixture/__init__.py | 0 designate/openstack/common/fixture/config.py | 46 +++ .../openstack/common/fixture/lockutils.py | 53 +++ .../openstack/common/fixture/mockpatch.py | 51 +++ .../openstack/common/fixture/moxstubout.py | 34 ++ designate/openstack/common/lockutils.py | 305 ++++++++++++++++++ designate/openstack/common/test.py | 53 +++ designate/tests/__init__.py | 61 +++- designate/tests/test_agent/__init__.py | 2 +- designate/tests/test_agent/test_service.py | 2 - designate/tests/test_api/__init__.py | 2 +- designate/tests/test_api/test_middleware.py | 6 - designate/tests/test_api/test_service.py | 2 - designate/tests/test_api/test_v1/__init__.py | 2 - .../tests/test_api/test_v1/test_domains.py | 2 - .../tests/test_api/test_v1/test_records.py | 2 - .../tests/test_api/test_v1/test_servers.py | 2 - designate/tests/test_api/test_v2/__init__.py | 2 - .../tests/test_api/test_v2/test_limits.py | 2 - .../tests/test_api/test_v2/test_zones.py | 2 - designate/tests/test_backend/__init__.py | 2 - designate/tests/test_backend/test_bind9.py | 2 - designate/tests/test_backend/test_dnsmasq.py | 2 - designate/tests/test_backend/test_fake.py | 2 - .../tests/test_backend/test_mysqlbind9.py | 2 - designate/tests/test_backend/test_powerdns.py | 2 - designate/tests/test_central/__init__.py | 2 +- designate/tests/test_central/test_service.py | 74 ++--- .../test_notification_handler/__init__.py | 9 +- .../test_notification_handler/test_nova.py | 4 +- .../test_notification_handler/test_quantum.py | 4 +- designate/tests/test_quota/__init__.py | 21 +- designate/tests/test_quota/test_noop.py | 2 - designate/tests/test_quota/test_storage.py | 18 +- .../test_resources/test_schemas/test_v2.py | 13 +- designate/tests/test_storage/__init__.py | 88 +++-- designate/tests/test_storage/test_api.py | 79 +++-- .../tests/test_storage/test_sqlalchemy.py | 9 +- designate/tests/test_utils.py | 7 +- openstack-common.conf | 1 + test-requirements.txt | 12 +- tox.ini | 14 +- 46 files changed, 906 insertions(+), 239 deletions(-) create mode 100644 .testr.conf create mode 100644 designate/openstack/common/fileutils.py create mode 100644 designate/openstack/common/fixture/__init__.py create mode 100644 designate/openstack/common/fixture/config.py create mode 100644 designate/openstack/common/fixture/lockutils.py create mode 100644 designate/openstack/common/fixture/mockpatch.py create mode 100644 designate/openstack/common/fixture/moxstubout.py create mode 100644 designate/openstack/common/lockutils.py create mode 100644 designate/openstack/common/test.py diff --git a/.gitignore b/.gitignore index d0bca8320..9ed6b9e5d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ designate/versioninfo *.DS_Store *.idea /bind9 +.testrepository/* diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 000000000..60477e871 --- /dev/null +++ b/.testr.conf @@ -0,0 +1,4 @@ +[DEFAULT] +test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} ${PYTHON:-python} -m subunit.run discover -t ./ ./ $LISTOPT $IDOPTION +test_id_option=--load-list $IDFILE +test_list_option=--list diff --git a/designate.sublime-project b/designate.sublime-project index 87c975545..dffc7884f 100644 --- a/designate.sublime-project +++ b/designate.sublime-project @@ -24,7 +24,6 @@ "*.psd", "*.db", ".vagrant", - ".noseids" ], "folder_exclude_patterns": [ diff --git a/designate/openstack/common/fileutils.py b/designate/openstack/common/fileutils.py new file mode 100644 index 000000000..8fb7c2859 --- /dev/null +++ b/designate/openstack/common/fileutils.py @@ -0,0 +1,139 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import contextlib +import errno +import os +import tempfile + +from designate.openstack.common import excutils +from designate.openstack.common.gettextutils import _ # noqa +from designate.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + +_FILE_CACHE = {} + + +def ensure_tree(path): + """Create a directory (and any ancestor directories required) + + :param path: Directory to create + """ + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST: + if not os.path.isdir(path): + raise + else: + raise + + +def read_cached_file(filename, force_reload=False): + """Read from a file if it has been modified. + + :param force_reload: Whether to reload the file. + :returns: A tuple with a boolean specifying if the data is fresh + or not. + """ + global _FILE_CACHE + + if force_reload and filename in _FILE_CACHE: + del _FILE_CACHE[filename] + + reloaded = False + mtime = os.path.getmtime(filename) + cache_info = _FILE_CACHE.setdefault(filename, {}) + + if not cache_info or mtime > cache_info.get('mtime', 0): + LOG.debug(_("Reloading cached file %s") % filename) + with open(filename) as fap: + cache_info['data'] = fap.read() + cache_info['mtime'] = mtime + reloaded = True + return (reloaded, cache_info['data']) + + +def delete_if_exists(path, remove=os.unlink): + """Delete a file, but ignore file not found error. + + :param path: File to delete + :param remove: Optional function to remove passed path + """ + + try: + remove(path) + except OSError as e: + if e.errno != errno.ENOENT: + raise + + +@contextlib.contextmanager +def remove_path_on_error(path, remove=delete_if_exists): + """Protect code that wants to operate on PATH atomically. + Any exception will cause PATH to be removed. + + :param path: File to work with + :param remove: Optional function to remove passed path + """ + + try: + yield + except Exception: + with excutils.save_and_reraise_exception(): + remove(path) + + +def file_open(*args, **kwargs): + """Open file + + see built-in file() documentation for more details + + Note: The reason this is kept in a separate module is to easily + be able to provide a stub module that doesn't alter system + state at all (for unit tests) + """ + return file(*args, **kwargs) + + +def write_to_tempfile(content, path=None, suffix='', prefix='tmp'): + """Create temporary file or use existing file. + + This util is needed for creating temporary file with + specified content, suffix and prefix. If path is not None, + it will be used for writing content. If the path doesn't + exist it'll be created. + + :param content: content for temporary file. + :param path: same as parameter 'dir' for mkstemp + :param suffix: same as parameter 'suffix' for mkstemp + :param prefix: same as parameter 'prefix' for mkstemp + + For example: it can be used in database tests for creating + configuration files. + """ + if path: + ensure_tree(path) + + (fd, path) = tempfile.mkstemp(suffix=suffix, dir=path, prefix=prefix) + try: + os.write(fd, content) + finally: + os.close(fd) + return path diff --git a/designate/openstack/common/fixture/__init__.py b/designate/openstack/common/fixture/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/designate/openstack/common/fixture/config.py b/designate/openstack/common/fixture/config.py new file mode 100644 index 000000000..93748c1c9 --- /dev/null +++ b/designate/openstack/common/fixture/config.py @@ -0,0 +1,46 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2013 Mirantis, Inc. +# Copyright 2013 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import fixtures +from oslo.config import cfg +import six + + +class Config(fixtures.Fixture): + """Override some configuration values. + + The keyword arguments are the names of configuration options to + override and their values. + + If a group argument is supplied, the overrides are applied to + the specified configuration option group. + + All overrides are automatically cleared at the end of the current + test by the reset() method, which is registered by addCleanup(). + """ + + def __init__(self, conf=cfg.CONF): + self.conf = conf + + def setUp(self): + super(Config, self).setUp() + self.addCleanup(self.conf.reset) + + def config(self, **kw): + group = kw.pop('group', None) + for k, v in six.iteritems(kw): + self.conf.set_override(k, v, group) diff --git a/designate/openstack/common/fixture/lockutils.py b/designate/openstack/common/fixture/lockutils.py new file mode 100644 index 000000000..2c0dd1010 --- /dev/null +++ b/designate/openstack/common/fixture/lockutils.py @@ -0,0 +1,53 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import fixtures + +from designate.openstack.common.lockutils import lock + + +class LockFixture(fixtures.Fixture): + """External locking fixture. + + This fixture is basically an alternative to the synchronized decorator with + the external flag so that tearDowns and addCleanups will be included in + the lock context for locking between tests. The fixture is recommended to + be the first line in a test method, like so:: + + def test_method(self): + self.useFixture(LockFixture) + ... + + or the first line in setUp if all the test methods in the class are + required to be serialized. Something like:: + + class TestCase(testtools.testcase): + def setUp(self): + self.useFixture(LockFixture) + super(TestCase, self).setUp() + ... + + This is because addCleanups are put on a LIFO queue that gets run after the + test method exits. (either by completing or raising an exception) + """ + def __init__(self, name, lock_file_prefix=None): + self.mgr = lock(name, lock_file_prefix, True) + + def setUp(self): + super(LockFixture, self).setUp() + self.addCleanup(self.mgr.__exit__, None, None, None) + self.mgr.__enter__() diff --git a/designate/openstack/common/fixture/mockpatch.py b/designate/openstack/common/fixture/mockpatch.py new file mode 100644 index 000000000..cd0d6ca6b --- /dev/null +++ b/designate/openstack/common/fixture/mockpatch.py @@ -0,0 +1,51 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import fixtures +import mock + + +class PatchObject(fixtures.Fixture): + """Deal with code around mock.""" + + def __init__(self, obj, attr, **kwargs): + self.obj = obj + self.attr = attr + self.kwargs = kwargs + + def setUp(self): + super(PatchObject, self).setUp() + _p = mock.patch.object(self.obj, self.attr, **self.kwargs) + self.mock = _p.start() + self.addCleanup(_p.stop) + + +class Patch(fixtures.Fixture): + + """Deal with code around mock.patch.""" + + def __init__(self, obj, **kwargs): + self.obj = obj + self.kwargs = kwargs + + def setUp(self): + super(Patch, self).setUp() + _p = mock.patch(self.obj, **self.kwargs) + self.mock = _p.start() + self.addCleanup(_p.stop) diff --git a/designate/openstack/common/fixture/moxstubout.py b/designate/openstack/common/fixture/moxstubout.py new file mode 100644 index 000000000..a0e74fd11 --- /dev/null +++ b/designate/openstack/common/fixture/moxstubout.py @@ -0,0 +1,34 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import fixtures +import mox + + +class MoxStubout(fixtures.Fixture): + """Deal with code around mox and stubout as a fixture.""" + + def setUp(self): + super(MoxStubout, self).setUp() + # emulate some of the mox stuff, we can't use the metaclass + # because it screws with our generators + self.mox = mox.Mox() + self.stubs = self.mox.stubs + self.addCleanup(self.mox.UnsetStubs) + self.addCleanup(self.mox.VerifyAll) diff --git a/designate/openstack/common/lockutils.py b/designate/openstack/common/lockutils.py new file mode 100644 index 000000000..413a7f000 --- /dev/null +++ b/designate/openstack/common/lockutils.py @@ -0,0 +1,305 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import contextlib +import errno +import functools +import os +import shutil +import subprocess +import sys +import tempfile +import threading +import time +import weakref + +from oslo.config import cfg + +from designate.openstack.common import fileutils +from designate.openstack.common.gettextutils import _ # noqa +from designate.openstack.common import local +from designate.openstack.common import log as logging + + +LOG = logging.getLogger(__name__) + + +util_opts = [ + cfg.BoolOpt('disable_process_locking', default=False, + help='Whether to disable inter-process locks'), + cfg.StrOpt('lock_path', + default=os.environ.get("DESIGNATE_LOCK_PATH"), + help=('Directory to use for lock files.')) +] + + +CONF = cfg.CONF +CONF.register_opts(util_opts) + + +def set_defaults(lock_path): + cfg.set_defaults(util_opts, lock_path=lock_path) + + +class _InterProcessLock(object): + """Lock implementation which allows multiple locks, working around + issues like bugs.debian.org/cgi-bin/bugreport.cgi?bug=632857 and does + not require any cleanup. Since the lock is always held on a file + descriptor rather than outside of the process, the lock gets dropped + automatically if the process crashes, even if __exit__ is not executed. + + There are no guarantees regarding usage by multiple green threads in a + single process here. This lock works only between processes. Exclusive + access between local threads should be achieved using the semaphores + in the @synchronized decorator. + + Note these locks are released when the descriptor is closed, so it's not + safe to close the file descriptor while another green thread holds the + lock. Just opening and closing the lock file can break synchronisation, + so lock files must be accessed only using this abstraction. + """ + + def __init__(self, name): + self.lockfile = None + self.fname = name + + def __enter__(self): + self.lockfile = open(self.fname, 'w') + + while True: + try: + # Using non-blocking locks since green threads are not + # patched to deal with blocking locking calls. + # Also upon reading the MSDN docs for locking(), it seems + # to have a laughable 10 attempts "blocking" mechanism. + self.trylock() + return self + except IOError as e: + if e.errno in (errno.EACCES, errno.EAGAIN): + # external locks synchronise things like iptables + # updates - give it some time to prevent busy spinning + time.sleep(0.01) + else: + raise + + def __exit__(self, exc_type, exc_val, exc_tb): + try: + self.unlock() + self.lockfile.close() + except IOError: + LOG.exception(_("Could not release the acquired lock `%s`"), + self.fname) + + def trylock(self): + raise NotImplementedError() + + def unlock(self): + raise NotImplementedError() + + +class _WindowsLock(_InterProcessLock): + def trylock(self): + msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_NBLCK, 1) + + def unlock(self): + msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1) + + +class _PosixLock(_InterProcessLock): + def trylock(self): + fcntl.lockf(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) + + def unlock(self): + fcntl.lockf(self.lockfile, fcntl.LOCK_UN) + + +if os.name == 'nt': + import msvcrt + InterProcessLock = _WindowsLock +else: + import fcntl + InterProcessLock = _PosixLock + +_semaphores = weakref.WeakValueDictionary() +_semaphores_lock = threading.Lock() + + +@contextlib.contextmanager +def lock(name, lock_file_prefix=None, external=False, lock_path=None): + """Context based lock + + This function yields a `threading.Semaphore` instance (if we don't use + eventlet.monkey_patch(), else `semaphore.Semaphore`) unless external is + True, in which case, it'll yield an InterProcessLock instance. + + :param lock_file_prefix: The lock_file_prefix argument is used to provide + lock files on disk with a meaningful prefix. + + :param external: The external keyword argument denotes whether this lock + should work across multiple processes. This means that if two different + workers both run a a method decorated with @synchronized('mylock', + external=True), only one of them will execute at a time. + + :param lock_path: The lock_path keyword argument is used to specify a + special location for external lock files to live. If nothing is set, then + CONF.lock_path is used as a default. + """ + with _semaphores_lock: + try: + sem = _semaphores[name] + except KeyError: + sem = threading.Semaphore() + _semaphores[name] = sem + + with sem: + LOG.debug(_('Got semaphore "%(lock)s"'), {'lock': name}) + + # NOTE(mikal): I know this looks odd + if not hasattr(local.strong_store, 'locks_held'): + local.strong_store.locks_held = [] + local.strong_store.locks_held.append(name) + + try: + if external and not CONF.disable_process_locking: + LOG.debug(_('Attempting to grab file lock "%(lock)s"'), + {'lock': name}) + + # We need a copy of lock_path because it is non-local + local_lock_path = lock_path or CONF.lock_path + if not local_lock_path: + raise cfg.RequiredOptError('lock_path') + + if not os.path.exists(local_lock_path): + fileutils.ensure_tree(local_lock_path) + LOG.info(_('Created lock path: %s'), local_lock_path) + + def add_prefix(name, prefix): + if not prefix: + return name + sep = '' if prefix.endswith('-') else '-' + return '%s%s%s' % (prefix, sep, name) + + # NOTE(mikal): the lock name cannot contain directory + # separators + lock_file_name = add_prefix(name.replace(os.sep, '_'), + lock_file_prefix) + + lock_file_path = os.path.join(local_lock_path, lock_file_name) + + try: + lock = InterProcessLock(lock_file_path) + with lock as lock: + LOG.debug(_('Got file lock "%(lock)s" at %(path)s'), + {'lock': name, 'path': lock_file_path}) + yield lock + finally: + LOG.debug(_('Released file lock "%(lock)s" at %(path)s'), + {'lock': name, 'path': lock_file_path}) + else: + yield sem + + finally: + local.strong_store.locks_held.remove(name) + + +def synchronized(name, lock_file_prefix=None, external=False, lock_path=None): + """Synchronization decorator. + + Decorating a method like so:: + + @synchronized('mylock') + def foo(self, *args): + ... + + ensures that only one thread will execute the foo method at a time. + + Different methods can share the same lock:: + + @synchronized('mylock') + def foo(self, *args): + ... + + @synchronized('mylock') + def bar(self, *args): + ... + + This way only one of either foo or bar can be executing at a time. + """ + + def wrap(f): + @functools.wraps(f) + def inner(*args, **kwargs): + try: + with lock(name, lock_file_prefix, external, lock_path): + LOG.debug(_('Got semaphore / lock "%(function)s"'), + {'function': f.__name__}) + return f(*args, **kwargs) + finally: + LOG.debug(_('Semaphore / lock released "%(function)s"'), + {'function': f.__name__}) + return inner + return wrap + + +def synchronized_with_prefix(lock_file_prefix): + """Partial object generator for the synchronization decorator. + + Redefine @synchronized in each project like so:: + + (in nova/utils.py) + from nova.openstack.common import lockutils + + synchronized = lockutils.synchronized_with_prefix('nova-') + + + (in nova/foo.py) + from nova import utils + + @utils.synchronized('mylock') + def bar(self, *args): + ... + + The lock_file_prefix argument is used to provide lock files on disk with a + meaningful prefix. + """ + + return functools.partial(synchronized, lock_file_prefix=lock_file_prefix) + + +def main(argv): + """Create a dir for locks and pass it to command from arguments + + If you run this: + python -m openstack.common.lockutils python setup.py testr + + a temporary directory will be created for all your locks and passed to all + your tests in an environment variable. The temporary dir will be deleted + afterwards and the return value will be preserved. + """ + + lock_dir = tempfile.mkdtemp() + os.environ["DESIGNATE_LOCK_PATH"] = lock_dir + try: + ret_val = subprocess.call(argv[1:]) + finally: + shutil.rmtree(lock_dir, ignore_errors=True) + return ret_val + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/designate/openstack/common/test.py b/designate/openstack/common/test.py new file mode 100644 index 000000000..09d9210a8 --- /dev/null +++ b/designate/openstack/common/test.py @@ -0,0 +1,53 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Common utilities used in testing""" + +import os + +import fixtures +import testtools + +_TRUE_VALUES = ('True', 'true', '1', 'yes') + + +class BaseTestCase(testtools.TestCase): + def setUp(self): + super(BaseTestCase, self).setUp() + self._set_timeout() + self._fake_output() + self.useFixture(fixtures.FakeLogger('designate.openstack.common')) + self.useFixture(fixtures.NestedTempfile()) + self.useFixture(fixtures.TempHomeDir()) + + def _set_timeout(self): + test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) + try: + test_timeout = int(test_timeout) + except ValueError: + # If timeout value is invalid do not set a timeout. + test_timeout = 0 + if test_timeout > 0: + self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) + + def _fake_output(self): + if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES: + stdout = self.useFixture(fixtures.StringStream('stdout')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) + if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES: + stderr = self.useFixture(fixtures.StringStream('stderr')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) diff --git a/designate/tests/__init__.py b/designate/tests/__init__.py index f55082fcd..ac9d146d8 100644 --- a/designate/tests/__init__.py +++ b/designate/tests/__init__.py @@ -14,14 +14,16 @@ # License for the specific language governing permissions and limitations # under the License. import copy -import unittest2 -import mox -import nose +import fixtures +import functools import os +from testtools import testcase from oslo.config import cfg from designate.openstack.common import log as logging from designate.openstack.common.notifier import test_notifier +from designate.openstack.common.fixture import config from designate.openstack.common import policy +from designate.openstack.common import test from designate.context import DesignateContext from designate import storage from designate import exceptions @@ -42,7 +44,18 @@ cfg.CONF.import_opt('database_connection', 'designate.storage.impl_sqlalchemy', group='storage:sqlalchemy') -class TestCase(unittest2.TestCase): +class NotifierFixture(fixtures.Fixture): + def tearDown(self): + self.clear() + + def get(self): + return test_notifier.NOTIFICATIONS + + def clear(self): + test_notifier.NOTIFICATIONS = [] + + +class TestCase(test.BaseTestCase): quota_fixtures = [{ 'tenant_id': '12345', 'resource': 'domains', @@ -90,7 +103,7 @@ class TestCase(unittest2.TestCase): def setUp(self): super(TestCase, self).setUp() - self.mox = mox.Mox() + self.CONF = self.useFixture(config.Config(cfg.CONF)).conf self.config( notification_driver=[ @@ -120,6 +133,11 @@ class TestCase(unittest2.TestCase): group='storage:sqlalchemy' ) + self.CONF([], project='designate') + + self.notifications = NotifierFixture() + self.useFixture(self.notifications) + storage.setup_schema() self.admin_context = self.get_admin_context() @@ -128,13 +146,8 @@ class TestCase(unittest2.TestCase): self.reset_notifications() policy.reset() storage.teardown_schema() - cfg.CONF.reset() - self.mox.UnsetStubs() super(TestCase, self).tearDown() - def skip(self, message=None): - raise nose.SkipTest(message) - # Config Methods def config(self, **kwargs): group = kwargs.pop('group', None) @@ -156,10 +169,10 @@ class TestCase(unittest2.TestCase): # Other Utility Methods def get_notifications(self): - return test_notifier.NOTIFICATIONS + return self.notifications.get() def reset_notifications(self): - test_notifier.NOTIFICATIONS = [] + self.notifications.clear() # Service Methods def get_agent_service(self): @@ -264,3 +277,27 @@ class TestCase(unittest2.TestCase): values=kwargs) return self.central_service.create_record(context, domain['id'], values=values) + + +def _skip_decorator(func): + @functools.wraps(func) + def skip_if_not_implemented(*args, **kwargs): + try: + return func(*args, **kwargs) + except NotImplementedError as e: + raise testcase.TestSkipped(str(e)) + except Exception as e: + if 'not implemented' in str(e): + raise testcase.TestSkipped(str(e)) + raise + return skip_if_not_implemented + + +class SkipNotImplementedMeta(type): + def __new__(cls, name, bases, local): + for attr in local: + value = local[attr] + if callable(value) and ( + attr.startswith('test_') or attr == 'setUp'): + local[attr] = _skip_decorator(value) + return type.__new__(cls, name, bases, local) diff --git a/designate/tests/test_agent/__init__.py b/designate/tests/test_agent/__init__.py index 705debf1e..5d34fe135 100644 --- a/designate/tests/test_agent/__init__.py +++ b/designate/tests/test_agent/__init__.py @@ -17,4 +17,4 @@ from designate.tests import TestCase class AgentTestCase(TestCase): - __test__ = False + pass diff --git a/designate/tests/test_agent/test_service.py b/designate/tests/test_agent/test_service.py index 1a19b8baa..e4fb32900 100644 --- a/designate/tests/test_agent/test_service.py +++ b/designate/tests/test_agent/test_service.py @@ -17,8 +17,6 @@ from designate.tests.test_agent import AgentTestCase class AgentServiceTest(AgentTestCase): - __test__ = True - def setUp(self): super(AgentServiceTest, self).setUp() self.service = self.get_agent_service() diff --git a/designate/tests/test_api/__init__.py b/designate/tests/test_api/__init__.py index e1467a770..bf9cc4d7f 100644 --- a/designate/tests/test_api/__init__.py +++ b/designate/tests/test_api/__init__.py @@ -17,4 +17,4 @@ from designate.tests import TestCase class ApiTestCase(TestCase): - __test__ = False + pass diff --git a/designate/tests/test_api/test_middleware.py b/designate/tests/test_api/test_middleware.py index b860c57b5..af59274ba 100644 --- a/designate/tests/test_api/test_middleware.py +++ b/designate/tests/test_api/test_middleware.py @@ -33,8 +33,6 @@ class FakeRequest(object): class MaintenanceMiddlewareTest(ApiTestCase): - __test__ = True - def test_process_request_disabled(self): self.config(maintenance_mode=False, group='service:api') @@ -107,8 +105,6 @@ class MaintenanceMiddlewareTest(ApiTestCase): class KeystoneContextMiddlewareTest(ApiTestCase): - __test__ = True - def test_process_request(self): app = middleware.KeystoneContextMiddleware({}) @@ -168,8 +164,6 @@ class KeystoneContextMiddlewareTest(ApiTestCase): class NoAuthContextMiddlewareTest(ApiTestCase): - __test__ = True - def test_process_request(self): app = middleware.NoAuthContextMiddleware({}) diff --git a/designate/tests/test_api/test_service.py b/designate/tests/test_api/test_service.py index 6e2ceb5d3..538eb33b6 100644 --- a/designate/tests/test_api/test_service.py +++ b/designate/tests/test_api/test_service.py @@ -17,8 +17,6 @@ from designate.tests.test_api import ApiTestCase class ApiServiceTest(ApiTestCase): - __test__ = True - def setUp(self): super(ApiServiceTest, self).setUp() diff --git a/designate/tests/test_api/test_v1/__init__.py b/designate/tests/test_api/test_v1/__init__.py index b6ca653e4..bc31209a1 100644 --- a/designate/tests/test_api/test_v1/__init__.py +++ b/designate/tests/test_api/test_v1/__init__.py @@ -24,8 +24,6 @@ LOG = logging.getLogger(__name__) class ApiV1Test(ApiTestCase): - __test__ = False - def setUp(self): super(ApiV1Test, self).setUp() diff --git a/designate/tests/test_api/test_v1/test_domains.py b/designate/tests/test_api/test_v1/test_domains.py index 3acbbd5d6..2a2dcdf6d 100644 --- a/designate/tests/test_api/test_v1/test_domains.py +++ b/designate/tests/test_api/test_v1/test_domains.py @@ -26,8 +26,6 @@ LOG = logging.getLogger(__name__) class ApiV1DomainsTest(ApiV1Test): - __test__ = True - def test_create_domain(self): # Create a server self.create_server() diff --git a/designate/tests/test_api/test_v1/test_records.py b/designate/tests/test_api/test_v1/test_records.py index 7e45332e9..240586193 100644 --- a/designate/tests/test_api/test_v1/test_records.py +++ b/designate/tests/test_api/test_v1/test_records.py @@ -25,8 +25,6 @@ LOG = logging.getLogger(__name__) class ApiV1RecordsTest(ApiV1Test): - __test__ = True - def setUp(self): super(ApiV1RecordsTest, self).setUp() diff --git a/designate/tests/test_api/test_v1/test_servers.py b/designate/tests/test_api/test_v1/test_servers.py index 8b3334c3e..bbdfb673e 100644 --- a/designate/tests/test_api/test_v1/test_servers.py +++ b/designate/tests/test_api/test_v1/test_servers.py @@ -25,8 +25,6 @@ LOG = logging.getLogger(__name__) class ApiV1ServersTest(ApiV1Test): - __test__ = True - def test_create_server(self): # Create a server fixture = self.get_server_fixture(0) diff --git a/designate/tests/test_api/test_v2/__init__.py b/designate/tests/test_api/test_v2/__init__.py index 6dbdd9e52..664637945 100644 --- a/designate/tests/test_api/test_v2/__init__.py +++ b/designate/tests/test_api/test_v2/__init__.py @@ -24,8 +24,6 @@ LOG = logging.getLogger(__name__) class ApiV2TestCase(ApiTestCase): - __test__ = False - def setUp(self): super(ApiV2TestCase, self).setUp() diff --git a/designate/tests/test_api/test_v2/test_limits.py b/designate/tests/test_api/test_v2/test_limits.py index db68723c0..69dbd5e81 100644 --- a/designate/tests/test_api/test_v2/test_limits.py +++ b/designate/tests/test_api/test_v2/test_limits.py @@ -18,8 +18,6 @@ from designate.tests.test_api.test_v2 import ApiV2TestCase class ApiV2LimitsTest(ApiV2TestCase): - __test__ = True - def test_get_limits(self): response = self.client.get('/limits/') diff --git a/designate/tests/test_api/test_v2/test_zones.py b/designate/tests/test_api/test_v2/test_zones.py index 9bfde4b93..08b332bf4 100644 --- a/designate/tests/test_api/test_v2/test_zones.py +++ b/designate/tests/test_api/test_v2/test_zones.py @@ -22,8 +22,6 @@ from designate.tests.test_api.test_v2 import ApiV2TestCase class ApiV2ZonesTest(ApiV2TestCase): - __test__ = True - def setUp(self): super(ApiV2ZonesTest, self).setUp() diff --git a/designate/tests/test_backend/__init__.py b/designate/tests/test_backend/__init__.py index a8a97cf26..b0c306c5a 100644 --- a/designate/tests/test_backend/__init__.py +++ b/designate/tests/test_backend/__init__.py @@ -22,8 +22,6 @@ LOG = logging.getLogger(__name__) class BackendTestCase(TestCase): - __test__ = False - def get_backend_driver(self): central_service = self.get_central_service() return backend.get_backend(cfg.CONF['service:agent'].backend_driver, diff --git a/designate/tests/test_backend/test_bind9.py b/designate/tests/test_backend/test_bind9.py index 8da31b9af..31f01d726 100644 --- a/designate/tests/test_backend/test_bind9.py +++ b/designate/tests/test_backend/test_bind9.py @@ -20,8 +20,6 @@ LOG = logging.getLogger(__name__) class Bind9BackendDriverTestCase(BackendTestCase): - __test__ = True - def setUp(self): super(Bind9BackendDriverTestCase, self).setUp() diff --git a/designate/tests/test_backend/test_dnsmasq.py b/designate/tests/test_backend/test_dnsmasq.py index 786a21d13..bc5f012e4 100644 --- a/designate/tests/test_backend/test_dnsmasq.py +++ b/designate/tests/test_backend/test_dnsmasq.py @@ -20,8 +20,6 @@ LOG = logging.getLogger(__name__) class DnsmasqBackendDriverTestCase(BackendTestCase): - __test__ = True - def setUp(self): super(DnsmasqBackendDriverTestCase, self).setUp() diff --git a/designate/tests/test_backend/test_fake.py b/designate/tests/test_backend/test_fake.py index 63ad1a605..d4adc0376 100644 --- a/designate/tests/test_backend/test_fake.py +++ b/designate/tests/test_backend/test_fake.py @@ -20,8 +20,6 @@ LOG = logging.getLogger(__name__) class FakeBackendDriverTestCase(BackendTestCase): - __test__ = True - def setUp(self): super(FakeBackendDriverTestCase, self).setUp() diff --git a/designate/tests/test_backend/test_mysqlbind9.py b/designate/tests/test_backend/test_mysqlbind9.py index 9c254a273..6cf8519a2 100644 --- a/designate/tests/test_backend/test_mysqlbind9.py +++ b/designate/tests/test_backend/test_mysqlbind9.py @@ -20,8 +20,6 @@ LOG = logging.getLogger(__name__) class MySQLBind9BackendDriverTestCase(BackendTestCase): - __test__ = True - def setUp(self): super(MySQLBind9BackendDriverTestCase, self).setUp() diff --git a/designate/tests/test_backend/test_powerdns.py b/designate/tests/test_backend/test_powerdns.py index b34a13b53..dfe8fd775 100644 --- a/designate/tests/test_backend/test_powerdns.py +++ b/designate/tests/test_backend/test_powerdns.py @@ -20,8 +20,6 @@ LOG = logging.getLogger(__name__) class PowerDNSBackendDriverTestCase(BackendTestCase): - __test__ = True - def setUp(self): super(PowerDNSBackendDriverTestCase, self).setUp() diff --git a/designate/tests/test_central/__init__.py b/designate/tests/test_central/__init__.py index c61c7e081..088df403c 100644 --- a/designate/tests/test_central/__init__.py +++ b/designate/tests/test_central/__init__.py @@ -17,4 +17,4 @@ from designate.tests import TestCase class CentralTestCase(TestCase): - __test__ = False + pass diff --git a/designate/tests/test_central/test_service.py b/designate/tests/test_central/test_service.py index 34d84dee9..a879df102 100644 --- a/designate/tests/test_central/test_service.py +++ b/designate/tests/test_central/test_service.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. import random +import testtools from designate.openstack.common import log as logging from designate import exceptions from designate.tests.test_central import CentralTestCase @@ -22,8 +23,6 @@ LOG = logging.getLogger(__name__) class CentralServiceTest(CentralTestCase): - __test__ = True - def setUp(self): super(CentralServiceTest, self).setUp() self.central_service = self.get_central_service() @@ -42,10 +41,10 @@ class CentralServiceTest(CentralTestCase): self.central_service._is_valid_domain_name(context, 'valid.org.') - with self.assertRaises(exceptions.InvalidDomainName): + with testtools.ExpectedException(exceptions.InvalidDomainName): self.central_service._is_valid_domain_name(context, 'example.org.') - with self.assertRaises(exceptions.InvalidDomainName): + with testtools.ExpectedException(exceptions.InvalidDomainName): self.central_service._is_valid_domain_name(context, 'example.tld.') def test_is_valid_record_name(self): @@ -61,15 +60,15 @@ class CentralServiceTest(CentralTestCase): 'valid.example.org.', 'A') - with self.assertRaises(exceptions.InvalidRecordName): + with testtools.ExpectedException(exceptions.InvalidRecordName): self.central_service._is_valid_record_name( context, domain, 'toolong.example.org.', 'A') - with self.assertRaises(exceptions.InvalidRecordLocation): + with testtools.ExpectedException(exceptions.InvalidRecordLocation): self.central_service._is_valid_record_name( context, domain, 'a.example.COM.', 'A') - with self.assertRaises(exceptions.InvalidRecordLocation): + with testtools.ExpectedException(exceptions.InvalidRecordLocation): self.central_service._is_valid_record_name( context, domain, 'example.org.', 'CNAME') @@ -201,12 +200,15 @@ class CentralServiceTest(CentralTestCase): self.central_service.delete_server(context, server['id']) # Fetch the server again, ensuring an exception is raised - with self.assertRaises(exceptions.ServerNotFound): - self.central_service.get_server(context, server['id']) + self.assertRaises( + exceptions.ServerNotFound, + self.central_service.get_server, + context, server['id']) # Try to delete last remaining server - expect exception - with self.assertRaises(exceptions.LastServerDeleteNotAllowed): - self.central_service.delete_server(context, server2['id']) + self.assertRaises( + exceptions.LastServerDeleteNotAllowed, + self.central_service.delete_server, context, server2['id']) # TsigKey Tests def test_create_tsigkey(self): @@ -288,7 +290,7 @@ class CentralServiceTest(CentralTestCase): self.central_service.delete_tsigkey(context, tsigkey['id']) # Fetch the tsigkey again, ensuring an exception is raised - with self.assertRaises(exceptions.TsigKeyNotFound): + with testtools.ExpectedException(exceptions.TsigKeyNotFound): self.central_service.get_tsigkey(context, tsigkey['id']) # Tenant Tests @@ -311,7 +313,7 @@ class CentralServiceTest(CentralTestCase): # Set the policy to reject the authz self.policy({'count_tenants': '!'}) - with self.assertRaises(exceptions.Forbidden): + with testtools.ExpectedException(exceptions.Forbidden): self.central_service.count_tenants(self.get_context()) # Domain Tests @@ -360,7 +362,7 @@ class CentralServiceTest(CentralTestCase): self.create_domain() - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.create_domain() def test_create_subdomain(self): @@ -402,7 +404,7 @@ class CentralServiceTest(CentralTestCase): values['name'] = 'www.%s' % parent_domain['name'] # Attempt to create the subdomain - with self.assertRaises(exceptions.Forbidden): + with testtools.ExpectedException(exceptions.Forbidden): self.central_service.create_domain(context, values=values) def test_create_blacklisted_domain_success(self): @@ -444,7 +446,7 @@ class CentralServiceTest(CentralTestCase): email='info@blacklisted.com' ) - with self.assertRaises(exceptions.InvalidDomainName): + with testtools.ExpectedException(exceptions.InvalidDomainName): # Create a domain self.central_service.create_domain(context, values=values) @@ -470,7 +472,7 @@ class CentralServiceTest(CentralTestCase): email='info@invalid.com' ) - with self.assertRaises(exceptions.InvalidTLD): + with testtools.ExpectedException(exceptions.InvalidTLD): # Create an invalid domain self.central_service.create_domain(context, values=values) @@ -561,7 +563,7 @@ class CentralServiceTest(CentralTestCase): # Retrieve the servers list servers = self.central_service.get_domain_servers(context, domain['id']) - self.assertGreater(len(servers), 0) + self.assertTrue(len(servers) > 0) def test_find_domain(self): context = self.get_admin_context() @@ -597,7 +599,7 @@ class CentralServiceTest(CentralTestCase): expected_domain['id']) # Ensure the domain was updated correctly - self.assertGreater(domain['serial'], expected_domain['serial']) + self.assertTrue(domain['serial'] > expected_domain['serial']) self.assertEqual(domain['email'], 'new@example.com') # Ensure we sent exactly 1 notification @@ -644,7 +646,7 @@ class CentralServiceTest(CentralTestCase): expected_domain = self.create_domain() # Update the domain - with self.assertRaises(exceptions.BadRequest): + with testtools.ExpectedException(exceptions.BadRequest): values = dict(name='renamed-domain.com.') self.central_service.update_domain(context, expected_domain['id'], values=values) @@ -662,7 +664,7 @@ class CentralServiceTest(CentralTestCase): self.central_service.delete_domain(context, domain['id']) # Fetch the domain again, ensuring an exception is raised - with self.assertRaises(exceptions.DomainNotFound): + with testtools.ExpectedException(exceptions.DomainNotFound): self.central_service.get_domain(context, domain['id']) # Ensure we sent exactly 1 notification @@ -696,7 +698,7 @@ class CentralServiceTest(CentralTestCase): self.central_service.create_domain(context, values=values) # Attempt to delete the parent domain - with self.assertRaises(exceptions.DomainHasSubdomain): + with testtools.ExpectedException(exceptions.DomainHasSubdomain): self.central_service.delete_domain(context, parent_domain['id']) def test_count_domains(self): @@ -717,7 +719,7 @@ class CentralServiceTest(CentralTestCase): # Set the policy to reject the authz self.policy({'count_domains': '!'}) - with self.assertRaises(exceptions.Forbidden): + with testtools.ExpectedException(exceptions.Forbidden): self.central_service.count_domains(self.get_context()) def test_touch_domain(self): @@ -734,7 +736,7 @@ class CentralServiceTest(CentralTestCase): expected_domain['id']) # Ensure the serial was incremented - self.assertGreater(domain['serial'], expected_domain['serial']) + self.assertTrue(domain['serial'] > expected_domain['serial']) # Record Tests def test_create_record(self): @@ -766,7 +768,7 @@ class CentralServiceTest(CentralTestCase): self.create_record(domain) - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.create_record(domain) def test_create_record_without_incrementing_serial(self): @@ -806,7 +808,7 @@ class CentralServiceTest(CentralTestCase): ) # Attempt to create a CNAME record at the apex - with self.assertRaises(exceptions.InvalidRecordLocation): + with testtools.ExpectedException(exceptions.InvalidRecordLocation): self.central_service.create_record(context, domain['id'], values=values) @@ -874,7 +876,7 @@ class CentralServiceTest(CentralTestCase): values=values) # Attempt to create a CNAME record alongside an A record - with self.assertRaises(exceptions.InvalidRecordLocation): + with testtools.ExpectedException(exceptions.InvalidRecordLocation): values = dict( name='www.%s' % domain['name'], type='CNAME', @@ -898,7 +900,7 @@ class CentralServiceTest(CentralTestCase): values=values) # Attempt to create a CNAME record alongside an A record - with self.assertRaises(exceptions.InvalidRecordLocation): + with testtools.ExpectedException(exceptions.InvalidRecordLocation): values = dict( name='www.%s' % domain['name'], type='A', @@ -922,7 +924,7 @@ class CentralServiceTest(CentralTestCase): values=values) # Attempt to create a second PTR with the same name. - with self.assertRaises(exceptions.DuplicateRecord): + with testtools.ExpectedException(exceptions.DuplicateRecord): values = dict( name='1.%s' % domain['name'], type='PTR', @@ -998,7 +1000,7 @@ class CentralServiceTest(CentralTestCase): expected_record = self.create_record(domain, name=record_name) # Ensure we get a 404 if we use the incorrect domain_id - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): self.central_service.get_record(context, other_domain['id'], expected_record['id']) @@ -1063,7 +1065,7 @@ class CentralServiceTest(CentralTestCase): values = dict(data='127.0.0.2') # Ensure we get a 404 if we use the incorrect domain_id - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): self.central_service.update_record(context, other_domain['id'], expected_record['id'], values=values) @@ -1091,7 +1093,7 @@ class CentralServiceTest(CentralTestCase): values=values) # Attempt to create a second PTR with the same name. - with self.assertRaises(exceptions.DuplicateRecord): + with testtools.ExpectedException(exceptions.DuplicateRecord): values = dict( name='1.%s' % domain['name'] ) @@ -1156,7 +1158,7 @@ class CentralServiceTest(CentralTestCase): self.central_service.delete_record(context, domain['id'], record['id']) # Fetch the record again, ensuring an exception is raised - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): self.central_service.get_record(context, domain['id'], record['id']) @@ -1175,7 +1177,7 @@ class CentralServiceTest(CentralTestCase): increment_serial=False) # Fetch the record again, ensuring an exception is raised - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): self.central_service.get_record(context, domain['id'], record['id']) @@ -1192,7 +1194,7 @@ class CentralServiceTest(CentralTestCase): record = self.create_record(domain) # Ensure we get a 404 if we use the incorrect domain_id - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): self.central_service.delete_record(context, other_domain['id'], record['id']) @@ -1215,5 +1217,5 @@ class CentralServiceTest(CentralTestCase): # Set the policy to reject the authz self.policy({'count_records': '!'}) - with self.assertRaises(exceptions.Forbidden): + with testtools.ExpectedException(exceptions.Forbidden): self.central_service.count_records(self.get_context()) diff --git a/designate/tests/test_notification_handler/__init__.py b/designate/tests/test_notification_handler/__init__.py index 265be55f5..ce897cd6b 100644 --- a/designate/tests/test_notification_handler/__init__.py +++ b/designate/tests/test_notification_handler/__init__.py @@ -15,16 +15,19 @@ # under the License. import json import os +import six +import testtools from designate.notification_handler.base import Handler from designate.tests import TestCase +from designate.tests import SkipNotImplementedMeta FIXTURES_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'sample_notifications')) +@six.add_metaclass(SkipNotImplementedMeta) class NotificationHandlerTestCase(TestCase): - __test__ = False __plugin_base__ = Handler def setUp(self): @@ -47,9 +50,11 @@ class NotificationHandlerTestCase(TestCase): return json.load(fh) def test_invalid_event_type(self): + if not hasattr(self, 'plugin'): + raise NotImplementedError event_type = 'invalid' self.assertNotIn(event_type, self.plugin.get_event_types()) - with self.assertRaises(ValueError): + with testtools.ExpectedException(ValueError): self.plugin.process_notification(event_type, 'payload') diff --git a/designate/tests/test_notification_handler/test_nova.py b/designate/tests/test_notification_handler/test_nova.py index 1fd9690f4..7863667c0 100644 --- a/designate/tests/test_notification_handler/test_nova.py +++ b/designate/tests/test_notification_handler/test_nova.py @@ -22,8 +22,6 @@ LOG = logging.getLogger(__name__) class NovaFixedHandlerTest(NotificationHandlerTestCase): - __test__ = True - def setUp(self): super(NovaFixedHandlerTest, self).setUp() @@ -71,7 +69,7 @@ class NovaFixedHandlerTest(NotificationHandlerTestCase): records = self.central_service.find_records(self.admin_context, self.domain_id) - self.assertGreaterEqual(len(records), 1) + self.assertTrue(len(records) >= 1) self.plugin.process_notification(event_type, fixture['payload']) diff --git a/designate/tests/test_notification_handler/test_quantum.py b/designate/tests/test_notification_handler/test_quantum.py index f276ca186..66781ba73 100644 --- a/designate/tests/test_notification_handler/test_quantum.py +++ b/designate/tests/test_notification_handler/test_quantum.py @@ -22,8 +22,6 @@ LOG = logging.getLogger(__name__) class QuantumFloatingHandlerTest(NotificationHandlerTestCase): - __test__ = True - def setUp(self): super(QuantumFloatingHandlerTest, self).setUp() @@ -71,7 +69,7 @@ class QuantumFloatingHandlerTest(NotificationHandlerTestCase): records = self.central_service.find_records(self.admin_context, self.domain_id) - self.assertGreaterEqual(len(records), 1) + self.assertTrue(len(records) >= 1) self.plugin.process_notification(event_type, fixture['payload']) diff --git a/designate/tests/test_quota/__init__.py b/designate/tests/test_quota/__init__.py index 136abd3ef..20f46f459 100644 --- a/designate/tests/test_quota/__init__.py +++ b/designate/tests/test_quota/__init__.py @@ -13,6 +13,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +import testtools from oslo.config import cfg from designate.openstack.common import log as logging from designate.tests import TestCase @@ -23,8 +24,6 @@ LOG = logging.getLogger(__name__) class QuotaTestCase(TestCase): - __test__ = False - def setUp(self): super(QuotaTestCase, self).setUp() self.quota = quota.get_quota() @@ -43,10 +42,10 @@ class QuotaTestCase(TestCase): def test_limit_check_unknown(self): context = self.get_admin_context() - with self.assertRaises(exceptions.QuotaResourceUnknown): + with testtools.ExpectedException(exceptions.QuotaResourceUnknown): self.quota.limit_check(context, 'tenant_id', unknown=0) - with self.assertRaises(exceptions.QuotaResourceUnknown): + with testtools.ExpectedException(exceptions.QuotaResourceUnknown): self.quota.limit_check(context, 'tenant_id', unknown=0, domains=0) def test_limit_check_under(self): @@ -67,11 +66,11 @@ class QuotaTestCase(TestCase): def test_limit_check_at(self): context = self.get_admin_context() - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.quota.limit_check(context, 'tenant_id', domains=cfg.CONF.quota_domains) - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.quota.limit_check( context, 'tenant_id', @@ -80,20 +79,20 @@ class QuotaTestCase(TestCase): def test_limit_check_over(self): context = self.get_admin_context() - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.quota.limit_check(context, 'tenant_id', domains=99999) - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.quota.limit_check(context, 'tenant_id', domain_records=99999) - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.quota.limit_check(context, 'tenant_id', domains=99999, domain_records=99999) - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.quota.limit_check(context, 'tenant_id', domains=99999, domain_records=0) - with self.assertRaises(exceptions.OverQuota): + with testtools.ExpectedException(exceptions.OverQuota): self.quota.limit_check(context, 'tenant_id', domains=0, domain_records=99999) diff --git a/designate/tests/test_quota/test_noop.py b/designate/tests/test_quota/test_noop.py index 6923c6b60..9a160d7d7 100644 --- a/designate/tests/test_quota/test_noop.py +++ b/designate/tests/test_quota/test_noop.py @@ -20,8 +20,6 @@ LOG = logging.getLogger(__name__) class NoopQuotaTest(QuotaTestCase): - __test__ = True - def setUp(self): self.config(quota_driver='noop') super(NoopQuotaTest, self).setUp() diff --git a/designate/tests/test_quota/test_storage.py b/designate/tests/test_quota/test_storage.py index 6ae904b33..95a233f46 100644 --- a/designate/tests/test_quota/test_storage.py +++ b/designate/tests/test_quota/test_storage.py @@ -20,8 +20,6 @@ LOG = logging.getLogger(__name__) class StorageQuotaTest(QuotaTestCase): - __test__ = True - def setUp(self): self.config(quota_driver='storage') super(StorageQuotaTest, self).setUp() @@ -30,7 +28,7 @@ class StorageQuotaTest(QuotaTestCase): quota = self.quota.set_quota(self.admin_context, 'tenant_id', 'domains', 1500) - self.assertEquals(quota, {'domains': 1500}) + self.assertEqual(quota, {'domains': 1500}) # Drop into the storage layer directly to ensure the quota was created # sucessfully. @@ -42,9 +40,9 @@ class StorageQuotaTest(QuotaTestCase): quota = self.quota.storage_api.find_quota(self.admin_context, criterion) - self.assertEquals(quota['tenant_id'], 'tenant_id') - self.assertEquals(quota['resource'], 'domains') - self.assertEquals(quota['hard_limit'], 1500) + self.assertEqual(quota['tenant_id'], 'tenant_id') + self.assertEqual(quota['resource'], 'domains') + self.assertEqual(quota['hard_limit'], 1500) def test_set_quota_update(self): # First up, Create the quota @@ -63,9 +61,9 @@ class StorageQuotaTest(QuotaTestCase): quota = self.quota.storage_api.find_quota(self.admin_context, criterion) - self.assertEquals(quota['tenant_id'], 'tenant_id') - self.assertEquals(quota['resource'], 'domains') - self.assertEquals(quota['hard_limit'], 1234) + self.assertEqual(quota['tenant_id'], 'tenant_id') + self.assertEqual(quota['resource'], 'domains') + self.assertEqual(quota['hard_limit'], 1234) def test_reset_quotas(self): # First up, Create a domains quota @@ -87,4 +85,4 @@ class StorageQuotaTest(QuotaTestCase): quotas = self.quota.storage_api.find_quotas(self.admin_context, criterion) - self.assertEquals(0, len(quotas)) + self.assertEqual(0, len(quotas)) diff --git a/designate/tests/test_resources/test_schemas/test_v2.py b/designate/tests/test_resources/test_schemas/test_v2.py index b5a8df830..fad6c1d06 100644 --- a/designate/tests/test_resources/test_schemas/test_v2.py +++ b/designate/tests/test_resources/test_schemas/test_v2.py @@ -13,6 +13,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +import testtools from designate.openstack.common import log as logging from designate import exceptions from designate import schema @@ -22,8 +23,6 @@ LOG = logging.getLogger(__name__) class SchemasV2Test(TestCase): - __test__ = True - def test_recordset(self): validator = schema.Schema('v2', 'recordset') @@ -54,7 +53,7 @@ class SchemasV2Test(TestCase): } }) - with self.assertRaises(exceptions.InvalidObject): + with testtools.ExpectedException(exceptions.InvalidObject): # Fail Expected - Empty Records Array validator.validate({ 'recordset': { @@ -66,7 +65,7 @@ class SchemasV2Test(TestCase): } }) - with self.assertRaises(exceptions.InvalidObject): + with testtools.ExpectedException(exceptions.InvalidObject): # Fail Expected - No Records validator.validate({ 'recordset': { @@ -77,7 +76,7 @@ class SchemasV2Test(TestCase): } }) - with self.assertRaises(exceptions.InvalidObject): + with testtools.ExpectedException(exceptions.InvalidObject): # Fail Expected - MX records in an A RRset validator.validate({ 'recordset': { @@ -92,7 +91,7 @@ class SchemasV2Test(TestCase): } }) - with self.assertRaises(exceptions.InvalidObject): + with testtools.ExpectedException(exceptions.InvalidObject): # Fail Expected - A records in an MX RRset validator.validate({ 'recordset': { @@ -107,7 +106,7 @@ class SchemasV2Test(TestCase): } }) - with self.assertRaises(exceptions.InvalidObject): + with testtools.ExpectedException(exceptions.InvalidObject): # Fail Expected - AAAA records in an A RRset validator.validate({ 'recordset': { diff --git a/designate/tests/test_storage/__init__.py b/designate/tests/test_storage/__init__.py index f99388a4d..06e1098b4 100644 --- a/designate/tests/test_storage/__init__.py +++ b/designate/tests/test_storage/__init__.py @@ -13,21 +13,15 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +import testtools + from designate.openstack.common import log as logging -from designate.tests import TestCase -from designate import storage from designate import exceptions LOG = logging.getLogger(__name__) -class StorageTestCase(TestCase): - __test__ = False - - def setUp(self): - super(StorageTestCase, self).setUp() - self.storage = storage.get_storage() - +class StorageTestCase(object): def create_quota(self, fixture=0, values={}): fixture = self.get_quota_fixture(fixture, values) return fixture, self.storage.create_quota(self.admin_context, fixture) @@ -70,7 +64,7 @@ class StorageTestCase(TestCase): # Create the initial quota self.create_quota() - with self.assertRaises(exceptions.DuplicateQuota): + with testtools.ExpectedException(exceptions.DuplicateQuota): self.create_quota() def test_find_quotas(self): @@ -137,7 +131,7 @@ class StorageTestCase(TestCase): self.assertEqual(actual['hard_limit'], expected['hard_limit']) def test_get_quota_missing(self): - with self.assertRaises(exceptions.QuotaNotFound): + with testtools.ExpectedException(exceptions.QuotaNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.get_quota(self.admin_context, uuid) @@ -174,7 +168,7 @@ class StorageTestCase(TestCase): tenant_id=expected['tenant_id'] + "NOT FOUND" ) - with self.assertRaises(exceptions.QuotaNotFound): + with testtools.ExpectedException(exceptions.QuotaNotFound): self.storage.find_quota(self.admin_context, criterion) def test_update_quota(self): @@ -195,12 +189,12 @@ class StorageTestCase(TestCase): values = self.quota_fixtures[0] - with self.assertRaises(exceptions.DuplicateQuota): + with testtools.ExpectedException(exceptions.DuplicateQuota): self.storage.update_quota(self.admin_context, quota['id'], values) def test_update_quota_missing(self): - with self.assertRaises(exceptions.QuotaNotFound): + with testtools.ExpectedException(exceptions.QuotaNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.update_quota(self.admin_context, uuid, {}) @@ -209,11 +203,11 @@ class StorageTestCase(TestCase): self.storage.delete_quota(self.admin_context, quota['id']) - with self.assertRaises(exceptions.QuotaNotFound): + with testtools.ExpectedException(exceptions.QuotaNotFound): self.storage.get_quota(self.admin_context, quota['id']) def test_delete_quota_missing(self): - with self.assertRaises(exceptions.QuotaNotFound): + with testtools.ExpectedException(exceptions.QuotaNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.delete_quota(self.admin_context, uuid) @@ -235,7 +229,7 @@ class StorageTestCase(TestCase): # Create the Initial Server self.create_server() - with self.assertRaises(exceptions.DuplicateServer): + with testtools.ExpectedException(exceptions.DuplicateServer): self.create_server() def test_find_servers(self): @@ -290,7 +284,7 @@ class StorageTestCase(TestCase): self.assertEqual(str(actual['name']), str(expected['name'])) def test_get_server_missing(self): - with self.assertRaises(exceptions.ServerNotFound): + with testtools.ExpectedException(exceptions.ServerNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.get_server(self.admin_context, uuid) @@ -310,12 +304,12 @@ class StorageTestCase(TestCase): values = self.server_fixtures[0] - with self.assertRaises(exceptions.DuplicateServer): + with testtools.ExpectedException(exceptions.DuplicateServer): self.storage.update_server(self.admin_context, server['id'], values) def test_update_server_missing(self): - with self.assertRaises(exceptions.ServerNotFound): + with testtools.ExpectedException(exceptions.ServerNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.update_server(self.admin_context, uuid, {}) @@ -324,11 +318,11 @@ class StorageTestCase(TestCase): self.storage.delete_server(self.admin_context, server['id']) - with self.assertRaises(exceptions.ServerNotFound): + with testtools.ExpectedException(exceptions.ServerNotFound): self.storage.get_server(self.admin_context, server['id']) def test_delete_server_missing(self): - with self.assertRaises(exceptions.ServerNotFound): + with testtools.ExpectedException(exceptions.ServerNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.delete_server(self.admin_context, uuid) @@ -353,7 +347,7 @@ class StorageTestCase(TestCase): values = self.get_tsigkey_fixture(1) values['name'] = tsigkey_one['name'] - with self.assertRaises(exceptions.DuplicateTsigKey): + with testtools.ExpectedException(exceptions.DuplicateTsigKey): self.create_tsigkey(values=values) def test_find_tsigkeys(self): @@ -415,7 +409,7 @@ class StorageTestCase(TestCase): self.assertEqual(actual['secret'], expected['secret']) def test_get_tsigkey_missing(self): - with self.assertRaises(exceptions.TsigKeyNotFound): + with testtools.ExpectedException(exceptions.TsigKeyNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.get_tsigkey(self.admin_context, uuid) @@ -438,12 +432,12 @@ class StorageTestCase(TestCase): values = self.tsigkey_fixtures[0] - with self.assertRaises(exceptions.DuplicateTsigKey): + with testtools.ExpectedException(exceptions.DuplicateTsigKey): self.storage.update_tsigkey(self.admin_context, tsigkey['id'], values) def test_update_tsigkey_missing(self): - with self.assertRaises(exceptions.TsigKeyNotFound): + with testtools.ExpectedException(exceptions.TsigKeyNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.update_tsigkey(self.admin_context, uuid, {}) @@ -452,11 +446,11 @@ class StorageTestCase(TestCase): self.storage.delete_tsigkey(self.admin_context, tsigkey['id']) - with self.assertRaises(exceptions.TsigKeyNotFound): + with testtools.ExpectedException(exceptions.TsigKeyNotFound): self.storage.get_tsigkey(self.admin_context, tsigkey['id']) def test_delete_tsigkey_missing(self): - with self.assertRaises(exceptions.TsigKeyNotFound): + with testtools.ExpectedException(exceptions.TsigKeyNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.delete_tsigkey(self.admin_context, uuid) @@ -481,7 +475,7 @@ class StorageTestCase(TestCase): 'domain_count': 1 }] - self.assertEquals(result, expected) + self.assertEqual(result, expected) def test_get_tenant(self): # create 2 domains in a tenant @@ -494,10 +488,10 @@ class StorageTestCase(TestCase): result = self.storage.get_tenant(self.admin_context, 1) - self.assertEquals(result['id'], 1) - self.assertEquals(result['domain_count'], 2) - self.assertItemsEqual(result['domains'], - [domain_1['name'], domain_2['name']]) + self.assertEqual(result['id'], 1) + self.assertEqual(result['domain_count'], 2) + self.assertEqual(sorted(result['domains']), + [domain_1['name'], domain_2['name']]) def test_count_tenants(self): # in the beginning, there should be nothing @@ -536,7 +530,7 @@ class StorageTestCase(TestCase): # Create the Initial Domain self.create_domain() - with self.assertRaises(exceptions.DuplicateDomain): + with testtools.ExpectedException(exceptions.DuplicateDomain): self.create_domain() def test_find_domains(self): @@ -596,7 +590,7 @@ class StorageTestCase(TestCase): self.assertIn('status', actual) def test_get_domain_missing(self): - with self.assertRaises(exceptions.DomainNotFound): + with testtools.ExpectedException(exceptions.DomainNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.get_domain(self.admin_context, uuid) @@ -641,7 +635,7 @@ class StorageTestCase(TestCase): name=expected['name'] + "NOT FOUND" ) - with self.assertRaises(exceptions.DomainNotFound): + with testtools.ExpectedException(exceptions.DomainNotFound): self.storage.find_domain(self.admin_context, criterion) def test_update_domain(self): @@ -660,12 +654,12 @@ class StorageTestCase(TestCase): fixture_one, domain_one = self.create_domain(fixture=0) _, domain_two = self.create_domain(fixture=1) - with self.assertRaises(exceptions.DuplicateDomain): + with testtools.ExpectedException(exceptions.DuplicateDomain): self.storage.update_domain(self.admin_context, domain_two['id'], fixture_one) def test_update_domain_missing(self): - with self.assertRaises(exceptions.DomainNotFound): + with testtools.ExpectedException(exceptions.DomainNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.update_domain(self.admin_context, uuid, {}) @@ -674,11 +668,11 @@ class StorageTestCase(TestCase): self.storage.delete_domain(self.admin_context, domain['id']) - with self.assertRaises(exceptions.DomainNotFound): + with testtools.ExpectedException(exceptions.DomainNotFound): self.storage.get_domain(self.admin_context, domain['id']) def test_delete_domain_missing(self): - with self.assertRaises(exceptions.DomainNotFound): + with testtools.ExpectedException(exceptions.DomainNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.delete_domain(self.admin_context, uuid) @@ -724,7 +718,7 @@ class StorageTestCase(TestCase): # Create the First Record self.create_record(domain) - with self.assertRaises(exceptions.DuplicateRecord): + with testtools.ExpectedException(exceptions.DuplicateRecord): # Attempt to create the second/duplicate record self.create_record(domain) @@ -807,7 +801,7 @@ class StorageTestCase(TestCase): self.assertIn('status', actual) def test_get_record_missing(self): - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.get_record(self.admin_context, uuid) @@ -835,7 +829,7 @@ class StorageTestCase(TestCase): name=expected['name'] + "NOT FOUND" ) - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): self.storage.find_record(self.admin_context, domain['id'], criterion) @@ -867,13 +861,13 @@ class StorageTestCase(TestCase): record_one_fixture, _ = self.create_record(domain, fixture=0) _, record_two = self.create_record(domain, fixture=1) - with self.assertRaises(exceptions.DuplicateRecord): + with testtools.ExpectedException(exceptions.DuplicateRecord): # Attempt to update the second record, making it a duplicate record self.storage.update_record(self.admin_context, record_two['id'], record_one_fixture) def test_update_record_missing(self): - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.update_record(self.admin_context, uuid, {}) @@ -885,11 +879,11 @@ class StorageTestCase(TestCase): self.storage.delete_record(self.admin_context, record['id']) - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): self.storage.get_record(self.admin_context, record['id']) def test_delete_record_missing(self): - with self.assertRaises(exceptions.RecordNotFound): + with testtools.ExpectedException(exceptions.RecordNotFound): uuid = 'caf771fc-6b05-4891-bee1-c2a48621f57b' self.storage.delete_record(self.admin_context, uuid) diff --git a/designate/tests/test_storage/test_api.py b/designate/tests/test_storage/test_api.py index 0569e26d6..416232636 100644 --- a/designate/tests/test_storage/test_api.py +++ b/designate/tests/test_storage/test_api.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. import mock +import testtools from designate.openstack.common import log as logging from designate.tests import TestCase from designate.storage import api as storage_api @@ -26,8 +27,6 @@ class SentinelException(Exception): class StorageAPITest(TestCase): - __test__ = True - def setUp(self): super(StorageAPITest, self).setUp() self.storage_api = storage_api.StorageAPI() @@ -69,7 +68,7 @@ class StorageAPITest(TestCase): self._set_side_effect('create_quota', [{'id': 12345}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.create_quota(context, values): raise SentinelException('Something Went Wrong') @@ -85,7 +84,7 @@ class StorageAPITest(TestCase): result = self.storage_api.get_quota(context, quota_id) self._assert_called_with('get_quota', context, quota_id) - self.assertEquals(quota, result) + self.assertEqual(quota, result) def test_find_quotas(self): context = mock.sentinel.context @@ -96,7 +95,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_quotas(context, criterion) self._assert_called_with('find_quotas', context, criterion) - self.assertEquals([quota], result) + self.assertEqual([quota], result) def test_find_quota(self): context = mock.sentinel.context @@ -107,7 +106,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_quota(context, criterion) self._assert_called_with('find_quota', context, criterion) - self.assertEquals(quota, result) + self.assertEqual(quota, result) def test_update_quota(self): context = mock.sentinel.context @@ -124,7 +123,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_quota', [{'id': 123, 'test': 1}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.update_quota(context, 123, values): raise SentinelException('Something Went Wrong') @@ -140,7 +139,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_quota', [quota]) with self.storage_api.delete_quota(context, 123) as q: - self.assertEquals(quota, q) + self.assertEqual(quota, q) self._assert_called_with('delete_quota', context, 123) @@ -150,7 +149,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_quota', [quota]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.delete_quota(context, 123): raise SentinelException('Something Went Wrong') @@ -175,7 +174,7 @@ class StorageAPITest(TestCase): self._set_side_effect('create_server', [{'id': 12345}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.create_server(context, values): raise SentinelException('Something Went Wrong') @@ -191,7 +190,7 @@ class StorageAPITest(TestCase): result = self.storage_api.get_server(context, server_id) self._assert_called_with('get_server', context, server_id) - self.assertEquals(server, result) + self.assertEqual(server, result) def test_find_servers(self): context = mock.sentinel.context @@ -202,7 +201,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_servers(context, criterion) self._assert_called_with('find_servers', context, criterion) - self.assertEquals([server], result) + self.assertEqual([server], result) def test_find_server(self): context = mock.sentinel.context @@ -213,7 +212,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_server(context, criterion) self._assert_called_with('find_server', context, criterion) - self.assertEquals(server, result) + self.assertEqual(server, result) def test_update_server(self): context = mock.sentinel.context @@ -230,7 +229,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_server', [{'id': 123, 'test': 1}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.update_server(context, 123, values): raise SentinelException('Something Went Wrong') @@ -246,7 +245,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_server', [server]) with self.storage_api.delete_server(context, 123) as q: - self.assertEquals(server, q) + self.assertEqual(server, q) self._assert_called_with('delete_server', context, 123) @@ -256,7 +255,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_server', [server]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.delete_server(context, 123): raise SentinelException('Something Went Wrong') @@ -281,7 +280,7 @@ class StorageAPITest(TestCase): self._set_side_effect('create_tsigkey', [{'id': 12345}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.create_tsigkey(context, values): raise SentinelException('Something Went Wrong') @@ -297,7 +296,7 @@ class StorageAPITest(TestCase): result = self.storage_api.get_tsigkey(context, tsigkey_id) self._assert_called_with('get_tsigkey', context, tsigkey_id) - self.assertEquals(tsigkey, result) + self.assertEqual(tsigkey, result) def test_find_tsigkeys(self): context = mock.sentinel.context @@ -308,7 +307,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_tsigkeys(context, criterion) self._assert_called_with('find_tsigkeys', context, criterion) - self.assertEquals([tsigkey], result) + self.assertEqual([tsigkey], result) def test_find_tsigkey(self): context = mock.sentinel.context @@ -319,7 +318,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_tsigkey(context, criterion) self._assert_called_with('find_tsigkey', context, criterion) - self.assertEquals(tsigkey, result) + self.assertEqual(tsigkey, result) def test_update_tsigkey(self): context = mock.sentinel.context @@ -336,7 +335,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_tsigkey', [{'id': 123, 'test': 1}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.update_tsigkey(context, 123, values): raise SentinelException('Something Went Wrong') @@ -352,7 +351,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_tsigkey', [tsigkey]) with self.storage_api.delete_tsigkey(context, 123) as q: - self.assertEquals(tsigkey, q) + self.assertEqual(tsigkey, q) self._assert_called_with('delete_tsigkey', context, 123) @@ -362,7 +361,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_tsigkey', [tsigkey]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.delete_tsigkey(context, 123): raise SentinelException('Something Went Wrong') @@ -377,7 +376,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_tenants(context) self._assert_called_with('find_tenants', context) - self.assertEquals([tenant], result) + self.assertEqual([tenant], result) def test_get_tenant(self): context = mock.sentinel.context @@ -387,7 +386,7 @@ class StorageAPITest(TestCase): result = self.storage_api.get_tenant(context, 123) self._assert_called_with('get_tenant', context, 123) - self.assertEquals(tenant, result) + self.assertEqual(tenant, result) def test_count_tenants(self): context = mock.sentinel.context @@ -396,7 +395,7 @@ class StorageAPITest(TestCase): result = self.storage_api.count_tenants(context) self._assert_called_with('count_tenants', context) - self.assertEquals(1, result) + self.assertEqual(1, result) # Domain Tests def test_create_domain(self): @@ -417,7 +416,7 @@ class StorageAPITest(TestCase): self._set_side_effect('create_domain', [{'id': 12345}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.create_domain(context, values): raise SentinelException('Something Went Wrong') @@ -433,7 +432,7 @@ class StorageAPITest(TestCase): result = self.storage_api.get_domain(context, domain_id) self._assert_called_with('get_domain', context, domain_id) - self.assertEquals(domain, result) + self.assertEqual(domain, result) def test_find_domains(self): context = mock.sentinel.context @@ -444,7 +443,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_domains(context, criterion) self._assert_called_with('find_domains', context, criterion) - self.assertEquals([domain], result) + self.assertEqual([domain], result) def test_find_domain(self): context = mock.sentinel.context @@ -455,7 +454,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_domain(context, criterion) self._assert_called_with('find_domain', context, criterion) - self.assertEquals(domain, result) + self.assertEqual(domain, result) def test_update_domain(self): context = mock.sentinel.context @@ -472,7 +471,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_domain', [{'id': 123, 'test': 1}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.update_domain(context, 123, values): raise SentinelException('Something Went Wrong') @@ -488,7 +487,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_domain', [domain]) with self.storage_api.delete_domain(context, 123) as q: - self.assertEquals(domain, q) + self.assertEqual(domain, q) self._assert_called_with('delete_domain', context, 123) @@ -498,7 +497,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_domain', [domain]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.delete_domain(context, 123): raise SentinelException('Something Went Wrong') @@ -523,7 +522,7 @@ class StorageAPITest(TestCase): self._set_side_effect('create_record', [{'id': 12345}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.create_record(context, 123, values): raise SentinelException('Something Went Wrong') @@ -539,7 +538,7 @@ class StorageAPITest(TestCase): result = self.storage_api.get_record(context, record_id) self._assert_called_with('get_record', context, record_id) - self.assertEquals(record, result) + self.assertEqual(record, result) def test_find_records(self): context = mock.sentinel.context @@ -551,7 +550,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_records(context, domain_id, criterion) self._assert_called_with('find_records', context, domain_id, criterion) - self.assertEquals([record], result) + self.assertEqual([record], result) def test_find_record(self): context = mock.sentinel.context @@ -563,7 +562,7 @@ class StorageAPITest(TestCase): result = self.storage_api.find_record(context, domain_id, criterion) self._assert_called_with('find_record', context, domain_id, criterion) - self.assertEquals(record, result) + self.assertEqual(record, result) def test_update_record(self): context = mock.sentinel.context @@ -580,7 +579,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_record', [{'id': 123, 'test': 1}]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.update_record(context, 123, values): raise SentinelException('Something Went Wrong') @@ -596,7 +595,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_record', [record]) with self.storage_api.delete_record(context, 123) as q: - self.assertEquals(record, q) + self.assertEqual(record, q) self._assert_called_with('delete_record', context, 123) @@ -606,7 +605,7 @@ class StorageAPITest(TestCase): self._set_side_effect('get_record', [record]) - with self.assertRaises(SentinelException): + with testtools.ExpectedException(SentinelException): with self.storage_api.delete_record(context, 123): raise SentinelException('Something Went Wrong') diff --git a/designate/tests/test_storage/test_sqlalchemy.py b/designate/tests/test_storage/test_sqlalchemy.py index 859cc72f5..b0c470d26 100644 --- a/designate/tests/test_storage/test_sqlalchemy.py +++ b/designate/tests/test_storage/test_sqlalchemy.py @@ -19,6 +19,8 @@ from migrate.versioning import api as versioning_api from migrate.versioning import repository import sqlalchemy from designate.openstack.common import log as logging +from designate import storage +from designate.tests import TestCase from designate.tests.test_storage import StorageTestCase LOG = logging.getLogger(__name__) @@ -27,21 +29,18 @@ REPOSITORY = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'migrate_repo')) -class SqlalchemyStorageTest(StorageTestCase): - __test__ = True - +class SqlalchemyStorageTest(StorageTestCase, TestCase): def setUp(self): self.config(database_connection='sqlite://', group='storage:sqlalchemy') super(SqlalchemyStorageTest, self).setUp() - + self.storage = storage.get_storage() self.REPOSITORY = repository.Repository(REPOSITORY) # Migration Test Stuff def _init_database(self, url): LOG.debug('Building Engine') engine = sqlalchemy.create_engine(url) - LOG.debug('Initializing database') versioning_api.version_control(engine, repository=self.REPOSITORY) diff --git a/designate/tests/test_utils.py b/designate/tests/test_utils.py index 0b554eb70..d1590a7ec 100644 --- a/designate/tests/test_utils.py +++ b/designate/tests/test_utils.py @@ -15,6 +15,7 @@ # under the License. import os import tempfile +import testtools from jinja2 import Template from designate.tests import TestCase from designate import exceptions @@ -32,7 +33,7 @@ class TestUtils(TestCase): def test_resource_string_missing(self): name = 'invalid.jinja2' - with self.assertRaises(exceptions.ResourceNotFound): + with testtools.ExpectedException(exceptions.ResourceNotFound): utils.resource_string(name) def test_load_schema(self): @@ -41,7 +42,7 @@ class TestUtils(TestCase): self.assertIsInstance(schema, dict) def test_load_schema_missing(self): - with self.assertRaises(exceptions.ResourceNotFound): + with testtools.ExpectedException(exceptions.ResourceNotFound): utils.load_schema('v1', 'missing') def test_load_template(self): @@ -54,7 +55,7 @@ class TestUtils(TestCase): def test_load_template_missing(self): name = 'invalid.jinja2' - with self.assertRaises(exceptions.ResourceNotFound): + with testtools.ExpectedException(exceptions.ResourceNotFound): utils.load_template(name) def test_render_template(self): diff --git a/openstack-common.conf b/openstack-common.conf index 12300032b..f62ce3e41 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -12,6 +12,7 @@ module=processutils module=rootwrap module=rpc module=service +module=test module=timeutils module=uuidutils module=wsgi diff --git a/test-requirements.txt b/test-requirements.txt index c3cf09c49..316393df9 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,13 +1,13 @@ coverage>=3.6 +discover flake8==2.0 -hacking>=0.5.6,<0.8 +fixtures>=0.3.14 +hacking>=0.8,<0.9 mock>=1.0 mox>=0.5.3 -nose -nosehtmloutput>=0.0.3 -nosexcover -openstack.nose_plugin>=0.7 pep8==1.4.5 pyflakes>=0.7.2,<0.7.4 -unittest2 +python-subunit +testtools>=0.9.32 +testrepository>=0.0.8 WebTest>=2.0 diff --git a/tox.ini b/tox.ini index 95b074a40..004e0feee 100644 --- a/tox.ini +++ b/tox.ini @@ -7,24 +7,14 @@ skipsdist = True downloadcache = ~/cache/pip [testenv] -sitepackages = False usedevelop = True install_command = pip install {opts} {packages} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -setenv = VIRTUAL_ENV={envdir} - NOSE_WITH_OPENSTACK=1 - NOSE_OPENSTACK_COLOR=1 - NOSE_OPENSTACK_RED=0.05 - NOSE_OPENSTACK_YELLOW=0.025 - NOSE_OPENSTACK_SHOW_ELAPSED=1 -commands = nosetests {posargs} +commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:cover] -setenv = {[testenv]setenv} - NOSE_WITH_COVERAGE=1 - NOSE_COVER_PACKAGE=designate - NOSE_COVER_INCLUSIVE=1 +commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:flake8] commands = flake8