summaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/tests/unit/ksfixtures/database.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/tests/unit/ksfixtures/database.py')
-rw-r--r--keystone-moon/keystone/tests/unit/ksfixtures/database.py124
1 files changed, 124 insertions, 0 deletions
diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/database.py b/keystone-moon/keystone/tests/unit/ksfixtures/database.py
new file mode 100644
index 00000000..15597539
--- /dev/null
+++ b/keystone-moon/keystone/tests/unit/ksfixtures/database.py
@@ -0,0 +1,124 @@
+# 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 functools
+import os
+import shutil
+
+import fixtures
+from oslo_config import cfg
+from oslo_db import options as db_options
+from oslo_db.sqlalchemy import migration
+
+from keystone.common import sql
+from keystone.common.sql import migration_helpers
+from keystone.tests import unit as tests
+
+
+CONF = cfg.CONF
+
+
+def run_once(f):
+ """A decorator to ensure the decorated function is only executed once.
+
+ The decorated function cannot expect any arguments.
+ """
+ @functools.wraps(f)
+ def wrapper():
+ if not wrapper.already_ran:
+ f()
+ wrapper.already_ran = True
+ wrapper.already_ran = False
+ return wrapper
+
+
+def _setup_database(extensions=None):
+ if CONF.database.connection != tests.IN_MEM_DB_CONN_STRING:
+ db = tests.dirs.tmp('test.db')
+ pristine = tests.dirs.tmp('test.db.pristine')
+
+ if os.path.exists(db):
+ os.unlink(db)
+ if not os.path.exists(pristine):
+ migration.db_sync(sql.get_engine(),
+ migration_helpers.find_migrate_repo())
+ for extension in (extensions or []):
+ migration_helpers.sync_database_to_version(extension=extension)
+ shutil.copyfile(db, pristine)
+ else:
+ shutil.copyfile(pristine, db)
+
+
+# NOTE(I159): Every execution all the options will be cleared. The method must
+# be called at the every fixture initialization.
+def initialize_sql_session():
+ # Make sure the DB is located in the correct location, in this case set
+ # the default value, as this should be able to be overridden in some
+ # test cases.
+ db_options.set_defaults(
+ CONF,
+ connection=tests.IN_MEM_DB_CONN_STRING)
+
+
+@run_once
+def _load_sqlalchemy_models():
+ """Find all modules containing SQLAlchemy models and import them.
+
+ This creates more consistent, deterministic test runs because tables
+ for all core and extension models are always created in the test
+ database. We ensure this by importing all modules that contain model
+ definitions.
+
+ The database schema during test runs is created using reflection.
+ Reflection is simply SQLAlchemy taking the model definitions for
+ all models currently imported and making tables for each of them.
+ The database schema created during test runs may vary between tests
+ as more models are imported. Importing all models at the start of
+ the test run avoids this problem.
+
+ """
+ keystone_root = os.path.normpath(os.path.join(
+ os.path.dirname(__file__), '..', '..', '..'))
+ for root, dirs, files in os.walk(keystone_root):
+ # NOTE(morganfainberg): Slice the keystone_root off the root to ensure
+ # we do not end up with a module name like:
+ # Users.home.openstack.keystone.assignment.backends.sql
+ root = root[len(keystone_root):]
+ if root.endswith('backends') and 'sql.py' in files:
+ # The root will be prefixed with an instance of os.sep, which will
+ # make the root after replacement '.<root>', the 'keystone' part
+ # of the module path is always added to the front
+ module_name = ('keystone.%s.sql' %
+ root.replace(os.sep, '.').lstrip('.'))
+ __import__(module_name)
+
+
+class Database(fixtures.Fixture):
+ """A fixture for setting up and tearing down a database.
+
+ """
+
+ def __init__(self, extensions=None):
+ super(Database, self).__init__()
+ self._extensions = extensions
+ initialize_sql_session()
+ _load_sqlalchemy_models()
+
+ def setUp(self):
+ super(Database, self).setUp()
+ _setup_database(extensions=self._extensions)
+
+ self.engine = sql.get_engine()
+ sql.ModelBase.metadata.create_all(bind=self.engine)
+ self.addCleanup(sql.cleanup)
+ self.addCleanup(sql.ModelBase.metadata.drop_all, bind=self.engine)