diff --git a/.travis.yml b/.travis.yml index 2d092a01..c59bac4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,124 +12,73 @@ matrix: include: - python: '2.7' env: - - TOXENV=py27-t35-c44 + - TOXENV=py27-t310-c45 - python: '2.7' env: - - TOXENV=py27-t35-c45 + - TOXENV=py27-t40-c45 - python: '2.7' env: - - TOXENV=py27-t36-c44 - - python: '2.7' - env: - - TOXENV=py27-t36-c45 - - python: '2.7' - env: - - TOXENV=py27-t37-c44 - - python: '2.7' - env: - - TOXENV=py27-t37-c45 - - python: '3.4' - env: - - TOXENV=py34-t35-c44 - - python: '3.4' - env: - - TOXENV=py34-t35-c45 - - python: '3.4' - env: - - TOXENV=py34-t36-c44 + - TOXENV=py27-t41-c45 - python: '3.4' env: - - TOXENV=py34-t36-c45 + - TOXENV=py34-t310-c45 - python: '3.4' env: - - TOXENV=py34-t37-c44 + - TOXENV=py34-t40-c45 - python: '3.4' env: - - TOXENV=py34-t37-c45 - - python: '3.5' - env: - - TOXENV=py35-t35-c44 - - python: '3.5' - env: - - TOXENV=py35-t35-c45 - - python: '3.5' - env: - - TOXENV=py35-t36-c44 + - TOXENV=py34-t41-c45 - python: '3.5' env: - - TOXENV=py35-t36-c45 + - TOXENV=py35-t310-c45 - python: '3.5' env: - - TOXENV=py35-t37-c44 + - TOXENV=py35-t40-c45 - python: '3.5' env: - - TOXENV=py35-t37-c45 + - TOXENV=py35-t41-c45 - python: '3.6' env: - - TOXENV=py36-t35-c44 + - TOXENV=py36-t310-c45 - python: '3.6' env: - - TOXENV=py36-t35-c45 + - TOXENV=py36-t40-c45 - python: '3.6' env: - - TOXENV=py36-t36-c44 - - python: '3.6' - env: - - TOXENV=py36-t36-c45 - - python: '3.6' - env: - - TOXENV=py36-t37-c44 - - python: '3.6' - env: - - TOXENV=py36-t37-c45 + - TOXENV=py36-t41-c45 - python: '3.7' - env: - - TOXENV=py37-t35-c44 dist: xenial sudo: required - - python: '3.7' env: - - TOXENV=py37-t35-c45 - dist: xenial - sudo: required + - TOXENV=py37-t310-c45 - python: '3.7' - env: - - TOXENV=py37-t36-c44 dist: xenial sudo: required - - python: '3.7' env: - - TOXENV=py37-t36-c45 - dist: xenial - sudo: required + - TOXENV=py37-t40-c45 - python: '3.7' - env: - - TOXENV=py37-t37-c44 dist: xenial sudo: required - - python: '3.7' env: - - TOXENV=py37-t37-c45 - dist: xenial - sudo: required - - python: 'pypy-5.4' + - TOXENV=py37-t41-c45 + - python: 'pypy' env: - - TOXENV=pypy-t35-c44 - - python: 'pypy-5.4' + - TOXENV=pypy-t310-c45 + - python: 'pypy' env: - - TOXENV=pypy-t35-c45 - - python: 'pypy-5.4' + - TOXENV=pypy-t40-c45 + - python: 'pypy' env: - - TOXENV=pypy-t36-c44 - - python: 'pypy-5.4' + - TOXENV=pypy-t41-c45 + - python: 'pypy3' env: - - TOXENV=pypy-t36-c45 - - python: 'pypy-5.4' + - TOXENV=pypy3-t310-c45 + - python: 'pypy3' env: - - TOXENV=pypy-t37-c44 - - python: 'pypy-5.4' + - TOXENV=pypy3-t40-c45 + - python: 'pypy3' env: - - TOXENV=pypy-t37-c45 + - TOXENV=pypy3-t41-c45 before_install: - python --version - uname -a @@ -140,6 +89,27 @@ install: - easy_install --version - pip --version - tox --version + - | + set -ex + if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then + (cd $HOME + wget https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-linux64.tar.bz2 + tar xf pypy2-*.tar.bz2 + pypy2-*/bin/pypy -m ensurepip + pypy2-*/bin/pypy -m pip install -U virtualenv) + export PATH=$(echo $HOME/pypy2-*/bin):$PATH + export TOXPYTHON=$(echo $HOME/pypy2-*/bin/pypy) + fi + if [[ $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then + (cd $HOME + wget https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux64.tar.bz2 + tar xf pypy3-*.tar.bz2 + pypy3-*/bin/pypy3 -m ensurepip + pypy3-*/bin/pypy3 -m pip install -U virtualenv) + export PATH=$(echo $HOME/pypy3-*/bin):$PATH + export TOXPYTHON=$(echo $HOME/pypy3-*/bin/pypy3) + fi + set +x script: - tox -v after_failure: diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index e29333af..17d7c3d4 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -83,7 +83,7 @@ Tips To run a subset of tests:: - tox -e envname -- py.test -k test_myfeature + tox -e envname -- pytest -k test_myfeature To run all the test environments in *parallel* (you need to ``pip install detox``):: diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 7798166b..e64a6c08 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -4,15 +4,15 @@ import os import sys +from os.path import abspath +from os.path import dirname from os.path import exists from os.path import join -from os.path import dirname -from os.path import abspath if __name__ == "__main__": base_path = dirname(dirname(abspath(__file__))) - print("Project path: {}".format(base_path)) + print("Project path: {0}".format(base_path)) env_path = join(base_path, ".tox", "bootstrap") if sys.platform == "win32": bin_path = join(env_path, "Scripts") @@ -20,21 +20,23 @@ bin_path = join(env_path, "bin") if not exists(env_path): import subprocess - print("Making bootstrap env in: {} ...".format(env_path)) + + print("Making bootstrap env in: {0} ...".format(env_path)) try: subprocess.check_call(["virtualenv", env_path]) - except Exception: + except subprocess.CalledProcessError: subprocess.check_call([sys.executable, "-m", "virtualenv", env_path]) - print("Installing `jinja2` into bootstrap environment ...") + print("Installing `jinja2` into bootstrap environment...") subprocess.check_call([join(bin_path, "pip"), "install", "jinja2"]) - activate = join(bin_path, "activate_this.py") - exec(compile(open(activate, "rb").read(), activate, "exec"), dict(__file__=activate)) + python_executable = join(bin_path, "python") + if not os.path.samefile(python_executable, sys.executable): + print("Re-executing with: {0}".format(python_executable)) + os.execv(python_executable, [python_executable, __file__]) import jinja2 import subprocess - jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), trim_blocks=True, @@ -42,15 +44,15 @@ keep_trailing_newline=True ) - tox_environments = [line.strip() for line in subprocess.check_output(['tox', '--listenvs'], universal_newlines=True).splitlines()] + tox_environments = [ + line.strip() + # WARNING: 'tox' must be installed globally or in the project's virtualenv + for line in subprocess.check_output(['tox', '--listenvs'], universal_newlines=True).splitlines() + ] tox_environments = [line for line in tox_environments if line not in ['clean', 'report', 'docs', 'check']] - template_vars = {'tox_environments': tox_environments} - for py_ver in '27 34 35 py'.split(): - template_vars['py%s_environments' % py_ver] = [x for x in tox_environments if x.startswith('py' + py_ver)] - for name in os.listdir(join("ci", "templates")): with open(join(base_path, name), "w") as fh: - fh.write(jinja.get_template(name).render(**template_vars)) + fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) print("Wrote {}".format(name)) print("DONE.") diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 88969802..60674e0d 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -11,14 +11,13 @@ env: matrix: include: {%- for env in tox_environments %}{{ '' }} - - python: '{{ '{0[0]}-5.4'.format(env.split('-')) if env.startswith('pypy') else '{0[2]}.{0[3]}'.format(env) }}' - env: - - TOXENV={{ env -}} + - python: '{{ env.split("-")[0] if env.startswith("pypy") else "{0[2]}.{0[3]}".format(env) }}' {% if env.startswith('py37') %} - dist: xenial sudo: required -{%- endif -%} +{% endif %} + env: + - TOXENV={{ env }}{% if 'cover' in env %},report,coveralls,codecov{% endif -%} {%- endfor %}{{ '' }} before_install: - python --version @@ -30,6 +29,27 @@ install: - easy_install --version - pip --version - tox --version + - | + set -ex + if [[ $TRAVIS_PYTHON_VERSION == 'pypy' ]]; then + (cd $HOME + wget https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-linux64.tar.bz2 + tar xf pypy2-*.tar.bz2 + pypy2-*/bin/pypy -m ensurepip + pypy2-*/bin/pypy -m pip install -U virtualenv) + export PATH=$(echo $HOME/pypy2-*/bin):$PATH + export TOXPYTHON=$(echo $HOME/pypy2-*/bin/pypy) + fi + if [[ $TRAVIS_PYTHON_VERSION == 'pypy3' ]]; then + (cd $HOME + wget https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux64.tar.bz2 + tar xf pypy3-*.tar.bz2 + pypy3-*/bin/pypy3 -m ensurepip + pypy3-*/bin/pypy3 -m pip install -U virtualenv) + export PATH=$(echo $HOME/pypy3-*/bin):$PATH + export TOXPYTHON=$(echo $HOME/pypy3-*/bin/pypy3) + fi + set +x script: - tox -v after_failure: diff --git a/docs/config.rst b/docs/config.rst index 1f2d8a33..07b8f020 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -8,7 +8,7 @@ further control of coverage use a coverage config file. For example if tests are contained within the directory tree being measured the tests may be excluded if desired by using a .coveragerc file with the omit option set:: - py.test --cov-config .coveragerc + pytest --cov-config .coveragerc --cov=myproj myproj/tests/ diff --git a/docs/plugins.rst b/docs/plugins.rst index 9ef82f8f..e1284798 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -8,7 +8,7 @@ See `pytest/issues/935 `_ of course):: diff --git a/docs/reporting.rst b/docs/reporting.rst index ce9d3682..e9e4b06b 100644 --- a/docs/reporting.rst +++ b/docs/reporting.rst @@ -8,7 +8,7 @@ annotated source code. The terminal report without line numbers (default):: - py.test --cov-report term --cov=myproj tests/ + pytest --cov-report term --cov=myproj tests/ -------------------- coverage: platform linux2, python 2.6.4-final-0 --------------------- Name Stmts Miss Cover @@ -22,7 +22,7 @@ The terminal report without line numbers (default):: The terminal report with line numbers:: - py.test --cov-report term-missing --cov=myproj tests/ + pytest --cov-report term-missing --cov=myproj tests/ -------------------- coverage: platform linux2, python 2.6.4-final-0 --------------------- Name Stmts Miss Cover Missing @@ -35,7 +35,7 @@ The terminal report with line numbers:: The terminal report with skip covered:: - py.test --cov-report term:skip-covered --cov=myproj tests/ + pytest --cov-report term:skip-covered --cov=myproj tests/ -------------------- coverage: platform linux2, python 2.6.4-final-0 --------------------- Name Stmts Miss Cover @@ -51,7 +51,7 @@ You can use ``skip-covered`` with ``term-missing`` as well. e.g. ``--cov-report These three report options output to files without showing anything on the terminal:: - py.test --cov-report html + pytest --cov-report html --cov-report xml --cov-report annotate --cov=myproj tests/ @@ -60,14 +60,14 @@ The output location for each of these reports can be specified. The output locat report is a file. Where as the output location for the HTML and annotated source code reports are directories:: - py.test --cov-report html:cov_html + pytest --cov-report html:cov_html --cov-report xml:cov.xml --cov-report annotate:cov_annotate --cov=myproj tests/ The final report option can also suppress printing to the terminal:: - py.test --cov-report= --cov=myproj tests/ + pytest --cov-report= --cov=myproj tests/ This mode can be especially useful on continuous integration servers, where a coverage file is needed for subsequent processing, but no local report needs to be viewed. For example, diff --git a/docs/xdist.rst b/docs/xdist.rst index 457e9fa6..86a63db4 100644 --- a/docs/xdist.rst +++ b/docs/xdist.rst @@ -11,7 +11,7 @@ file system. Each slave will have its subprocesses measured. Running distributed testing with dist mode set to load:: - py.test --cov=myproj -n 2 tests/ + pytest --cov=myproj -n 2 tests/ Shows a terminal report:: @@ -27,7 +27,7 @@ Shows a terminal report:: Again but spread over different hosts and different directories:: - py.test --cov=myproj --dist load + pytest --cov=myproj --dist load --tx ssh=memedough@host1//chdir=testenv1 --tx ssh=memedough@host2//chdir=/tmp/testenv2//python=/tmp/env1/bin/python --rsyncdir myproj --rsyncdir tests --rsync examples @@ -54,7 +54,7 @@ environments. Running distributed testing with dist mode set to each:: - py.test --cov=myproj --dist each + pytest --cov=myproj --dist each --tx popen//chdir=/tmp/testenv3//python=/usr/local/python27/bin/python --tx ssh=memedough@host2//chdir=/tmp/testenv4//python=/tmp/env2/bin/python --rsyncdir myproj --rsyncdir tests --rsync examples diff --git a/example/tox.ini b/example/tox.ini index 88f66529..430b5331 100644 --- a/example/tox.ini +++ b/example/tox.ini @@ -6,7 +6,7 @@ envlist = cov-init,py27,py34,cov-report usedevelop=True setenv = COVERAGE_FILE = .coverage.{envname} -commands = py.test --cov --cov-report= {posargs} +commands = pytest --cov --cov-report= {posargs} deps = ../cov-core pytest diff --git a/setup.cfg b/setup.cfg index 694845f4..ebddd8b6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ universal = 1 max-line-length = 140 exclude = tests/*,*/migrations/*,*/south_migrations/* -[pytest] +[tool:pytest] # using the deprecated name instead of [tool:pytest] cause we test with older pytest versions norecursedirs = .git diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 4f2267df..2896ac96 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -9,11 +9,12 @@ import py import pytest import virtualenv +import xdist +from fields import Namespace from process_tests import TestProcess as _TestProcess from process_tests import dump_on_error from process_tests import wait_for_strings from six import exec_ -from fields import Namespace try: from StringIO import StringIO @@ -28,10 +29,10 @@ import sys, helper def pytest_generate_tests(metafunc): - for i in range(10): - metafunc.addcall() + for i in [10]: + metafunc.parametrize('p', range(i)) -def test_foo(): +def test_foo(p): x = True helper.do_stuff() # get some coverage in some other completely different location if sys.version_info[0] > 5: @@ -70,8 +71,8 @@ def test_bar(): import sys def pytest_generate_tests(metafunc): - for i in range(2): - metafunc.addcall(funcargs=dict(idx=i)) + for i in [2]: + metafunc.parametrize('idx', range(i)) def test_foo(idx): out, err = subprocess.Popen( @@ -90,8 +91,8 @@ def test_foo(idx): import os def pytest_generate_tests(metafunc): - for i in range(2): - metafunc.addcall(funcargs=dict(idx=i)) + for i in [2]: + metafunc.parametrize('idx', range(i)) def test_foo(idx): os.mkdir("foobar") @@ -114,8 +115,8 @@ def test_foo(idx): import os def pytest_generate_tests(metafunc): - for i in range(2): - metafunc.addcall(funcargs=dict(idx=i)) + for i in [2]: + if metafunc.function is test_foo: metafunc.parametrize('idx', range(i)) def test_foo(idx): os.mkdir("foobar") @@ -143,18 +144,12 @@ def test_foo(cov): assert cov is None ''' -SCRIPT_FAIL = ''' -def test_fail(): - assert False - -''' - CHILD_SCRIPT_RESULT = '[56] * 100%' PARENT_SCRIPT_RESULT = '9 * 100%' DEST_DIR = 'cov_dest' REPORT_NAME = 'cov.xml' -xdist = pytest.mark.parametrize('opts', ['', '-n 1'], ids=['nodist', 'xdist']) +xdist_params = pytest.mark.parametrize('opts', ['', '-n 1'], ids=['nodist', 'xdist']) @pytest.fixture(params=[ @@ -396,7 +391,7 @@ def test_central_coveragerc(testdir, prop): assert result.ret == 0 -@xdist +@xdist_params def test_central_with_path_aliasing(testdir, monkeypatch, opts, prop): mod1 = testdir.mkdir('src').join('mod.py') mod1.write(SCRIPT) @@ -499,7 +494,11 @@ def test_show_missing_coveragerc(testdir, prop): def test_no_cov_on_fail(testdir): - script = testdir.makepyfile(SCRIPT_FAIL) + script = testdir.makepyfile(''' +def test_fail(): + assert False + +''') result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), @@ -527,7 +526,11 @@ def test_no_cov(testdir): def test_cov_and_failure_report_on_fail(testdir): - script = testdir.makepyfile(SCRIPT + SCRIPT_FAIL) + script = testdir.makepyfile(SCRIPT + ''' +def test_fail(p): + assert False + +''') result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), @@ -802,7 +805,7 @@ def test_dist_subprocess_not_collocated(testdir, tmpdir): def test_invalid_coverage_source(testdir): script = testdir.makepyfile(SCRIPT) testdir.makeini(""" - [pytest] + [tool:pytest] console_output_style=classic """) result = testdir.runpytest('-v', @@ -821,7 +824,7 @@ def test_invalid_coverage_source(testdir): ]) assert result.ret == 0 - matching_lines = [line for line in result.outlines if '%' in line] + matching_lines = [line for line in result.outlines if '%' in line and 'PASSED' not in line] assert not matching_lines @@ -838,7 +841,9 @@ def test_dist_missing_data(testdir): '-mpip', 'install', 'py==%s' % py.__version__, - 'pytest==%s' % pytest.__version__ + 'pytest==%s' % pytest.__version__, + 'pytest_xdist==%s' % xdist.__version__ + ]) script = testdir.makepyfile(SCRIPT) @@ -1341,8 +1346,8 @@ def test_external_data_file_negative(testdir): assert glob.glob(str(testdir.tmpdir.join('.coverage*'))) -@xdist -def xtest_append_coverage(testdir, opts, prop): +@xdist_params +def test_append_coverage(testdir, opts, prop): script = testdir.makepyfile(test_1=prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', @@ -1364,10 +1369,10 @@ def xtest_append_coverage(testdir, opts, prop): ]) -@xdist -def xtest_do_not_append_coverage(testdir, opts, prop): +@xdist_params +def test_do_not_append_coverage(testdir, opts, prop): script = testdir.makepyfile(test_1=prop.code) - testdir.tmpdir.join('.coveragerc').write("") + testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), script, diff --git a/tox.ini b/tox.ini index 1d88b75d..e4619594 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ [tox] envlist = - check, - {py27,py34,py35,py36,py37,pypy}-{t35,t36,t37}-{c44,c45} + check + {py27,py34,py35,py36,py37,pypy,pypy3}-{t310,t40,t41}-{c45} docs [testenv] @@ -21,13 +21,13 @@ setenv = passenv = * deps = - t35: pytest==3.5.1 - t36: pytest==3.6.4 - t37: pytest==3.7.4 - pytest-xdist==1.23.0 + t310: pytest==3.10.1 + t40: pytest==4.0.2 + t41: pytest==4.1.0 + pytest-xdist==1.25.0 c44: coverage==4.4.2 - c45: coverage==4.5.1 + c45: coverage==4.5.2 virtualenv hunter @@ -37,7 +37,7 @@ deps = pip_pre = true commands = - {posargs:py.test -vv} + {posargs:pytest -vv} [testenv:spell] setenv =