From 9d7f71d5754c8b45f8e7c6ab80202de09933afb8 Mon Sep 17 00:00:00 2001 From: Richard Hawkins Date: Fri, 7 Aug 2015 18:14:13 -0500 Subject: [PATCH] Modify functional tests to use ostestr/testr Defcore uses Tempest, which uses Test Repository. This change makes it easier for Defcore to pull functional tests from Swift and run them. Additionally, using testr allows tests to be run in parallel. Concurrency set to 1 for now, >1 causes failures for reasons that are still TBD. With switch to ostestr all the server logs are being sent to stdout which makes it completely unreadable. Suppressing the logs by default now with a flag to enable it if desired. Co-Authored-By: John Dickinson Co-Authored-By: Robert Collins Co-Authored-By: Matthew Oliver Co-Authored-By: Ganesh Maharaj Mahalingam Change-Id: I53ef4a116996a772cf1f3abc2eb0ad60047322d5 Related-Bug: 1177924 --- .functests | 6 ++-- .gitignore | 2 ++ .testr.conf | 6 ++++ test-requirements.txt | 1 + test/functional/__init__.py | 50 +++++++++++++++++++++++------ test/functional/test_account.py | 8 +++++ test/functional/test_container.py | 8 +++++ test/functional/test_object.py | 8 +++++ test/functional/tests.py | 8 +++++ test/unit/account/test_backend.py | 4 +-- test/unit/container/test_backend.py | 4 +-- tox.ini | 2 +- 12 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 .testr.conf diff --git a/.functests b/.functests index 65a9ea191c..af989f50ff 100755 --- a/.functests +++ b/.functests @@ -1,9 +1,11 @@ #!/bin/bash SRC_DIR=$(python -c "import os; print os.path.dirname(os.path.realpath('$0'))") +set -e -cd ${SRC_DIR}/test/functional -nosetests --exe $@ +cd ${SRC_DIR} +export TESTS_DIR=${SRC_DIR}/test/functional +ostestr --serial --pretty $@ rvalue=$? cd - diff --git a/.gitignore b/.gitignore index 9c4f1c6b10..580518daac 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ pycscope.* .idea MANIFEST +.testrepository/* +subunit.log test/probe/.noseids diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 0000000000..293e07d22d --- /dev/null +++ b/.testr.conf @@ -0,0 +1,6 @@ +[DEFAULT] +test_command=SWIFT_TEST_DEBUG_LOGS=${SWIFT_TEST_DEBUG_LOGS} \ + ${PYTHON:-python} -m subunit.run \ + discover -t ./ ${TESTS_DIR:-./test/functional/} $LISTOPT $IDOPTION +test_id_option=--load-list $IDFILE +test_list_option=--list diff --git a/test-requirements.txt b/test-requirements.txt index 73ca508fe3..0c6e9fe2cc 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -10,6 +10,7 @@ nosexcover nosehtmloutput oslosphinx sphinx>=1.1.2,<1.2 +os-testr>=0.4.1 mock>=1.0 python-swiftclient python-keystoneclient>=1.3.0 diff --git a/test/functional/__init__.py b/test/functional/__init__.py index 242a4667a9..a2b422ec01 100644 --- a/test/functional/__init__.py +++ b/test/functional/__init__.py @@ -109,7 +109,7 @@ orig_hash_path_suff_pref = ('', '') orig_swift_conf_name = None in_process = False -_testdir = _test_servers = _test_coros = None +_testdir = _test_servers = _test_coros = _test_socks = None policy_specified = None @@ -290,6 +290,7 @@ def in_process_setup(the_object_server=object_server): _info('IN-PROCESS SERVERS IN USE FOR FUNCTIONAL TESTS') _info('Using object_server class: %s' % the_object_server.__name__) conf_src_dir = os.environ.get('SWIFT_TEST_IN_PROCESS_CONF_DIR') + show_debug_logs = os.environ.get('SWIFT_TEST_DEBUG_LOGS') if conf_src_dir is not None: if not os.path.isdir(conf_src_dir): @@ -339,10 +340,13 @@ def in_process_setup(the_object_server=object_server): orig_hash_path_suff_pref = utils.HASH_PATH_PREFIX, utils.HASH_PATH_SUFFIX utils.validate_hash_conf() + global _test_socks + _test_socks = [] # We create the proxy server listening socket to get its port number so # that we can add it as the "auth_port" value for the functional test # clients. prolis = eventlet.listen(('localhost', 0)) + _test_socks.append(prolis) # The following set of configuration values is used both for the # functional test frame work and for the various proxy, account, container @@ -388,6 +392,7 @@ def in_process_setup(the_object_server=object_server): acc2lis = eventlet.listen(('localhost', 0)) con1lis = eventlet.listen(('localhost', 0)) con2lis = eventlet.listen(('localhost', 0)) + _test_socks += [acc1lis, acc2lis, con1lis, con2lis] + obj_sockets account_ring_path = os.path.join(_testdir, 'account.ring.gz') with closing(GzipFile(account_ring_path, 'wb')) as f: @@ -416,23 +421,30 @@ def in_process_setup(the_object_server=object_server): # Default to only 4 seconds for in-process functional test runs eventlet.wsgi.WRITE_TIMEOUT = 4 + def get_logger_name(name): + if show_debug_logs: + return debug_logger(name) + else: + return None + acc1srv = account_server.AccountController( - config, logger=debug_logger('acct1')) + config, logger=get_logger_name('acct1')) acc2srv = account_server.AccountController( - config, logger=debug_logger('acct2')) + config, logger=get_logger_name('acct2')) con1srv = container_server.ContainerController( - config, logger=debug_logger('cont1')) + config, logger=get_logger_name('cont1')) con2srv = container_server.ContainerController( - config, logger=debug_logger('cont2')) + config, logger=get_logger_name('cont2')) objsrvs = [ (obj_sockets[index], the_object_server.ObjectController( - config, logger=debug_logger('obj%d' % (index + 1)))) + config, logger=get_logger_name('obj%d' % (index + 1)))) for index in range(len(obj_sockets)) ] - logger = debug_logger('proxy') + if show_debug_logs: + logger = debug_logger('proxy') def get_logger(name, *args, **kwargs): return logger @@ -446,6 +458,8 @@ def in_process_setup(the_object_server=object_server): raise InProcessException(e) nl = utils.NullLogger() + global proxy_srv + proxy_srv = prolis prospa = eventlet.spawn(eventlet.wsgi.server, prolis, app, nl) acc1spa = eventlet.spawn(eventlet.wsgi.server, acc1lis, acc1srv, nl) acc2spa = eventlet.spawn(eventlet.wsgi.server, acc2lis, acc2srv, nl) @@ -487,6 +501,7 @@ def get_cluster_info(): # We'll update those constraints based on what the /info API provides, if # anything. global cluster_info + global config try: conn = Connection(config) conn.authenticate() @@ -536,6 +551,7 @@ def setup_package(): global in_process + global config if use_in_process: # Explicitly set to True, so barrel on ahead with in-process # functional test setup. @@ -722,7 +738,6 @@ def setup_package(): % policy_specified) raise Exception('Failed to find specified policy %s' % policy_specified) - get_cluster_info() @@ -731,16 +746,21 @@ def teardown_package(): locale.setlocale(locale.LC_COLLATE, orig_collate) # clean up containers and objects left behind after running tests + global config conn = Connection(config) conn.authenticate() account = Account(conn, config.get('account', config['username'])) account.delete_containers() global in_process + global _test_socks if in_process: try: - for server in _test_coros: + for i, server in enumerate(_test_coros): server.kill() + if not server.dead: + # kill it from the socket level + _test_socks[i].close() except Exception: pass try: @@ -751,6 +771,7 @@ def teardown_package(): orig_hash_path_suff_pref utils.SWIFT_CONF_FILE = orig_swift_conf_name constraints.reload_constraints() + reset_globals() class AuthError(Exception): @@ -768,6 +789,17 @@ parsed = [None, None, None, None, None] conn = [None, None, None, None, None] +def reset_globals(): + global url, token, service_token, parsed, conn, config + url = [None, None, None, None, None] + token = [None, None, None, None, None] + service_token = [None, None, None, None, None] + parsed = [None, None, None, None, None] + conn = [None, None, None, None, None] + if config: + config = {} + + def connection(url): if has_insecure: parsed_url, http_conn = http_connection(url, insecure=insecure) diff --git a/test/functional/test_account.py b/test/functional/test_account.py index e952c0923b..e2847f29be 100755 --- a/test/functional/test_account.py +++ b/test/functional/test_account.py @@ -29,6 +29,14 @@ from test.functional import check_response, retry, requires_acls, \ import test.functional as tf +def setUpModule(): + tf.setup_package() + + +def tearDownModule(): + tf.teardown_package() + + class TestAccount(unittest.TestCase): def setUp(self): diff --git a/test/functional/test_container.py b/test/functional/test_container.py index 345aa0aa84..c4a8a3fcdf 100755 --- a/test/functional/test_container.py +++ b/test/functional/test_container.py @@ -27,6 +27,14 @@ import test.functional as tf from six.moves import range +def setUpModule(): + tf.setup_package() + + +def tearDownModule(): + tf.teardown_package() + + class TestContainer(unittest.TestCase): def setUp(self): diff --git a/test/functional/test_object.py b/test/functional/test_object.py index 55868098be..b13d3f8fe7 100755 --- a/test/functional/test_object.py +++ b/test/functional/test_object.py @@ -27,6 +27,14 @@ from test.functional import check_response, retry, requires_acls, \ import test.functional as tf +def setUpModule(): + tf.setup_package() + + +def tearDownModule(): + tf.teardown_package() + + class TestObject(unittest.TestCase): def setUp(self): diff --git a/test/functional/tests.py b/test/functional/tests.py index 571a6b3473..fcc239c4c4 100644 --- a/test/functional/tests.py +++ b/test/functional/tests.py @@ -39,6 +39,14 @@ from test.functional.swift_test_client import Account, Connection, File, \ ResponseError +def setUpModule(): + tf.setup_package() + + +def tearDownModule(): + tf.teardown_package() + + class Utils(object): @classmethod def create_ascii_name(cls, length=None): diff --git a/test/unit/account/test_backend.py b/test/unit/account/test_backend.py index ca89736e5c..ebc0ebfca2 100644 --- a/test/unit/account/test_backend.py +++ b/test/unit/account/test_backend.py @@ -37,7 +37,7 @@ from test.unit import patch_policies, with_tempdir, make_timestamp_iter from swift.common.db import DatabaseConnectionError from swift.common.storage_policy import StoragePolicy, POLICIES -from test.unit.common.test_db import TestExampleBroker +from test.unit.common import test_db @patch_policies @@ -979,7 +979,7 @@ def premetadata_create_account_stat_table(self, conn, put_timestamp): put_timestamp)) -class TestCommonAccountBroker(TestExampleBroker): +class TestCommonAccountBroker(test_db.TestExampleBroker): broker_class = AccountBroker diff --git a/test/unit/container/test_backend.py b/test/unit/container/test_backend.py index 332e161eef..721f0f9094 100644 --- a/test/unit/container/test_backend.py +++ b/test/unit/container/test_backend.py @@ -36,7 +36,7 @@ import mock from test.unit import (patch_policies, with_tempdir, make_timestamp_iter, EMPTY_ETAG) -from test.unit.common.test_db import TestExampleBroker +from test.unit.common import test_db class TestContainerBroker(unittest.TestCase): @@ -1680,7 +1680,7 @@ class TestContainerBroker(unittest.TestCase): self.assertEqual(broker.get_policy_stats(), expected) -class TestCommonContainerBroker(TestExampleBroker): +class TestCommonContainerBroker(test_db.TestExampleBroker): broker_class = ContainerBroker diff --git a/tox.ini b/tox.ini index ac22896de4..46e7b37494 100644 --- a/tox.ini +++ b/tox.ini @@ -45,7 +45,7 @@ commands = flake8 --filename=swift* bin [testenv:func] -commands = nosetests {posargs:test/functional} +commands = ./.functests {posargs} [testenv:venv] commands = {posargs}