Skip to content

Commit

Permalink
Fix up the test framework
Browse files Browse the repository at this point in the history
When trying to test a bug the cinder test framework was
running all the tests when I just wanted one to run.

Instead of fixing this problem in nose I tried to move cinder to
testr. I copied a fair bit of code from nova to get this to work.
This way at least if some one makes an improvement to the nova
test framework cinder can improve here also.

Note that now all the tests a thread safe. Added a concurrency argument
to run_tests.sh that defaults to 1. You can specify your own level then
and take the risk of intermittent failures. Tell jenkins to use a
concurrency level of 1 to avoid failing gate jobs.

Note One of these tests is broken and I have disabled for now. There
is a bug for this here: https://bugs.launchpad.net/cinder/+bug/1173266

Note: cinder.api.openstack.FaultWrapper is deprecated and is causing
a circular import when I try and import cinder.tests.test_wsgi. This
is fixed by only importing the openstack.FaultWrapper during the
__init__ phase of the wsgi middleware.

Fixes: bug 1183434
Fixes: bug 1177924

Change-Id: I5e10b55c5b236eb81a6a3e0e9ea56af8ca4ef8e1
  • Loading branch information
Michael Kerrin committed Jun 14, 2013
1 parent 0a26b70 commit 930f589
Show file tree
Hide file tree
Showing 16 changed files with 630 additions and 194 deletions.
8 changes: 8 additions & 0 deletions .testr.conf
@@ -0,0 +1,8 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
${PYTHON:-python} -m subunit.run discover -t ./ ./cinder/tests $LISTOPT $IDOPTION

test_id_option=--load-list $IDFILE
test_list_option=--list
8 changes: 5 additions & 3 deletions cinder/api/openstack/__init__.py
Expand Up @@ -20,7 +20,6 @@

import routes

from cinder.api.middleware import fault
from cinder.api.openstack import wsgi
from cinder.openstack.common import log as logging
from cinder import utils
Expand Down Expand Up @@ -123,8 +122,11 @@ def _setup_routes(self, mapper, ext_mgr):
raise NotImplementedError


class FaultWrapper(fault.FaultWrapper):
class FaultWrapper(base_wsgi.Middleware):

def __init__(self, application):
LOG.warn(_('cinder.api.openstack:FaultWrapper is deprecated. Please '
'use cinder.api.middleware.fault:FaultWrapper instead.'))
super(FaultWrapper, self).__init__(application)
# Avoid circular imports from here. Can I just remove this class?
from cinder.api.middleware import fault
super(FaultWrapper, self).__init__(fault.FaultWrapper(application))
62 changes: 60 additions & 2 deletions cinder/test.py
Expand Up @@ -25,6 +25,7 @@

import functools
import os
import shutil
import uuid

import fixtures
Expand All @@ -33,11 +34,12 @@
import stubout
import testtools

from cinder.db import migration
from cinder import flags
from cinder.openstack.common.db.sqlalchemy import session
from cinder.openstack.common import log as logging
from cinder.openstack.common import timeutils
from cinder import service
from cinder import tests
from cinder.tests import fake_flags

test_opts = [
Expand All @@ -53,11 +55,54 @@

LOG = logging.getLogger(__name__)

_DB_CACHE = None


class TestingException(Exception):
pass


class Database(fixtures.Fixture):

def __init__(self, db_session, db_migrate, sql_connection,
sqlite_db, sqlite_clean_db):
self.sql_connection = sql_connection
self.sqlite_db = sqlite_db
self.sqlite_clean_db = sqlite_clean_db

self.engine = db_session.get_engine()
self.engine.dispose()
conn = self.engine.connect()
if sql_connection == "sqlite://":
if db_migrate.db_version() > db_migrate.INIT_VERSION:
return
else:
testdb = os.path.join(FLAGS.state_path, sqlite_db)
if os.path.exists(testdb):
return
db_migrate.db_sync()
# self.post_migrations()
if sql_connection == "sqlite://":
conn = self.engine.connect()
self._DB = "".join(line for line in conn.connection.iterdump())
self.engine.dispose()
else:
cleandb = os.path.join(FLAGS.state_path, sqlite_clean_db)
shutil.copyfile(testdb, cleandb)

def setUp(self):
super(Database, self).setUp()

if self.sql_connection == "sqlite://":
conn = self.engine.connect()
conn.connection.executescript(self._DB)
self.addCleanup(self.engine.dispose)
else:
shutil.copyfile(
os.path.join(FLAGS.state_path, self.sqlite_clean_db),
os.path.join(FLAGS.state_path, self.sqlite_db))


class TestCase(testtools.TestCase):
"""Test case base class for all unit tests."""

Expand Down Expand Up @@ -94,7 +139,19 @@ def setUp(self):
# now that we have some required db setup for the system
# to work properly.
self.start = timeutils.utcnow()
tests.reset_db()

FLAGS.set_default('connection', 'sqlite://', 'database')
FLAGS.set_default('sqlite_synchronous', False)

self.log_fixture = self.useFixture(fixtures.FakeLogger())

global _DB_CACHE
if not _DB_CACHE:
_DB_CACHE = Database(session, migration,
sql_connection=FLAGS.database.connection,
sqlite_db=FLAGS.sqlite_db,
sqlite_clean_db=FLAGS.sqlite_clean_db)
self.useFixture(_DB_CACHE)

# emulate some of the mox stuff, we can't use the metaclass
# because it screws with our generators
Expand All @@ -107,6 +164,7 @@ def setUp(self):
self.addCleanup(self.mox.VerifyAll)
self.injected = []
self._services = []

FLAGS.set_override('fatal_exception_format_errors', True)

def tearDown(self):
Expand Down
49 changes: 0 additions & 49 deletions cinder/tests/__init__.py
Expand Up @@ -38,52 +38,3 @@
# The code below enables nosetests to work with i18n _() blocks
import __builtin__
setattr(__builtin__, '_', lambda x: x)
import os
import shutil

from oslo.config import cfg

from cinder.db.sqlalchemy.api import get_engine


_DB = None

CONF = cfg.CONF


def reset_db():
if CONF.database.connection == "sqlite://":
engine = get_engine()
engine.dispose()
conn = engine.connect()
conn.connection.executescript(_DB)
else:
shutil.copyfile(os.path.join(CONF.state_path,
CONF.sqlite_clean_db),
os.path.join(CONF.state_path, CONF.sqlite_db))


def setup():
import mox # Fail fast if you don't have mox. Workaround for bug 810424

from cinder.db import migration
from cinder.tests import fake_flags
fake_flags.set_defaults(CONF)

if CONF.database.connection == "sqlite://":
if migration.db_version() > 1:
return
else:
testdb = os.path.join(CONF.state_path, CONF.sqlite_db)
if os.path.exists(testdb):
return
migration.db_sync()

if CONF.database.connection == "sqlite://":
global _DB
engine = get_engine()
conn = engine.connect()
_DB = "".join(line for line in conn.connection.iterdump())
else:
cleandb = os.path.join(CONF.state_path, CONF.sqlite_clean_db)
shutil.copyfile(testdb, cleandb)
6 changes: 5 additions & 1 deletion cinder/tests/api/contrib/test_admin_actions.py
Expand Up @@ -23,12 +23,16 @@ def app():
class AdminActionsTest(test.TestCase):

def setUp(self):
self.tempdir = tempfile.mkdtemp()
super(AdminActionsTest, self).setUp()
self.tempdir = tempfile.mkdtemp()
self.flags(rpc_backend='cinder.openstack.common.rpc.impl_fake')
self.flags(lock_path=self.tempdir)
self.volume_api = volume_api.API()

def tearDown(self):
shutil.rmtree(self.tempdir)
super(AdminActionsTest, self).tearDown()

def test_reset_status_as_admin(self):
# admin context
ctx = context.RequestContext('admin', 'fake', True)
Expand Down
2 changes: 2 additions & 0 deletions cinder/tests/integrated/test_volumes.py
Expand Up @@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.

import testtools
import time

from cinder.openstack.common import log as logging
Expand Down Expand Up @@ -80,6 +81,7 @@ def _poll_while(self, volume_id, continue_states, max_retries=5):
break
return found_volume

@testtools.skip('This test is failing: bug 1173266')
def test_create_and_delete_volume(self):
"""Creates and deletes a volume."""

Expand Down

0 comments on commit 930f589

Please sign in to comment.