From b8c756ecdd7cced1db4300935484e8c83701c82e Mon Sep 17 00:00:00 2001 From: WuKong Date: Tue, 30 Jun 2015 18:47:29 +0200 Subject: migrate moon code from github to opnfv Change-Id: Ice53e368fd1114d56a75271aa9f2e598e3eba604 Signed-off-by: WuKong --- .../keystone/tests/unit/ksfixtures/__init__.py | 15 + .../keystone/tests/unit/ksfixtures/appserver.py | 79 ++++ .../keystone/tests/unit/ksfixtures/cache.py | 36 ++ .../keystone/tests/unit/ksfixtures/database.py | 124 ++++++ .../keystone/tests/unit/ksfixtures/hacking.py | 489 +++++++++++++++++++++ .../tests/unit/ksfixtures/key_repository.py | 34 ++ .../tests/unit/ksfixtures/temporaryfile.py | 29 ++ 7 files changed, 806 insertions(+) create mode 100644 keystone-moon/keystone/tests/unit/ksfixtures/__init__.py create mode 100644 keystone-moon/keystone/tests/unit/ksfixtures/appserver.py create mode 100644 keystone-moon/keystone/tests/unit/ksfixtures/cache.py create mode 100644 keystone-moon/keystone/tests/unit/ksfixtures/database.py create mode 100644 keystone-moon/keystone/tests/unit/ksfixtures/hacking.py create mode 100644 keystone-moon/keystone/tests/unit/ksfixtures/key_repository.py create mode 100644 keystone-moon/keystone/tests/unit/ksfixtures/temporaryfile.py (limited to 'keystone-moon/keystone/tests/unit/ksfixtures') diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/__init__.py b/keystone-moon/keystone/tests/unit/ksfixtures/__init__.py new file mode 100644 index 00000000..81b80298 --- /dev/null +++ b/keystone-moon/keystone/tests/unit/ksfixtures/__init__.py @@ -0,0 +1,15 @@ +# 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. + +from keystone.tests.unit.ksfixtures.cache import Cache # noqa +from keystone.tests.unit.ksfixtures.key_repository import KeyRepository # noqa diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/appserver.py b/keystone-moon/keystone/tests/unit/ksfixtures/appserver.py new file mode 100644 index 00000000..ea1e6255 --- /dev/null +++ b/keystone-moon/keystone/tests/unit/ksfixtures/appserver.py @@ -0,0 +1,79 @@ +# Copyright 2013 OpenStack Foundation +# +# 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. + +from __future__ import absolute_import + +import fixtures +from oslo_config import cfg +from paste import deploy + +from keystone.common import environment + + +CONF = cfg.CONF + +MAIN = 'main' +ADMIN = 'admin' + + +class AppServer(fixtures.Fixture): + """A fixture for managing an application server instance. + """ + + def __init__(self, config, name, cert=None, key=None, ca=None, + cert_required=False, host='127.0.0.1', port=0): + super(AppServer, self).__init__() + self.config = config + self.name = name + self.cert = cert + self.key = key + self.ca = ca + self.cert_required = cert_required + self.host = host + self.port = port + + def setUp(self): + super(AppServer, self).setUp() + + app = deploy.loadapp(self.config, name=self.name) + self.server = environment.Server(app, self.host, self.port) + self._setup_SSL_if_requested() + self.server.start(key='socket') + + # some tests need to know the port we ran on. + self.port = self.server.socket_info['socket'][1] + self._update_config_opt() + + self.addCleanup(self.server.stop) + + def _setup_SSL_if_requested(self): + # TODO(dstanek): fix environment.Server to take a SSLOpts instance + # so that the params are either always set or not + if (self.cert is not None and + self.ca is not None and + self.key is not None): + self.server.set_ssl(certfile=self.cert, + keyfile=self.key, + ca_certs=self.ca, + cert_required=self.cert_required) + + def _update_config_opt(self): + """Updates the config with the actual port used.""" + opt_name = self._get_config_option_for_section_name() + CONF.set_override(opt_name, self.port, group='eventlet_server') + + def _get_config_option_for_section_name(self): + """Maps Paster config section names to port option names.""" + return {'admin': 'admin_port', 'main': 'public_port'}[self.name] diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/cache.py b/keystone-moon/keystone/tests/unit/ksfixtures/cache.py new file mode 100644 index 00000000..74566f1e --- /dev/null +++ b/keystone-moon/keystone/tests/unit/ksfixtures/cache.py @@ -0,0 +1,36 @@ +# 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 keystone.common import cache + + +class Cache(fixtures.Fixture): + """A fixture for setting up and tearing down the cache between test cases. + """ + + def setUp(self): + super(Cache, self).setUp() + + # NOTE(dstanek): We must remove the existing cache backend in the + # setUp instead of the tearDown because it defaults to a no-op cache + # and we want the configure call below to create the correct backend. + + # NOTE(morganfainberg): The only way to reconfigure the CacheRegion + # object on each setUp() call is to remove the .backend property. + if cache.REGION.is_configured: + del cache.REGION.backend + + # ensure the cache region instance is setup + cache.configure_cache_region(cache.REGION) 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 '.', 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) diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/hacking.py b/keystone-moon/keystone/tests/unit/ksfixtures/hacking.py new file mode 100644 index 00000000..47ef6b4b --- /dev/null +++ b/keystone-moon/keystone/tests/unit/ksfixtures/hacking.py @@ -0,0 +1,489 @@ +# 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. + +# NOTE(morganfainberg) This file shouldn't have flake8 run on it as it has +# code examples that will fail normal CI pep8/flake8 tests. This is expected. +# The code has been moved here to ensure that proper tests occur on the +# test_hacking_checks test cases. +# flake8: noqa + +import fixtures + + +class HackingCode(fixtures.Fixture): + """A fixture to house the various code examples for the keystone hacking + style checks. + """ + + mutable_default_args = { + 'code': """ + def f(): + pass + + def f(a, b='', c=None): + pass + + def f(bad=[]): + pass + + def f(foo, bad=[], more_bad=[x for x in range(3)]): + pass + + def f(foo, bad={}): + pass + + def f(foo, bad={}, another_bad=[], fine=None): + pass + + def f(bad=[]): # noqa + pass + + def funcs(bad=dict(), more_bad=list(), even_more_bad=set()): + "creating mutables through builtins" + + def funcs(bad=something(), more_bad=some_object.something()): + "defaults from any functions" + + def f(bad=set(), more_bad={x for x in range(3)}, + even_more_bad={1, 2, 3}): + "set and set comprehession" + + def f(bad={x: x for x in range(3)}): + "dict comprehension" + """, + 'expected_errors': [ + (7, 10, 'K001'), + (10, 15, 'K001'), + (10, 29, 'K001'), + (13, 15, 'K001'), + (16, 15, 'K001'), + (16, 31, 'K001'), + (22, 14, 'K001'), + (22, 31, 'K001'), + (22, 53, 'K001'), + (25, 14, 'K001'), + (25, 36, 'K001'), + (28, 10, 'K001'), + (28, 27, 'K001'), + (29, 21, 'K001'), + (32, 11, 'K001'), + ]} + + comments_begin_with_space = { + 'code': """ + # This is a good comment + + #This is a bad one + + # This is alright and can + # be continued with extra indentation + # if that's what the developer wants. + """, + 'expected_errors': [ + (3, 0, 'K002'), + ]} + + asserting_none_equality = { + 'code': """ + class Test(object): + + def test(self): + self.assertEqual('', '') + self.assertEqual('', None) + self.assertEqual(None, '') + self.assertNotEqual('', None) + self.assertNotEqual(None, '') + self.assertNotEqual('', None) # noqa + self.assertNotEqual(None, '') # noqa + """, + 'expected_errors': [ + (5, 8, 'K003'), + (6, 8, 'K003'), + (7, 8, 'K004'), + (8, 8, 'K004'), + ]} + + assert_no_translations_for_debug_logging = { + 'code': """ + import logging + import logging as stlib_logging + from keystone.i18n import _ + from keystone.i18n import _ as oslo_i18n + from keystone.openstack.common import log + from keystone.openstack.common import log as oslo_logging + + # stdlib logging + L0 = logging.getLogger() + L0.debug(_('text')) + class C: + def __init__(self): + L0.debug(oslo_i18n('text', {})) + + # stdlib logging w/ alias and specifying a logger + class C: + def __init__(self): + self.L1 = logging.getLogger(__name__) + def m(self): + self.L1.debug( + _('text'), {} + ) + + # oslo logging and specifying a logger + L2 = log.getLogger(__name__) + L2.debug(oslo_i18n('text')) + + # oslo logging w/ alias + class C: + def __init__(self): + self.L3 = oslo_logging.getLogger() + self.L3.debug(_('text')) + + # translation on a separate line + msg = _('text') + L2.debug(msg) + + # this should not fail + if True: + msg = _('message %s') % X + L2.error(msg) + raise TypeError(msg) + if True: + msg = 'message' + L2.debug(msg) + + # this should not fail + if True: + if True: + msg = _('message') + else: + msg = _('message') + L2.debug(msg) + raise Exception(msg) + """, + 'expected_errors': [ + (10, 9, 'K005'), + (13, 17, 'K005'), + (21, 12, 'K005'), + (26, 9, 'K005'), + (32, 22, 'K005'), + (36, 9, 'K005'), + ] + } + + oslo_namespace_imports = { + 'code': """ + import oslo.utils + import oslo_utils + import oslo.utils.encodeutils + import oslo_utils.encodeutils + from oslo import utils + from oslo.utils import encodeutils + from oslo_utils import encodeutils + + import oslo.serialization + import oslo_serialization + import oslo.serialization.jsonutils + import oslo_serialization.jsonutils + from oslo import serialization + from oslo.serialization import jsonutils + from oslo_serialization import jsonutils + + import oslo.messaging + import oslo_messaging + import oslo.messaging.conffixture + import oslo_messaging.conffixture + from oslo import messaging + from oslo.messaging import conffixture + from oslo_messaging import conffixture + + import oslo.db + import oslo_db + import oslo.db.api + import oslo_db.api + from oslo import db + from oslo.db import api + from oslo_db import api + + import oslo.config + import oslo_config + import oslo.config.cfg + import oslo_config.cfg + from oslo import config + from oslo.config import cfg + from oslo_config import cfg + + import oslo.i18n + import oslo_i18n + import oslo.i18n.log + import oslo_i18n.log + from oslo import i18n + from oslo.i18n import log + from oslo_i18n import log + """, + 'expected_errors': [ + (1, 0, 'K333'), + (3, 0, 'K333'), + (5, 0, 'K333'), + (6, 0, 'K333'), + (9, 0, 'K333'), + (11, 0, 'K333'), + (13, 0, 'K333'), + (14, 0, 'K333'), + (17, 0, 'K333'), + (19, 0, 'K333'), + (21, 0, 'K333'), + (22, 0, 'K333'), + (25, 0, 'K333'), + (27, 0, 'K333'), + (29, 0, 'K333'), + (30, 0, 'K333'), + (33, 0, 'K333'), + (35, 0, 'K333'), + (37, 0, 'K333'), + (38, 0, 'K333'), + (41, 0, 'K333'), + (43, 0, 'K333'), + (45, 0, 'K333'), + (46, 0, 'K333'), + ], + } + + dict_constructor = { + 'code': """ + lower_res = {k.lower(): v for k, v in six.iteritems(res[1])} + fool = dict(a='a', b='b') + lower_res = dict((k.lower(), v) for k, v in six.iteritems(res[1])) + attrs = dict([(k, _from_json(v))]) + dict([[i,i] for i in range(3)]) + dict(({1:2})) + """, + 'expected_errors': [ + (3, 0, 'K008'), + (4, 0, 'K008'), + (5, 0, 'K008'), + ]} + + +class HackingLogging(fixtures.Fixture): + + shared_imports = """ + import logging + import logging as stlib_logging + from keystone.i18n import _ + from keystone.i18n import _ as oslo_i18n + from keystone.i18n import _LC + from keystone.i18n import _LE + from keystone.i18n import _LE as error_hint + from keystone.i18n import _LI + from keystone.i18n import _LW + from keystone.openstack.common import log + from keystone.openstack.common import log as oslo_logging + """ + + examples = [ + { + 'code': """ + # stdlib logging + LOG = logging.getLogger() + LOG.info(_('text')) + class C: + def __init__(self): + LOG.warn(oslo_i18n('text', {})) + LOG.warn(_LW('text', {})) + """, + 'expected_errors': [ + (3, 9, 'K006'), + (6, 17, 'K006'), + ], + }, + { + 'code': """ + # stdlib logging w/ alias and specifying a logger + class C: + def __init__(self): + self.L = logging.getLogger(__name__) + def m(self): + self.L.warning( + _('text'), {} + ) + self.L.warning( + _LW('text'), {} + ) + """, + 'expected_errors': [ + (7, 12, 'K006'), + ], + }, + { + 'code': """ + # oslo logging and specifying a logger + L = log.getLogger(__name__) + L.error(oslo_i18n('text')) + L.error(error_hint('text')) + """, + 'expected_errors': [ + (3, 8, 'K006'), + ], + }, + { + 'code': """ + # oslo logging w/ alias + class C: + def __init__(self): + self.LOG = oslo_logging.getLogger() + self.LOG.critical(_('text')) + self.LOG.critical(_LC('text')) + """, + 'expected_errors': [ + (5, 26, 'K006'), + ], + }, + { + 'code': """ + LOG = log.getLogger(__name__) + # translation on a separate line + msg = _('text') + LOG.exception(msg) + msg = _LE('text') + LOG.exception(msg) + """, + 'expected_errors': [ + (4, 14, 'K006'), + ], + }, + { + 'code': """ + LOG = logging.getLogger() + + # ensure the correct helper is being used + LOG.warn(_LI('this should cause an error')) + + # debug should not allow any helpers either + LOG.debug(_LI('this should cause an error')) + """, + 'expected_errors': [ + (4, 9, 'K006'), + (7, 10, 'K005'), + ], + }, + { + 'code': """ + # this should not be an error + L = log.getLogger(__name__) + msg = _('text') + L.warn(msg) + raise Exception(msg) + """, + 'expected_errors': [], + }, + { + 'code': """ + L = log.getLogger(__name__) + def f(): + msg = _('text') + L2.warn(msg) + something = True # add an extra statement here + raise Exception(msg) + """, + 'expected_errors': [], + }, + { + 'code': """ + LOG = log.getLogger(__name__) + def func(): + msg = _('text') + LOG.warn(msg) + raise Exception('some other message') + """, + 'expected_errors': [ + (4, 13, 'K006'), + ], + }, + { + 'code': """ + LOG = log.getLogger(__name__) + if True: + msg = _('text') + else: + msg = _('text') + LOG.warn(msg) + raise Exception(msg) + """, + 'expected_errors': [ + ], + }, + { + 'code': """ + LOG = log.getLogger(__name__) + if True: + msg = _('text') + else: + msg = _('text') + LOG.warn(msg) + """, + 'expected_errors': [ + (6, 9, 'K006'), + ], + }, + { + 'code': """ + LOG = log.getLogger(__name__) + msg = _LW('text') + LOG.warn(msg) + raise Exception(msg) + """, + 'expected_errors': [ + (3, 9, 'K007'), + ], + }, + { + 'code': """ + LOG = log.getLogger(__name__) + msg = _LW('text') + LOG.warn(msg) + msg = _('something else') + raise Exception(msg) + """, + 'expected_errors': [], + }, + { + 'code': """ + LOG = log.getLogger(__name__) + msg = _LW('hello %s') % 'world' + LOG.warn(msg) + raise Exception(msg) + """, + 'expected_errors': [ + (3, 9, 'K007'), + ], + }, + { + 'code': """ + LOG = log.getLogger(__name__) + msg = _LW('hello %s') % 'world' + LOG.warn(msg) + """, + 'expected_errors': [], + }, + { + 'code': """ + # this should not be an error + LOG = log.getLogger(__name__) + try: + something = True + except AssertionError as e: + LOG.warning(six.text_type(e)) + raise exception.Unauthorized(e) + """, + 'expected_errors': [], + }, + ] diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/key_repository.py b/keystone-moon/keystone/tests/unit/ksfixtures/key_repository.py new file mode 100644 index 00000000..d1ac2ab4 --- /dev/null +++ b/keystone-moon/keystone/tests/unit/ksfixtures/key_repository.py @@ -0,0 +1,34 @@ +# 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 shutil +import tempfile + +import fixtures + +from keystone.token.providers.fernet import utils + + +class KeyRepository(fixtures.Fixture): + def __init__(self, config_fixture): + super(KeyRepository, self).__init__() + self.config_fixture = config_fixture + + def setUp(self): + super(KeyRepository, self).setUp() + directory = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, directory) + self.config_fixture.config(group='fernet_tokens', + key_repository=directory) + + utils.create_key_directory() + utils.initialize_key_repository() diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/temporaryfile.py b/keystone-moon/keystone/tests/unit/ksfixtures/temporaryfile.py new file mode 100644 index 00000000..a4be06f8 --- /dev/null +++ b/keystone-moon/keystone/tests/unit/ksfixtures/temporaryfile.py @@ -0,0 +1,29 @@ +# 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 os +import tempfile + +import fixtures + + +class SecureTempFile(fixtures.Fixture): + """A fixture for creating a secure temp file.""" + + def setUp(self): + super(SecureTempFile, self).setUp() + + _fd, self.file_name = tempfile.mkstemp() + # Make sure no file descriptors are leaked, close the unused FD. + os.close(_fd) + self.addCleanup(os.remove, self.file_name) -- cgit 1.2.3-korg