diff --git a/.gitignore b/.gitignore index d6ae633..ed8e4b5 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ cover/ .tox nosetests.xml .testrepository +.stestr .venv # Translations diff --git a/.stestr.conf b/.stestr.conf new file mode 100644 index 0000000..2b84f4f --- /dev/null +++ b/.stestr.conf @@ -0,0 +1,3 @@ +[DEFAULT] +test_path=os_testr/tests +top_dir=./ diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 6d83b3c..0000000 --- a/.testr.conf +++ /dev/null @@ -1,7 +0,0 @@ -[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 ./ . $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/os_testr/ostestr.py b/os_testr/ostestr.py index 1c41079..7d18068 100755 --- a/os_testr/ostestr.py +++ b/os_testr/ostestr.py @@ -14,21 +14,21 @@ # under the License. import argparse -import atexit import copy import io import os import subprocess import sys -import tempfile +import warnings import pbr.version +import six.moves +from stestr import commands from subunit import run as subunit_run from testtools import run as testtools_run from os_testr import regex_builder as rb -from os_testr import testlist_builder as tlb __version__ = pbr.version.VersionInfo('os_testr').version_string() @@ -107,92 +107,68 @@ def get_parser(args): def call_testr(regex, subunit, pretty, list_tests, slowest, parallel, concur, - until_failure, color, list_of_tests=None, others=None): - others = others or [] - if parallel: - cmd = ['testr', 'run', '--parallel'] - if concur: - cmd.append('--concurrency=%s' % concur) - else: - cmd = ['testr', 'run'] + until_failure, color, others=None, blacklist_file=None, + whitelist_file=None, black_regex=None, load_list=None): + # Handle missing .stestr.conf from users from before stestr migration + test_dir = None + top_dir = None + group_regex = None + if not os.path.isfile('.stestr.conf') and os.path.isfile('.testr.conf'): + msg = ('No .stestr.conf file found in the CWD. Please create one to ' + 'to replace the .testr.conf. You can find a script to do this ' + 'in the stestr git repository.') + warnings.warn(msg) + + with open('.testr.conf', 'r') as testr_conf_file: + config = six.moves.configparser.ConfigParser() + config.readfp(testr_conf_file) + test_command = config.get('DEFAULT', 'test_command') + group_regex = None + if config.has_option('DEFAULT', 'group_regex'): + group_regex = config.get('DEFAULT', 'group_regex') + + for line in test_command.split('\n'): + if 'subunit.run discover' in line: + command_parts = line.split(' ') + top_dir_present = '-t' in line + for idx, val in enumerate(command_parts): + if top_dir_present: + if val == '-t': + top_dir = command_parts[idx + 1] + test_dir = command_parts[idx + 2] + else: + if val == 'discover': + test_dir = command_parts[idx + 2] + elif not os.path.isfile( + '.testr.conf') and not os.path.isfile('.stestr.conf'): + msg = ('No .stestr.conf found, please create one.') + print(msg) + sys.exit(1) + + regexes = None + if regex: + regexes = regex.split() + serial = not parallel if list_tests: - cmd = ['testr', 'list-tests'] - elif (subunit or pretty) and not until_failure: - cmd.append('--subunit') - elif not (subunit or pretty) and until_failure: - cmd.append('--until-failure') - if list_of_tests: - test_fd, test_file_name = tempfile.mkstemp() - atexit.register(os.remove, test_file_name) - test_file = os.fdopen(test_fd, 'w') - test_file.write('\n'.join(list_of_tests) + '\n') - test_file.close() - cmd.extend(('--load-list', test_file_name)) - elif regex: - cmd.append(regex) + # TODO(mtreinish): remove init call after list command detects and + # autocreates the repository + if not os.path.isdir('.stestr'): + commands.init_command() + return commands.list_command(filters=regexes) + return_code = commands.run_command(filters=regexes, subunit_out=subunit, + concurrency=concur, test_path=test_dir, + top_dir=top_dir, + group_regex=group_regex, + until_failure=until_failure, + serial=serial, pretty_out=pretty, + load_list=load_list, + blacklist_file=blacklist_file, + whitelist_file=whitelist_file, + black_regex=black_regex) - env = copy.deepcopy(os.environ) - - if pretty: - subunit_trace_cmd = ['subunit-trace', '--no-failure-debug', '-f'] - if color: - subunit_trace_cmd.append('--color') - - # This workaround is necessary because of lp bug 1411804 it's super hacky - # and makes tons of unfounded assumptions, but it works for the most part - if (subunit or pretty) and until_failure: - test_list = rb._get_test_list(regex, env) - count = 0 - failed = False - if not test_list: - print("No tests to run") - return 1 - # If pretty or subunit output is desired manually loop forever over - # test individually and generate the desired output in a linear series - # this avoids 1411804 while retaining most of the desired behavior - while True: - for test in test_list: - if pretty: - cmd = ['python', '-m', 'subunit.run', test] - ps = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE) - subunit_trace_cmd.append('--no-summary') - proc = subprocess.Popen(subunit_trace_cmd, - env=env, - stdin=ps.stdout) - ps.stdout.close() - proc.communicate() - if proc.returncode > 0: - failed = True - break - else: - try: - subunit_run.main([sys.argv[0], test], sys.stdout) - except SystemExit as e: - if e > 0: - print("Ran %s tests without failure" % count) - return 1 - else: - raise - count = count + 1 - if failed: - print("Ran %s tests without failure" % count) - return 0 - # If not until-failure special case call testr like normal - elif pretty and not list_tests: - cmd.extend(others) - ps = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE) - proc = subprocess.Popen(subunit_trace_cmd, - env=env, stdin=ps.stdout) - ps.stdout.close() - else: - cmd.extend(others) - proc = subprocess.Popen(cmd, env=env) - proc.communicate() - return_code = proc.returncode - if slowest and not list_tests: - print("\nSlowest Tests:\n") - slow_proc = subprocess.Popen(['testr', 'slowest'], env=env) - slow_proc.communicate() + if slowest: + sys.stdout.write("\nSlowest Tests:\n") + commands.slowest_command() return return_code @@ -224,19 +200,16 @@ def call_subunit_run(test_id, pretty, subunit): testtools_run.main([sys.argv[0], test_id], sys.stdout) -def _ensure_testr(): - if not os.path.isdir('.testrepository'): - subprocess.call(['testr', 'init']) - - def _select_and_call_runner(opts, exclude_regex, others): ec = 1 - _ensure_testr() if not opts.no_discover and not opts.pdb: ec = call_testr(exclude_regex, opts.subunit, opts.pretty, opts.list, opts.slowest, opts.parallel, opts.concurrency, - opts.until_failure, opts.color, None, others) + opts.until_failure, opts.color, others, + blacklist_file=opts.blacklist_file, + whitelist_file=opts.whitelist_file, + black_regex=opts.black_regex) else: if others: print('Unexpected arguments: ' + ' '.join(others)) @@ -248,24 +221,6 @@ def _select_and_call_runner(opts, exclude_regex, others): return ec -def _call_testr_with_list(opts, test_list, others): - ec = 1 - _ensure_testr() - - if opts.list: - print("\n".join(test_list)) - return 0 - - if not test_list: - print("No testcase selected to run") - return 8 - - ec = call_testr(None, opts.subunit, opts.pretty, opts.list, - opts.slowest, opts.parallel, opts.concurrency, - opts.until_failure, opts.color, test_list, others) - return ec - - def ostestr(args): opts, others = get_parser(args) if opts.pretty and opts.subunit: @@ -301,15 +256,7 @@ def ostestr(args): else: regex = opts.regex - if opts.blacklist_file or opts.whitelist_file or opts.black_regex: - list_of_tests = tlb.construct_list(opts.blacklist_file, - opts.whitelist_file, - regex, - opts.black_regex, - opts.print_exclude) - return (_call_testr_with_list(opts, list_of_tests, others)) - else: - return (_select_and_call_runner(opts, regex, others)) + return _select_and_call_runner(opts, regex, others) def main(): diff --git a/os_testr/regex_builder.py b/os_testr/regex_builder.py index 81a917a..6c373d4 100644 --- a/os_testr/regex_builder.py +++ b/os_testr/regex_builder.py @@ -19,7 +19,7 @@ import subprocess def _get_test_list(regex, env=None): env = env or copy.deepcopy(os.environ) - testr_args = ['testr', 'list-tests'] + testr_args = ['stestr', 'list'] if regex: testr_args.append(regex) proc = subprocess.Popen(testr_args, env=env, diff --git a/os_testr/tests/files/stestr-conf b/os_testr/tests/files/stestr-conf new file mode 100644 index 0000000..63b3c44 --- /dev/null +++ b/os_testr/tests/files/stestr-conf @@ -0,0 +1,3 @@ +[DEFAULT] +test_path=./tests +group_regex=([^\.]*\.)* diff --git a/os_testr/tests/files/testr-conf b/os_testr/tests/files/testr-conf deleted file mode 100644 index d5ad083..0000000 --- a/os_testr/tests/files/testr-conf +++ /dev/null @@ -1,5 +0,0 @@ -[DEFAULT] -test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./tests $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list -group_regex=([^\.]*\.)* diff --git a/os_testr/tests/test_return_codes.py b/os_testr/tests/test_return_codes.py index 4c336b1..2595aa8 100644 --- a/os_testr/tests/test_return_codes.py +++ b/os_testr/tests/test_return_codes.py @@ -34,13 +34,13 @@ class TestReturnCodes(base.TestCase): self.test_dir = os.path.join(self.directory, 'tests') os.mkdir(self.test_dir) # Setup Test files - self.testr_conf_file = os.path.join(self.directory, '.testr.conf') + self.testr_conf_file = os.path.join(self.directory, '.stestr.conf') self.setup_cfg_file = os.path.join(self.directory, 'setup.cfg') self.passing_file = os.path.join(self.test_dir, 'test_passing.py') self.failing_file = os.path.join(self.test_dir, 'test_failing.py') self.init_file = os.path.join(self.test_dir, '__init__.py') self.setup_py = os.path.join(self.directory, 'setup.py') - shutil.copy('os_testr/tests/files/testr-conf', self.testr_conf_file) + shutil.copy('os_testr/tests/files/stestr-conf', self.testr_conf_file) shutil.copy('os_testr/tests/files/passing-tests', self.passing_file) shutil.copy('os_testr/tests/files/failing-tests', self.failing_file) shutil.copy('setup.py', self.setup_py) @@ -104,4 +104,4 @@ class TestReturnCodes(base.TestCase): self.assertRunExit('ostestr --list', 0) def test_no_test(self): - self.assertRunExit('ostestr --regex a --black-regex a', 8) + self.assertRunExit('ostestr --regex a --black-regex a', 1) diff --git a/requirements.txt b/requirements.txt index 4fc7eb7..95cd081 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,6 @@ # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 -testrepository>=0.0.18 # Apache-2.0/BSD +stestr>=1.0.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT diff --git a/tox.ini b/tox.ini index 08fca43..c7f210e 100644 --- a/tox.ini +++ b/tox.ini @@ -10,6 +10,9 @@ setenv = VIRTUAL_ENV={envdir} BRANCH_NAME=master CLIENT_NAME=os-testr + OS_STDOUT_CAPTURE=1 + OS_STDERR_CAPTURE=1 + OS_TEST_TIMEOUT=500 whitelist_externals = find deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt @@ -24,7 +27,14 @@ commands = flake8 commands = {posargs} [testenv:cover] -commands = python setup.py test --coverage --coverage-package-name='os_testr' --testr-args='{posargs}' +setenv = + VIRTUAL_ENV={envdir} + PYTHON=coverage run --source os_testr --parallel-mode +commands = + ostestr {posargs} + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml [testenv:docs] commands = python setup.py build_sphinx