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 9581e54d6d..3af407543c 100644 --- a/tox.ini +++ b/tox.ini @@ -42,7 +42,7 @@ commands = flake8 --filename=swift* bin [testenv:func] -commands = nosetests {posargs:test/functional} +commands = ./.functests {posargs} [testenv:venv] commands = {posargs}