commit
f2659f77be
|
@ -1,8 +1,7 @@
|
||||||
# evaluating GitHub actions for CI, disconsider failures when evaluating PRs
|
# evaluating GitHub actions for CI, disregard failures when evaluating PRs
|
||||||
#
|
#
|
||||||
# this is still missing:
|
# this is still missing:
|
||||||
# - deploy
|
# - deploy
|
||||||
# - coverage
|
|
||||||
# - upload github notes
|
# - upload github notes
|
||||||
#
|
#
|
||||||
name: main
|
name: main
|
||||||
|
@ -11,13 +10,14 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- features
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- features
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
@ -86,6 +86,8 @@ jobs:
|
||||||
python: "3.7"
|
python: "3.7"
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
tox_env: "py37-freeze"
|
tox_env: "py37-freeze"
|
||||||
|
# coverage does not apply for freeze test, skip it
|
||||||
|
skip_coverage: true
|
||||||
- name: "ubuntu-py38"
|
- name: "ubuntu-py38"
|
||||||
python: "3.8"
|
python: "3.8"
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
|
@ -94,6 +96,8 @@ jobs:
|
||||||
python: "pypy3"
|
python: "pypy3"
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
tox_env: "pypy3-xdist"
|
tox_env: "pypy3-xdist"
|
||||||
|
# coverage too slow with pypy3, skip it
|
||||||
|
skip_coverage: true
|
||||||
|
|
||||||
- name: "macos-py37"
|
- name: "macos-py37"
|
||||||
python: "3.7"
|
python: "3.7"
|
||||||
|
@ -118,6 +122,37 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install tox
|
pip install tox coverage
|
||||||
- name: Test
|
|
||||||
run: tox -e ${{ matrix.tox_env }}
|
- name: Test without coverage
|
||||||
|
if: "matrix.skip_coverage"
|
||||||
|
run: "tox -e ${{ matrix.tox_env }}"
|
||||||
|
|
||||||
|
- name: Test with coverage
|
||||||
|
if: "! matrix.skip_coverage"
|
||||||
|
env:
|
||||||
|
_PYTEST_TOX_COVERAGE_RUN: "coverage run -m"
|
||||||
|
COVERAGE_PROCESS_START: ".coveragerc"
|
||||||
|
_PYTEST_TOX_EXTRA_DEP: "coverage-enable-subprocess"
|
||||||
|
run: "tox -e ${{ matrix.tox_env }}"
|
||||||
|
|
||||||
|
- name: Prepare coverage token
|
||||||
|
if: success() && !matrix.skip_coverage && ( github.repository == 'pytest-dev/pytest' || github.event_name == 'pull_request' )
|
||||||
|
run: |
|
||||||
|
python scripts/append_codecov_token.py
|
||||||
|
|
||||||
|
- name: Combine coverage
|
||||||
|
if: success() && !matrix.skip_coverage
|
||||||
|
run: |
|
||||||
|
python -m coverage combine
|
||||||
|
python -m coverage xml
|
||||||
|
|
||||||
|
- name: Codecov upload
|
||||||
|
if: success() && !matrix.skip_coverage
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.codecov }}
|
||||||
|
file: ./coverage.xml
|
||||||
|
flags: ${{ runner.os }}
|
||||||
|
fail_ci_if_error: false
|
||||||
|
name: ${{ matrix.name }}
|
||||||
|
|
|
@ -52,8 +52,10 @@ jobs:
|
||||||
- env: TOXENV=pypy3-xdist
|
- env: TOXENV=pypy3-xdist
|
||||||
python: 'pypy3'
|
python: 'pypy3'
|
||||||
|
|
||||||
- env: TOXENV=py35-xdist
|
# Coverage for Python 3.5.{0,1} specific code, mostly typing related.
|
||||||
python: '3.5'
|
- env: TOXENV=py35 PYTEST_COVERAGE=1 PYTEST_ADDOPTS="-k test_raises_cyclic_reference"
|
||||||
|
python: '3.5.1'
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
# Specialized factors for py37.
|
# Specialized factors for py37.
|
||||||
- env: TOXENV=py37-pluggymaster-xdist
|
- env: TOXENV=py37-pluggymaster-xdist
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
"""
|
||||||
|
Appends the codecov token to the 'codecov.yml' file at the root of the repository.
|
||||||
|
|
||||||
|
This is done by CI during PRs and builds on the pytest-dev repository so we can upload coverage, at least
|
||||||
|
until codecov grows some native integration like it has with Travis and AppVeyor.
|
||||||
|
|
||||||
|
See discussion in https://github.com/pytest-dev/pytest/pull/6441 for more information.
|
||||||
|
"""
|
||||||
|
import os.path
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
this_dir = os.path.dirname(__file__)
|
||||||
|
cov_file = os.path.join(this_dir, "..", "codecov.yml")
|
||||||
|
|
||||||
|
assert os.path.isfile(cov_file), "{cov_file} does not exist".format(
|
||||||
|
cov_file=cov_file
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(cov_file, "a") as f:
|
||||||
|
# token from: https://codecov.io/gh/pytest-dev/pytest/settings
|
||||||
|
# use same URL to regenerate it if needed
|
||||||
|
text = dedent(
|
||||||
|
"""
|
||||||
|
codecov:
|
||||||
|
token: "1eca3b1f-31a2-4fb8-a8c3-138b441b50a7"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
f.write(text)
|
||||||
|
|
||||||
|
print("Token updated:", cov_file)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -382,7 +382,7 @@ class CaptureAndPassthroughIO(CaptureIO):
|
||||||
return self._other.write(s)
|
return self._other.write(s)
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info < (3, 5, 2): # pragma: no cover
|
if sys.version_info < (3, 5, 2):
|
||||||
|
|
||||||
def overload(f): # noqa: F811
|
def overload(f): # noqa: F811
|
||||||
return f
|
return f
|
||||||
|
|
|
@ -108,7 +108,7 @@ CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supporte
|
||||||
|
|
||||||
|
|
||||||
def determine_setup(
|
def determine_setup(
|
||||||
inifile: str,
|
inifile: Optional[str],
|
||||||
args: List[str],
|
args: List[str],
|
||||||
rootdir_cmd_arg: Optional[str] = None,
|
rootdir_cmd_arg: Optional[str] = None,
|
||||||
config: Optional["Config"] = None,
|
config: Optional["Config"] = None,
|
||||||
|
|
|
@ -166,7 +166,7 @@ class TestRaises:
|
||||||
# Early versions of Python 3.5 have some bug causing the
|
# Early versions of Python 3.5 have some bug causing the
|
||||||
# __call__ frame to still refer to t even after everything
|
# __call__ frame to still refer to t even after everything
|
||||||
# is done. This makes the test pass for them.
|
# is done. This makes the test pass for them.
|
||||||
if sys.version_info < (3, 5, 2): # pragma: no cover
|
if sys.version_info < (3, 5, 2):
|
||||||
del self
|
del self
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
|
|
@ -859,7 +859,7 @@ class TestRootdir:
|
||||||
assert get_common_ancestor([no_path.join("a")]) == tmpdir
|
assert get_common_ancestor([no_path.join("a")]) == tmpdir
|
||||||
|
|
||||||
@pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
|
@pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
|
||||||
def test_with_ini(self, tmpdir, name):
|
def test_with_ini(self, tmpdir, name) -> None:
|
||||||
inifile = tmpdir.join(name)
|
inifile = tmpdir.join(name)
|
||||||
inifile.write("[pytest]\n" if name != "setup.cfg" else "[tool:pytest]\n")
|
inifile.write("[pytest]\n" if name != "setup.cfg" else "[tool:pytest]\n")
|
||||||
|
|
||||||
|
@ -874,7 +874,7 @@ class TestRootdir:
|
||||||
assert inifile == inifile
|
assert inifile == inifile
|
||||||
|
|
||||||
@pytest.mark.parametrize("name", "setup.cfg tox.ini".split())
|
@pytest.mark.parametrize("name", "setup.cfg tox.ini".split())
|
||||||
def test_pytestini_overrides_empty_other(self, tmpdir, name):
|
def test_pytestini_overrides_empty_other(self, tmpdir, name) -> None:
|
||||||
inifile = tmpdir.ensure("pytest.ini")
|
inifile = tmpdir.ensure("pytest.ini")
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
a.ensure(name)
|
a.ensure(name)
|
||||||
|
@ -882,7 +882,7 @@ class TestRootdir:
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile == inifile
|
assert inifile == inifile
|
||||||
|
|
||||||
def test_setuppy_fallback(self, tmpdir):
|
def test_setuppy_fallback(self, tmpdir) -> None:
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
a.ensure("setup.cfg")
|
a.ensure("setup.cfg")
|
||||||
tmpdir.ensure("setup.py")
|
tmpdir.ensure("setup.py")
|
||||||
|
@ -891,14 +891,14 @@ class TestRootdir:
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
assert inicfg == {}
|
assert inicfg == {}
|
||||||
|
|
||||||
def test_nothing(self, tmpdir, monkeypatch):
|
def test_nothing(self, tmpdir, monkeypatch) -> None:
|
||||||
monkeypatch.chdir(str(tmpdir))
|
monkeypatch.chdir(str(tmpdir))
|
||||||
rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
|
rootdir, inifile, inicfg = determine_setup(None, [tmpdir])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
assert inicfg == {}
|
assert inicfg == {}
|
||||||
|
|
||||||
def test_with_specific_inifile(self, tmpdir):
|
def test_with_specific_inifile(self, tmpdir) -> None:
|
||||||
inifile = tmpdir.ensure("pytest.ini")
|
inifile = tmpdir.ensure("pytest.ini")
|
||||||
rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
|
rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
|
@ -1039,7 +1039,7 @@ class TestOverrideIniArgs:
|
||||||
result = testdir.runpytest("--override-ini", "python_files=unittest_*.py")
|
result = testdir.runpytest("--override-ini", "python_files=unittest_*.py")
|
||||||
result.stdout.fnmatch_lines(["*1 passed in*"])
|
result.stdout.fnmatch_lines(["*1 passed in*"])
|
||||||
|
|
||||||
def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch):
|
def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch) -> None:
|
||||||
monkeypatch.chdir(str(tmpdir))
|
monkeypatch.chdir(str(tmpdir))
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
b = tmpdir.mkdir("b")
|
b = tmpdir.mkdir("b")
|
||||||
|
@ -1047,7 +1047,7 @@ class TestOverrideIniArgs:
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
|
|
||||||
def test_with_arg_outside_cwd_with_inifile(self, tmpdir):
|
def test_with_arg_outside_cwd_with_inifile(self, tmpdir) -> None:
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
b = tmpdir.mkdir("b")
|
b = tmpdir.mkdir("b")
|
||||||
inifile = a.ensure("pytest.ini")
|
inifile = a.ensure("pytest.ini")
|
||||||
|
@ -1056,13 +1056,13 @@ class TestOverrideIniArgs:
|
||||||
assert inifile == parsed_inifile
|
assert inifile == parsed_inifile
|
||||||
|
|
||||||
@pytest.mark.parametrize("dirs", ([], ["does-not-exist"], ["a/does-not-exist"]))
|
@pytest.mark.parametrize("dirs", ([], ["does-not-exist"], ["a/does-not-exist"]))
|
||||||
def test_with_non_dir_arg(self, dirs, tmpdir):
|
def test_with_non_dir_arg(self, dirs, tmpdir) -> None:
|
||||||
with tmpdir.ensure(dir=True).as_cwd():
|
with tmpdir.ensure(dir=True).as_cwd():
|
||||||
rootdir, inifile, inicfg = determine_setup(None, dirs)
|
rootdir, inifile, inicfg = determine_setup(None, dirs)
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
|
|
||||||
def test_with_existing_file_in_subdir(self, tmpdir):
|
def test_with_existing_file_in_subdir(self, tmpdir) -> None:
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
a.ensure("exist")
|
a.ensure("exist")
|
||||||
with tmpdir.as_cwd():
|
with tmpdir.as_cwd():
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest._code.code import ExceptionChainRepr
|
from _pytest._code.code import ExceptionChainRepr
|
||||||
from _pytest.pathlib import Path
|
from _pytest.pathlib import Path
|
||||||
|
@ -314,15 +316,39 @@ class TestReportSerialization:
|
||||||
# elsewhere and we do check the contents of the longrepr object after loading it.
|
# elsewhere and we do check the contents of the longrepr object after loading it.
|
||||||
loaded_report.longrepr.toterminal(tw_mock)
|
loaded_report.longrepr.toterminal(tw_mock)
|
||||||
|
|
||||||
def test_chained_exceptions_no_reprcrash(
|
def test_chained_exceptions_no_reprcrash(self, testdir, tw_mock):
|
||||||
self, testdir, tw_mock,
|
|
||||||
):
|
|
||||||
"""Regression test for tracebacks without a reprcrash (#5971)
|
"""Regression test for tracebacks without a reprcrash (#5971)
|
||||||
|
|
||||||
This happens notably on exceptions raised by multiprocess.pool: the exception transfer
|
This happens notably on exceptions raised by multiprocess.pool: the exception transfer
|
||||||
from subprocess to main process creates an artificial exception, which ExceptionInfo
|
from subprocess to main process creates an artificial exception, which ExceptionInfo
|
||||||
can't obtain the ReprFileLocation from.
|
can't obtain the ReprFileLocation from.
|
||||||
"""
|
"""
|
||||||
|
# somehow in Python 3.5 on Windows this test fails with:
|
||||||
|
# File "c:\...\3.5.4\x64\Lib\multiprocessing\connection.py", line 302, in _recv_bytes
|
||||||
|
# overlapped=True)
|
||||||
|
# OSError: [WinError 6] The handle is invalid
|
||||||
|
#
|
||||||
|
# so in this platform we opted to use a mock traceback which is identical to the
|
||||||
|
# one produced by the multiprocessing module
|
||||||
|
if sys.version_info[:2] <= (3, 5) and sys.platform.startswith("win"):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
# equivalent of multiprocessing.pool.RemoteTraceback
|
||||||
|
class RemoteTraceback(Exception):
|
||||||
|
def __init__(self, tb):
|
||||||
|
self.tb = tb
|
||||||
|
def __str__(self):
|
||||||
|
return self.tb
|
||||||
|
def test_a():
|
||||||
|
try:
|
||||||
|
raise ValueError('value error')
|
||||||
|
except ValueError as e:
|
||||||
|
# equivalent to how multiprocessing.pool.rebuild_exc does it
|
||||||
|
e.__cause__ = RemoteTraceback('runtime error')
|
||||||
|
raise e
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
else:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
from concurrent.futures import ProcessPoolExecutor
|
from concurrent.futures import ProcessPoolExecutor
|
||||||
|
@ -335,6 +361,7 @@ class TestReportSerialization:
|
||||||
p.submit(func).result()
|
p.submit(func).result()
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
reprec = testdir.inline_run()
|
reprec = testdir.inline_run()
|
||||||
|
|
||||||
reports = reprec.getreports("pytest_runtest_logreport")
|
reports = reprec.getreports("pytest_runtest_logreport")
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -26,7 +26,7 @@ passenv = USER USERNAME COVERAGE_* TRAVIS PYTEST_ADDOPTS TERM
|
||||||
setenv =
|
setenv =
|
||||||
_PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:}
|
_PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:}
|
||||||
|
|
||||||
# Configuration to run with coverage similar to Travis/Appveyor, e.g.
|
# Configuration to run with coverage similar to CI, e.g.
|
||||||
# "tox -e py37-coverage".
|
# "tox -e py37-coverage".
|
||||||
coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m
|
coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m
|
||||||
coverage: _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
coverage: _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
||||||
|
@ -53,7 +53,7 @@ deps =
|
||||||
skip_install = True
|
skip_install = True
|
||||||
basepython = python3
|
basepython = python3
|
||||||
deps = pre-commit>=1.11.0
|
deps = pre-commit>=1.11.0
|
||||||
commands = pre-commit run --all-files --show-diff-on-failure
|
commands = pre-commit run --all-files --show-diff-on-failure {posargs:}
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
|
|
Loading…
Reference in New Issue