Switch to stestr under the covers

This commit switches the ostestr command to use stestr under the covers.
This simplifies the majority of the logic, eventually ostestr will
be deprecated as a result of this migration. (since almost all the logic
here is contained in stestr already)

Depends-On: I2c7618a742439fd2ed26879f3114f0f66fd6337f
Change-Id: Id7cb2a39a8308f1413608dcf19273a1d7f33592e
This commit is contained in:
Matthew Treinish 2017-07-28 10:43:53 -04:00
parent 8ca44080d7
commit 7dd678e372
10 changed files with 91 additions and 139 deletions

1
.gitignore vendored
View File

@ -27,6 +27,7 @@ cover/
.tox
nosetests.xml
.testrepository
.stestr
.venv
# Translations

3
.stestr.conf Normal file
View File

@ -0,0 +1,3 @@
[DEFAULT]
test_path=os_testr/tests
top_dir=./

View File

@ -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

View File

@ -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)
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:
cmd = ['testr', 'run']
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():

View File

@ -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,

View File

@ -0,0 +1,3 @@
[DEFAULT]
test_path=./tests
group_regex=([^\.]*\.)*

View File

@ -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=([^\.]*\.)*

View File

@ -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)

View File

@ -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

12
tox.ini
View File

@ -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