diff --git a/.gitignore b/.gitignore index 3576b8447..90d201e2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .coverage .venv +.testrepository +subunit.log .tox *,cover cover diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 000000000..2109af6ce --- /dev/null +++ b/.testr.conf @@ -0,0 +1,4 @@ +[DEFAULT] +test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ./tests $LISTOPT $IDOPTION +test_id_option=--load-list $IDFILE +test_list_option=--list diff --git a/HACKING b/HACKING index f131e04a1..1c1a04a73 100644 --- a/HACKING +++ b/HACKING @@ -113,3 +113,11 @@ Text encoding returntext = do_some_magic_with(mytext) returnstring = returntext.encode('utf-8') outfile.write(returnstring) + +Running Tests +------------- +The testing system is based on a combination of tox and testr. If you just +want to run the whole suite, run `tox` and all will be fine. However, if +you'd like to dig in a bit more, you might want to learn some things about +testr itself. A basic walkthrough for OpenStack can be found at +http://wiki.openstack.org/testr diff --git a/run_tests.sh b/run_tests.sh index aa832b353..e33a95d98 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -33,8 +33,8 @@ function process_option { -p|--pep8) just_pep8=1;; -P|--no-pep8) no_pep8=1;; -c|--coverage) coverage=1;; - -*) noseopts="$noseopts $1";; - *) noseargs="$noseargs $1" + -*) testropts="$testropts $1";; + *) testrargs="$testrargs $1" esac } @@ -45,34 +45,62 @@ never_venv=0 force=0 no_site_packages=0 installvenvopts= -noseargs= -noseopts= +testrargs= +testropts= wrapper="" just_pep8=0 no_pep8=0 coverage=0 +LANG=en_US.UTF-8 +LANGUAGE=en_US:en +LC_ALL=C + for arg in "$@"; do process_option $arg done -# If enabled, tell nose to collect coverage data -if [ $coverage -eq 1 ]; then - noseopts="$noseopts --with-coverage --cover-package=novaclient" -fi - if [ $no_site_packages -eq 1 ]; then installvenvopts="--no-site-packages" fi +function init_testr { + if [ ! -d .testrepository ]; then + ${wrapper} testr init + fi +} + function run_tests { + # Cleanup *pyc + ${wrapper} find . -type f -name "*.pyc" -delete + + if [ $coverage -eq 1 ]; then + # Do not test test_coverage_ext when gathering coverage. + if [ "x$testrargs" = "x" ]; then + testrargs = "^(?!.*test_coverage_ext).*$" + fi + export PYTHON="${wrapper} coverage run --source novaclient --parallel-mode" + fi # Just run the test suites in current environment - ${wrapper} $NOSETESTS - # If we get some short import error right away, print the error log directly + set +e + TESTRTESTS="$TESTRTESTS $testrargs" + echo "Running \`${wrapper} $TESTRTESTS\`" + ${wrapper} $TESTRTESTS RESULT=$? + set -e + + copy_subunit_log + return $RESULT } +function copy_subunit_log { + LOGNAME=`cat .testrepository/next-stream` + LOGNAME=$(($LOGNAME - 1)) + LOGNAME=".testrepository/${LOGNAME}" + cp $LOGNAME subunit.log +} + function run_pep8 { echo "Running pep8 ..." srcfiles="novaclient tests" @@ -96,7 +124,7 @@ function run_pep8 { ${wrapper} pep8 ${pep8_opts} ${srcfiles} } -NOSETESTS="nosetests $noseopts $noseargs" +TESTRTESTS="testr run --parallel $testropts" if [ $never_venv -eq 0 ] then @@ -134,13 +162,14 @@ if [ $just_pep8 -eq 1 ]; then exit fi +init_testr run_tests # NOTE(sirp): we only want to run pep8 when we're running the full-test suite, # not when we're running tests individually. To handle this, we need to # distinguish between options (noseopts), which begin with a '-', and -# arguments (noseargs). -if [ -z "$noseargs" ]; then +# arguments (testrargs). +if [ -z "$testrargs" ]; then if [ $no_pep8 -eq 0 ]; then run_pep8 fi @@ -148,5 +177,6 @@ fi if [ $coverage -eq 1 ]; then echo "Generating coverage report in covhtml/" - ${wrapper} coverage html -d covhtml -i + ${wrapper} cverage combine + ${wrapper} coverage html --include='novaclient/*' --omit='novaclient/openstack/common/*' -d covhtml -i fi diff --git a/setup.cfg b/setup.cfg index 7fa3ddac5..11c72013c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,11 +1,3 @@ -[nosetests] -cover-package = novaclient -cover-html = true -cover-erase = true -cover-inclusive = true -verbosity=2 -detailed-errors=1 - [build_sphinx] source-dir = doc/source build-dir = doc/build diff --git a/setup.py b/setup.py index e0e68fa14..062e49b75 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,6 @@ setuptools.setup( url="https://github.com/openstack/python-novaclient", packages=setuptools.find_packages(exclude=['tests', 'tests.*']), install_requires=setup.parse_requirements(), - test_suite="nose.collector", cmdclass=setup.get_cmdclass(), classifiers=[ "Development Status :: 5 - Production/Stable", diff --git a/tests/utils.py b/tests/utils.py index ef231a775..d3a744219 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,3 +1,6 @@ +import os + +import fixtures import requests import testtools @@ -8,6 +11,17 @@ class TestCase(testtools.TestCase): 'verify': True, } + def setUp(self): + super(TestCase, self).setUp() + if (os.environ.get('OS_STDOUT_NOCAPTURE') == 'True' and + os.environ.get('OS_STDOUT_NOCAPTURE') == '1'): + stdout = self.useFixture(fixtures.StringStream('stdout')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) + if (os.environ.get('OS_STDERR_NOCAPTURE') == 'True' and + os.environ.get('OS_STDERR_NOCAPTURE') == '1'): + stderr = self.useFixture(fixtures.StringStream('stderr')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) + class TestResponse(requests.Response): """ diff --git a/tools/test-requires b/tools/test-requires index f69ef390d..0dacea2ac 100644 --- a/tools/test-requires +++ b/tools/test-requires @@ -1,12 +1,10 @@ distribute>=0.6.24 -fixtures +coverage +discover +fixtures>=0.3.12 mock -nose -nose-exclude -nosexcover -openstack.nose_plugin -nosehtmloutput pep8==1.1 sphinx>=1.1.2 -testtools +testrepository>=0.0.13 +testtools>=0.9.26 diff --git a/tox.ini b/tox.ini index 7ddc885b1..88fb5cc40 100644 --- a/tox.ini +++ b/tox.ini @@ -3,14 +3,13 @@ envlist = py26,py27,pep8 [testenv] setenv = VIRTUAL_ENV={envdir} - NOSE_WITH_OPENSTACK=1 - NOSE_OPENSTACK_COLOR=1 - NOSE_OPENSTACK_RED=0.05 - NOSE_OPENSTACK_YELLOW=0.025 - NOSE_OPENSTACK_SHOW_ELAPSED=1 + LANG=en_US.UTF-8 + LANGUAGE=en_US:en + LC_ALL=C + deps = -r{toxinidir}/tools/pip-requires -r{toxinidir}/tools/test-requires -commands = nosetests +commands = python setup.py testr --testr-args='{posargs}' [testenv:pep8] deps = pep8==1.1 @@ -20,27 +19,7 @@ commands = pep8 --repeat --show-source novaclient setup.py commands = {posargs} [testenv:cover] -commands = nosetests --cover-erase --cover-package=novaclient --with-xcoverage +commands = python setup.py testr --coverage --testr-args='{posargs}' [tox:jenkins] downloadcache = ~/cache/pip - -[testenv:jenkins26] -basepython = python2.6 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkins27] -basepython = python2.7 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkinscover] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = nosetests --cover-erase --cover-package=novaclient --with-xcoverage - -[testenv:jenkinsvenv] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = {posargs}