Merge remote-tracking branch 'upstream/master' into merge-master-into-features
This commit is contained in:
commit
9646a1cd7a
|
@ -3,7 +3,7 @@ Thanks for submitting a PR, your contribution is really appreciated!
|
||||||
Here's a quick checklist that should be present in PRs (you can delete this text from the final description, this is
|
Here's a quick checklist that should be present in PRs (you can delete this text from the final description, this is
|
||||||
just a guideline):
|
just a guideline):
|
||||||
|
|
||||||
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](/changelog/README.rst) for details.
|
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details.
|
||||||
- [ ] Target the `master` branch for bug fixes, documentation updates and trivial changes.
|
- [ ] Target the `master` branch for bug fixes, documentation updates and trivial changes.
|
||||||
- [ ] Target the `features` branch for new features and removals/deprecations.
|
- [ ] Target the `features` branch for new features and removals/deprecations.
|
||||||
- [ ] Include documentation when adding new features.
|
- [ ] Include documentation when adding new features.
|
||||||
|
|
20
.travis.yml
20
.travis.yml
|
@ -12,19 +12,15 @@ install:
|
||||||
- pip install --upgrade --pre tox
|
- pip install --upgrade --pre tox
|
||||||
env:
|
env:
|
||||||
matrix:
|
matrix:
|
||||||
# note: please use "tox --listenvs" to populate the build matrix below
|
# Specialized factors for py27.
|
||||||
# please remove the linting env in all cases
|
- TOXENV=py27-pexpect,py27-trial,py27-numpy
|
||||||
- TOXENV=py27-pexpect
|
|
||||||
- TOXENV=py27-xdist
|
|
||||||
- TOXENV=py27-trial
|
|
||||||
- TOXENV=py27-numpy
|
|
||||||
- TOXENV=py27-pluggymaster PYTEST_NO_COVERAGE=1
|
|
||||||
- TOXENV=py36-pexpect
|
|
||||||
- TOXENV=py36-xdist
|
|
||||||
- TOXENV=py36-trial
|
|
||||||
- TOXENV=py36-numpy
|
|
||||||
- TOXENV=py36-pluggymaster PYTEST_NO_COVERAGE=1
|
|
||||||
- TOXENV=py27-nobyte
|
- TOXENV=py27-nobyte
|
||||||
|
- TOXENV=py27-xdist
|
||||||
|
- TOXENV=py27-pluggymaster PYTEST_NO_COVERAGE=1
|
||||||
|
# Specialized factors for py36.
|
||||||
|
- TOXENV=py36-pexpect,py36-trial,py36-numpy
|
||||||
|
- TOXENV=py36-xdist
|
||||||
|
- TOXENV=py36-pluggymaster PYTEST_NO_COVERAGE=1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
|
|
1
AUTHORS
1
AUTHORS
|
@ -211,6 +211,7 @@ Thomas Hisch
|
||||||
Tim Strazny
|
Tim Strazny
|
||||||
Tom Dalton
|
Tom Dalton
|
||||||
Tom Viner
|
Tom Viner
|
||||||
|
Tomer Keren
|
||||||
Trevor Bekolay
|
Trevor Bekolay
|
||||||
Tyler Goodlet
|
Tyler Goodlet
|
||||||
Tzu-ping Chung
|
Tzu-ping Chung
|
||||||
|
|
|
@ -280,6 +280,47 @@ Here is a simple overview, with pytest-specific bits:
|
||||||
base: features # if it's a feature
|
base: features # if it's a feature
|
||||||
|
|
||||||
|
|
||||||
|
Writing Tests
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Writing tests for plugins or for pytest itself is often done using the `testdir fixture <https://docs.pytest.org/en/latest/reference.html#testdir>`_, as a "black-box" test.
|
||||||
|
|
||||||
|
For example, to ensure a simple test passes you can write:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def test_true_assertion(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
def test_foo():
|
||||||
|
assert True
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.assert_outcomes(failed=0, passed=1)
|
||||||
|
|
||||||
|
|
||||||
|
Alternatively, it is possible to make checks based on the actual output of the termal using
|
||||||
|
*glob-like* expressions:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def test_true_assertion(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
def test_foo():
|
||||||
|
assert False
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(["*assert False*", "*1 failed*"])
|
||||||
|
|
||||||
|
When choosing a file where to write a new test, take a look at the existing files and see if there's
|
||||||
|
one file which looks like a good fit. For example, a regression test about a bug in the ``--lf`` option
|
||||||
|
should go into ``test_cacheprovider.py``, given that this option is implemented in ``cacheprovider.py``.
|
||||||
|
If in doubt, go ahead and open a PR with your best guess and we can discuss this over the code.
|
||||||
|
|
||||||
|
|
||||||
Joining the Development Team
|
Joining the Development Team
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|
26
appveyor.yml
26
appveyor.yml
|
@ -1,27 +1,29 @@
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- TOXENV: "linting,docs,doctesting"
|
|
||||||
PYTEST_NO_COVERAGE: "1"
|
|
||||||
- TOXENV: "py27"
|
- TOXENV: "py27"
|
||||||
- TOXENV: "py34"
|
|
||||||
- TOXENV: "py35"
|
|
||||||
- TOXENV: "py36"
|
|
||||||
- TOXENV: "py37"
|
- TOXENV: "py37"
|
||||||
|
PYTEST_NO_COVERAGE: "1"
|
||||||
|
- TOXENV: "linting,docs,doctesting"
|
||||||
|
- TOXENV: "py36"
|
||||||
|
- TOXENV: "py35"
|
||||||
|
- TOXENV: "py34"
|
||||||
- TOXENV: "pypy"
|
- TOXENV: "pypy"
|
||||||
PYTEST_NO_COVERAGE: "1"
|
PYTEST_NO_COVERAGE: "1"
|
||||||
- TOXENV: "py27-xdist"
|
# Specialized factors for py27.
|
||||||
- TOXENV: "py27-trial"
|
- TOXENV: "py27-trial,py27-numpy,py27-nobyte"
|
||||||
- TOXENV: "py27-numpy"
|
|
||||||
- TOXENV: "py27-pluggymaster"
|
- TOXENV: "py27-pluggymaster"
|
||||||
PYTEST_NO_COVERAGE: "1"
|
PYTEST_NO_COVERAGE: "1"
|
||||||
- TOXENV: "py36-xdist"
|
- TOXENV: "py27-xdist"
|
||||||
- TOXENV: "py36-trial"
|
# Specialized factors for py36.
|
||||||
- TOXENV: "py36-numpy"
|
- TOXENV: "py36-trial,py36-numpy"
|
||||||
- TOXENV: "py36-pluggymaster"
|
- TOXENV: "py36-pluggymaster"
|
||||||
PYTEST_NO_COVERAGE: "1"
|
PYTEST_NO_COVERAGE: "1"
|
||||||
- TOXENV: "py27-nobyte"
|
|
||||||
- TOXENV: "py36-freeze"
|
- TOXENV: "py36-freeze"
|
||||||
PYTEST_NO_COVERAGE: "1"
|
PYTEST_NO_COVERAGE: "1"
|
||||||
|
- TOXENV: "py36-xdist"
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- echo Installed Pythons
|
- echo Installed Pythons
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Exclude 0.00 second entries from ``--duration`` output unless ``-vv`` is passed on the command-line.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix duplicate printing of internal errors when using ``--pdb``.
|
|
@ -0,0 +1 @@
|
||||||
|
Add tempir testing example to CONTRIBUTING.rst guide
|
|
@ -0,0 +1 @@
|
||||||
|
Display the filename when encountering ``SyntaxWarning``.
|
|
@ -304,7 +304,7 @@ This form of test function doesn't support fixtures properly, and users should s
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.mark.parametrize("x, y", [(2, 4), (3, 9)])
|
@pytest.mark.parametrize("x, y", [(2, 4), (3, 9)])
|
||||||
def test_squared():
|
def test_squared(x, y):
|
||||||
assert x ** x == y
|
assert x ** x == y
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -269,6 +269,7 @@ To get a list of the slowest 10 test durations::
|
||||||
|
|
||||||
pytest --durations=10
|
pytest --durations=10
|
||||||
|
|
||||||
|
By default, pytest will not show test durations that are too small (<0.01s) unless ``-vv`` is passed on the command-line.
|
||||||
|
|
||||||
Creating JUnitXML format files
|
Creating JUnitXML format files
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
|
@ -399,7 +399,7 @@ def _rewrite_test(config, fn):
|
||||||
finally:
|
finally:
|
||||||
del state._indecode
|
del state._indecode
|
||||||
try:
|
try:
|
||||||
tree = ast.parse(source)
|
tree = ast.parse(source, filename=fn.strpath)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
# Let this pop up again in the real import.
|
# Let this pop up again in the real import.
|
||||||
state.trace("failed to parse: %r" % (fn,))
|
state.trace("failed to parse: %r" % (fn,))
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
""" interactive debugging with PDB, the Python Debugger. """
|
""" interactive debugging with PDB, the Python Debugger. """
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
import os
|
||||||
import pdb
|
import pdb
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
from doctest import UnexpectedException
|
from doctest import UnexpectedException
|
||||||
|
|
||||||
|
from _pytest import outcomes
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -109,9 +111,6 @@ class PdbInvoke(object):
|
||||||
_enter_pdb(node, call.excinfo, report)
|
_enter_pdb(node, call.excinfo, report)
|
||||||
|
|
||||||
def pytest_internalerror(self, excrepr, excinfo):
|
def pytest_internalerror(self, excrepr, excinfo):
|
||||||
for line in str(excrepr).split("\n"):
|
|
||||||
sys.stderr.write("INTERNALERROR> %s\n" % line)
|
|
||||||
sys.stderr.flush()
|
|
||||||
tb = _postmortem_traceback(excinfo)
|
tb = _postmortem_traceback(excinfo)
|
||||||
post_mortem(tb)
|
post_mortem(tb)
|
||||||
|
|
||||||
|
@ -164,8 +163,9 @@ def _enter_pdb(node, excinfo, rep):
|
||||||
rep.toterminal(tw)
|
rep.toterminal(tw)
|
||||||
tw.sep(">", "entering PDB")
|
tw.sep(">", "entering PDB")
|
||||||
tb = _postmortem_traceback(excinfo)
|
tb = _postmortem_traceback(excinfo)
|
||||||
post_mortem(tb)
|
|
||||||
rep._pdbshown = True
|
rep._pdbshown = True
|
||||||
|
if post_mortem(tb):
|
||||||
|
outcomes.exit("Quitting debugger")
|
||||||
return rep
|
return rep
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,3 +196,4 @@ def post_mortem(t):
|
||||||
p = Pdb()
|
p = Pdb()
|
||||||
p.reset()
|
p.reset()
|
||||||
p.interaction(None, t)
|
p.interaction(None, t)
|
||||||
|
return p.quitting
|
||||||
|
|
|
@ -1020,7 +1020,7 @@ class FixtureFunctionMarker(object):
|
||||||
|
|
||||||
def __call__(self, function):
|
def __call__(self, function):
|
||||||
if isclass(function):
|
if isclass(function):
|
||||||
raise ValueError("class fixtures not supported (may be in the future)")
|
raise ValueError("class fixtures not supported (maybe in the future)")
|
||||||
|
|
||||||
if getattr(function, "_pytestfixturefunction", False):
|
if getattr(function, "_pytestfixturefunction", False):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
@ -1371,8 +1371,7 @@ class FixtureManager(object):
|
||||||
fixturedefs = self._arg2fixturedefs[argname]
|
fixturedefs = self._arg2fixturedefs[argname]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
else:
|
return tuple(self._matchfactories(fixturedefs, nodeid))
|
||||||
return tuple(self._matchfactories(fixturedefs, nodeid))
|
|
||||||
|
|
||||||
def _matchfactories(self, fixturedefs, nodeid):
|
def _matchfactories(self, fixturedefs, nodeid):
|
||||||
for fixturedef in fixturedefs:
|
for fixturedef in fixturedefs:
|
||||||
|
|
|
@ -570,9 +570,7 @@ class Session(nodes.FSCollector):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _tryconvertpyarg(self, x):
|
def _tryconvertpyarg(self, x):
|
||||||
"""Convert a dotted module name to path.
|
"""Convert a dotted module name to path."""
|
||||||
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
with _patched_find_module():
|
with _patched_find_module():
|
||||||
loader = pkgutil.find_loader(x)
|
loader = pkgutil.find_loader(x)
|
||||||
|
@ -604,8 +602,7 @@ class Session(nodes.FSCollector):
|
||||||
raise UsageError(
|
raise UsageError(
|
||||||
"file or package not found: " + arg + " (missing __init__.py?)"
|
"file or package not found: " + arg + " (missing __init__.py?)"
|
||||||
)
|
)
|
||||||
else:
|
raise UsageError("file not found: " + arg)
|
||||||
raise UsageError("file not found: " + arg)
|
|
||||||
parts[0] = path
|
parts[0] = path
|
||||||
return parts
|
return parts
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ from weakref import WeakKeyDictionary
|
||||||
|
|
||||||
from _pytest.capture import MultiCapture, SysCapture
|
from _pytest.capture import MultiCapture, SysCapture
|
||||||
from _pytest._code import Source
|
from _pytest._code import Source
|
||||||
from _pytest.main import Session, EXIT_OK
|
from _pytest.main import Session, EXIT_INTERRUPTED, EXIT_OK
|
||||||
from _pytest.assertion.rewrite import AssertionRewritingHook
|
from _pytest.assertion.rewrite import AssertionRewritingHook
|
||||||
from _pytest.pathlib import Path
|
from _pytest.pathlib import Path
|
||||||
from _pytest.compat import safe_str
|
from _pytest.compat import safe_str
|
||||||
|
@ -857,7 +857,7 @@ class Testdir(object):
|
||||||
|
|
||||||
# typically we reraise keyboard interrupts from the child run
|
# typically we reraise keyboard interrupts from the child run
|
||||||
# because it's our user requesting interruption of the testing
|
# because it's our user requesting interruption of the testing
|
||||||
if ret == 2 and not kwargs.get("no_reraise_ctrlc"):
|
if ret == EXIT_INTERRUPTED and not kwargs.get("no_reraise_ctrlc"):
|
||||||
calls = reprec.getcalls("pytest_keyboard_interrupt")
|
calls = reprec.getcalls("pytest_keyboard_interrupt")
|
||||||
if calls and calls[-1].excinfo.type == KeyboardInterrupt:
|
if calls and calls[-1].excinfo.type == KeyboardInterrupt:
|
||||||
raise KeyboardInterrupt()
|
raise KeyboardInterrupt()
|
||||||
|
|
|
@ -30,6 +30,7 @@ def pytest_addoption(parser):
|
||||||
|
|
||||||
def pytest_terminal_summary(terminalreporter):
|
def pytest_terminal_summary(terminalreporter):
|
||||||
durations = terminalreporter.config.option.durations
|
durations = terminalreporter.config.option.durations
|
||||||
|
verbose = terminalreporter.config.getvalue("verbose")
|
||||||
if durations is None:
|
if durations is None:
|
||||||
return
|
return
|
||||||
tr = terminalreporter
|
tr = terminalreporter
|
||||||
|
@ -49,6 +50,10 @@ def pytest_terminal_summary(terminalreporter):
|
||||||
dlist = dlist[:durations]
|
dlist = dlist[:durations]
|
||||||
|
|
||||||
for rep in dlist:
|
for rep in dlist:
|
||||||
|
if verbose < 2 and rep.duration < 0.005:
|
||||||
|
tr.write_line("")
|
||||||
|
tr.write_line("(0.00 durations hidden. Use -vv to show these durations.)")
|
||||||
|
break
|
||||||
nodeid = rep.nodeid.replace("::()::", "::")
|
nodeid = rep.nodeid.replace("::()::", "::")
|
||||||
tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, nodeid))
|
tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, nodeid))
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,13 @@ import pytest
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
|
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
|
||||||
|
|
||||||
|
|
||||||
|
def prepend_pythonpath(*dirs):
|
||||||
|
cur = os.getenv("PYTHONPATH")
|
||||||
|
if cur:
|
||||||
|
dirs += (cur,)
|
||||||
|
return os.pathsep.join(str(p) for p in dirs)
|
||||||
|
|
||||||
|
|
||||||
class TestGeneralUsage(object):
|
class TestGeneralUsage(object):
|
||||||
def test_config_error(self, testdir):
|
def test_config_error(self, testdir):
|
||||||
testdir.copy_example("conftest_usageerror/conftest.py")
|
testdir.copy_example("conftest_usageerror/conftest.py")
|
||||||
|
@ -590,14 +597,8 @@ class TestInvocationVariants(object):
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
def join_pythonpath(what):
|
|
||||||
cur = os.environ.get("PYTHONPATH")
|
|
||||||
if cur:
|
|
||||||
return str(what) + os.pathsep + cur
|
|
||||||
return what
|
|
||||||
|
|
||||||
empty_package = testdir.mkpydir("empty_package")
|
empty_package = testdir.mkpydir("empty_package")
|
||||||
monkeypatch.setenv("PYTHONPATH", str(join_pythonpath(empty_package)))
|
monkeypatch.setenv("PYTHONPATH", str(empty_package), prepend=os.pathsep)
|
||||||
# the path which is not a package raises a warning on pypy;
|
# the path which is not a package raises a warning on pypy;
|
||||||
# no idea why only pypy and not normal python warn about it here
|
# no idea why only pypy and not normal python warn about it here
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
|
@ -606,7 +607,7 @@ class TestInvocationVariants(object):
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*2 passed*"])
|
result.stdout.fnmatch_lines(["*2 passed*"])
|
||||||
|
|
||||||
monkeypatch.setenv("PYTHONPATH", str(join_pythonpath(testdir)))
|
monkeypatch.setenv("PYTHONPATH", str(testdir), prepend=os.pathsep)
|
||||||
result = testdir.runpytest("--pyargs", "tpkg.test_missing", syspathinsert=True)
|
result = testdir.runpytest("--pyargs", "tpkg.test_missing", syspathinsert=True)
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
result.stderr.fnmatch_lines(["*not*found*test_missing*"])
|
result.stderr.fnmatch_lines(["*not*found*test_missing*"])
|
||||||
|
@ -646,18 +647,13 @@ class TestInvocationVariants(object):
|
||||||
# ├── __init__.py
|
# ├── __init__.py
|
||||||
# └── test_world.py
|
# └── test_world.py
|
||||||
|
|
||||||
def join_pythonpath(*dirs):
|
# NOTE: the different/reversed ordering is intentional here.
|
||||||
cur = os.environ.get("PYTHONPATH")
|
monkeypatch.setenv("PYTHONPATH", prepend_pythonpath(*search_path))
|
||||||
if cur:
|
|
||||||
dirs += (cur,)
|
|
||||||
return os.pathsep.join(str(p) for p in dirs)
|
|
||||||
|
|
||||||
monkeypatch.setenv("PYTHONPATH", join_pythonpath(*search_path))
|
|
||||||
for p in search_path:
|
for p in search_path:
|
||||||
monkeypatch.syspath_prepend(p)
|
monkeypatch.syspath_prepend(p)
|
||||||
|
|
||||||
# mixed module and filenames:
|
# mixed module and filenames:
|
||||||
os.chdir("world")
|
monkeypatch.chdir("world")
|
||||||
result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "ns_pkg/world")
|
result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "ns_pkg/world")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
|
@ -708,8 +704,6 @@ class TestInvocationVariants(object):
|
||||||
pytest.skip(six.text_type(e.args[0]))
|
pytest.skip(six.text_type(e.args[0]))
|
||||||
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
|
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False)
|
||||||
|
|
||||||
search_path = ["lib", os.path.join("local", "lib")]
|
|
||||||
|
|
||||||
dirname = "lib"
|
dirname = "lib"
|
||||||
d = testdir.mkdir(dirname)
|
d = testdir.mkdir(dirname)
|
||||||
foo = d.mkdir("foo")
|
foo = d.mkdir("foo")
|
||||||
|
@ -742,13 +736,9 @@ class TestInvocationVariants(object):
|
||||||
# ├── conftest.py
|
# ├── conftest.py
|
||||||
# └── test_bar.py
|
# └── test_bar.py
|
||||||
|
|
||||||
def join_pythonpath(*dirs):
|
# NOTE: the different/reversed ordering is intentional here.
|
||||||
cur = os.getenv("PYTHONPATH")
|
search_path = ["lib", os.path.join("local", "lib")]
|
||||||
if cur:
|
monkeypatch.setenv("PYTHONPATH", prepend_pythonpath(*search_path))
|
||||||
dirs += (cur,)
|
|
||||||
return os.pathsep.join(str(p) for p in dirs)
|
|
||||||
|
|
||||||
monkeypatch.setenv("PYTHONPATH", join_pythonpath(*search_path))
|
|
||||||
for p in search_path:
|
for p in search_path:
|
||||||
monkeypatch.syspath_prepend(p)
|
monkeypatch.syspath_prepend(p)
|
||||||
|
|
||||||
|
@ -768,8 +758,8 @@ class TestInvocationVariants(object):
|
||||||
else:
|
else:
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"local/lib/foo/bar/test_bar.py::test_bar PASSED*",
|
"*lib/foo/bar/test_bar.py::test_bar PASSED*",
|
||||||
"local/lib/foo/bar/test_bar.py::test_other PASSED*",
|
"*lib/foo/bar/test_bar.py::test_other PASSED*",
|
||||||
"*2 passed*",
|
"*2 passed*",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -846,7 +836,10 @@ class TestDurations(object):
|
||||||
result = testdir.runpytest("--durations=10")
|
result = testdir.runpytest("--durations=10")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines_random(
|
result.stdout.fnmatch_lines_random(
|
||||||
["*durations*", "*call*test_3*", "*call*test_2*", "*call*test_1*"]
|
["*durations*", "*call*test_3*", "*call*test_2*"]
|
||||||
|
)
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
["(0.00 durations hidden. Use -vv to show these durations.)"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_calls_show_2(self, testdir):
|
def test_calls_show_2(self, testdir):
|
||||||
|
@ -860,6 +853,18 @@ class TestDurations(object):
|
||||||
testdir.makepyfile(self.source)
|
testdir.makepyfile(self.source)
|
||||||
result = testdir.runpytest("--durations=0")
|
result = testdir.runpytest("--durations=0")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
for x in "23":
|
||||||
|
for y in ("call",): # 'setup', 'call', 'teardown':
|
||||||
|
for line in result.stdout.lines:
|
||||||
|
if ("test_%s" % x) in line and y in line:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise AssertionError("not found {} {}".format(x, y))
|
||||||
|
|
||||||
|
def test_calls_showall_verbose(self, testdir):
|
||||||
|
testdir.makepyfile(self.source)
|
||||||
|
result = testdir.runpytest("--durations=0", "-vv")
|
||||||
|
assert result.ret == 0
|
||||||
for x in "123":
|
for x in "123":
|
||||||
for y in ("call",): # 'setup', 'call', 'teardown':
|
for y in ("call",): # 'setup', 'call', 'teardown':
|
||||||
for line in result.stdout.lines:
|
for line in result.stdout.lines:
|
||||||
|
@ -870,9 +875,9 @@ class TestDurations(object):
|
||||||
|
|
||||||
def test_with_deselected(self, testdir):
|
def test_with_deselected(self, testdir):
|
||||||
testdir.makepyfile(self.source)
|
testdir.makepyfile(self.source)
|
||||||
result = testdir.runpytest("--durations=2", "-k test_1")
|
result = testdir.runpytest("--durations=2", "-k test_2")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*durations*", "*call*test_1*"])
|
result.stdout.fnmatch_lines(["*durations*", "*call*test_2*"])
|
||||||
|
|
||||||
def test_with_failing_collection(self, testdir):
|
def test_with_failing_collection(self, testdir):
|
||||||
testdir.makepyfile(self.source)
|
testdir.makepyfile(self.source)
|
||||||
|
@ -892,13 +897,15 @@ class TestDurations(object):
|
||||||
|
|
||||||
class TestDurationWithFixture(object):
|
class TestDurationWithFixture(object):
|
||||||
source = """
|
source = """
|
||||||
|
import pytest
|
||||||
import time
|
import time
|
||||||
frag = 0.001
|
frag = 0.01
|
||||||
def setup_function(func):
|
|
||||||
time.sleep(frag * 3)
|
@pytest.fixture
|
||||||
def test_1():
|
def setup_fixt():
|
||||||
time.sleep(frag*2)
|
time.sleep(frag)
|
||||||
def test_2():
|
|
||||||
|
def test_1(setup_fixt):
|
||||||
time.sleep(frag)
|
time.sleep(frag)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -494,6 +494,12 @@ class TestRequestBasic(object):
|
||||||
reason="this method of test doesn't work on pypy",
|
reason="this method of test doesn't work on pypy",
|
||||||
)
|
)
|
||||||
def test_request_garbage(self, testdir):
|
def test_request_garbage(self, testdir):
|
||||||
|
try:
|
||||||
|
import xdist # noqa
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
pytest.xfail("this test is flaky when executed with xdist")
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
|
|
@ -25,6 +25,8 @@ def custom_pdb_calls():
|
||||||
|
|
||||||
# install dummy debugger class and track which methods were called on it
|
# install dummy debugger class and track which methods were called on it
|
||||||
class _CustomPdb(object):
|
class _CustomPdb(object):
|
||||||
|
quitting = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
called.append("init")
|
called.append("init")
|
||||||
|
|
||||||
|
@ -142,6 +144,9 @@ class TestPDB(object):
|
||||||
def test_1():
|
def test_1():
|
||||||
i = 0
|
i = 0
|
||||||
assert i == 1
|
assert i == 1
|
||||||
|
|
||||||
|
def test_not_called_due_to_quit():
|
||||||
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
child = testdir.spawn_pytest("--pdb %s" % p1)
|
child = testdir.spawn_pytest("--pdb %s" % p1)
|
||||||
|
@ -150,8 +155,9 @@ class TestPDB(object):
|
||||||
child.expect("Pdb")
|
child.expect("Pdb")
|
||||||
child.sendeof()
|
child.sendeof()
|
||||||
rest = child.read().decode("utf8")
|
rest = child.read().decode("utf8")
|
||||||
assert "1 failed" in rest
|
assert "= 1 failed in" in rest
|
||||||
assert "def test_1" not in rest
|
assert "def test_1" not in rest
|
||||||
|
assert "Exit: Quitting debugger" in rest
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -321,7 +327,7 @@ class TestPDB(object):
|
||||||
child = testdir.spawn_pytest("--pdb %s" % p1)
|
child = testdir.spawn_pytest("--pdb %s" % p1)
|
||||||
# child.expect(".*import pytest.*")
|
# child.expect(".*import pytest.*")
|
||||||
child.expect("Pdb")
|
child.expect("Pdb")
|
||||||
child.sendeof()
|
child.sendline("c")
|
||||||
child.expect("1 error")
|
child.expect("1 error")
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
|
@ -334,8 +340,20 @@ class TestPDB(object):
|
||||||
)
|
)
|
||||||
p1 = testdir.makepyfile("def test_func(): pass")
|
p1 = testdir.makepyfile("def test_func(): pass")
|
||||||
child = testdir.spawn_pytest("--pdb %s" % p1)
|
child = testdir.spawn_pytest("--pdb %s" % p1)
|
||||||
# child.expect(".*import pytest.*")
|
|
||||||
child.expect("Pdb")
|
child.expect("Pdb")
|
||||||
|
|
||||||
|
# INTERNALERROR is only displayed once via terminal reporter.
|
||||||
|
assert (
|
||||||
|
len(
|
||||||
|
[
|
||||||
|
x
|
||||||
|
for x in child.before.decode().splitlines()
|
||||||
|
if x.startswith("INTERNALERROR> Traceback")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
== 1
|
||||||
|
)
|
||||||
|
|
||||||
child.sendeof()
|
child.sendeof()
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
|
@ -345,7 +363,7 @@ class TestPDB(object):
|
||||||
import pytest
|
import pytest
|
||||||
def test_1():
|
def test_1():
|
||||||
i = 0
|
i = 0
|
||||||
print ("hello17")
|
print("hello17")
|
||||||
pytest.set_trace()
|
pytest.set_trace()
|
||||||
x = 3
|
x = 3
|
||||||
"""
|
"""
|
||||||
|
@ -376,6 +394,7 @@ class TestPDB(object):
|
||||||
rest = child.read().decode("utf8")
|
rest = child.read().decode("utf8")
|
||||||
assert "1 failed" in rest
|
assert "1 failed" in rest
|
||||||
assert "reading from stdin while output" not in rest
|
assert "reading from stdin while output" not in rest
|
||||||
|
assert "BdbQuit" in rest
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
def test_pdb_and_capsys(self, testdir):
|
def test_pdb_and_capsys(self, testdir):
|
||||||
|
@ -383,7 +402,7 @@ class TestPDB(object):
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
def test_1(capsys):
|
def test_1(capsys):
|
||||||
print ("hello1")
|
print("hello1")
|
||||||
pytest.set_trace()
|
pytest.set_trace()
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
@ -420,7 +439,7 @@ class TestPDB(object):
|
||||||
def test_1():
|
def test_1():
|
||||||
pdb.set_trace()
|
pdb.set_trace()
|
||||||
def test_2():
|
def test_2():
|
||||||
print ("hello")
|
print("hello")
|
||||||
assert 0
|
assert 0
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
@ -461,10 +480,10 @@ class TestPDB(object):
|
||||||
import pytest
|
import pytest
|
||||||
def test_1():
|
def test_1():
|
||||||
i = 0
|
i = 0
|
||||||
print ("hello17")
|
print("hello17")
|
||||||
pytest.set_trace()
|
pytest.set_trace()
|
||||||
x = 3
|
x = 3
|
||||||
print ("hello18")
|
print("hello18")
|
||||||
pytest.set_trace()
|
pytest.set_trace()
|
||||||
x = 4
|
x = 4
|
||||||
"""
|
"""
|
||||||
|
@ -518,14 +537,16 @@ class TestPDB(object):
|
||||||
def test_pdb_collection_failure_is_shown(self, testdir):
|
def test_pdb_collection_failure_is_shown(self, testdir):
|
||||||
p1 = testdir.makepyfile("xxx")
|
p1 = testdir.makepyfile("xxx")
|
||||||
result = testdir.runpytest_subprocess("--pdb", p1)
|
result = testdir.runpytest_subprocess("--pdb", p1)
|
||||||
result.stdout.fnmatch_lines(["*NameError*xxx*", "*1 error*"])
|
result.stdout.fnmatch_lines(
|
||||||
|
["E NameError: *xxx*", "*! *Exit: Quitting debugger !*"] # due to EOF
|
||||||
|
)
|
||||||
|
|
||||||
def test_enter_pdb_hook_is_called(self, testdir):
|
def test_enter_pdb_hook_is_called(self, testdir):
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
def pytest_enter_pdb(config):
|
def pytest_enter_pdb(config):
|
||||||
assert config.testing_verification == 'configured'
|
assert config.testing_verification == 'configured'
|
||||||
print 'enter_pdb_hook'
|
print('enter_pdb_hook')
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
config.testing_verification = 'configured'
|
config.testing_verification = 'configured'
|
||||||
|
@ -562,7 +583,7 @@ class TestPDB(object):
|
||||||
custom_pdb="""
|
custom_pdb="""
|
||||||
class CustomPdb(object):
|
class CustomPdb(object):
|
||||||
def set_trace(*args, **kwargs):
|
def set_trace(*args, **kwargs):
|
||||||
print 'custom set_trace>'
|
print('custom set_trace>')
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
p1 = testdir.makepyfile(
|
p1 = testdir.makepyfile(
|
||||||
|
|
27
tox.ini
27
tox.ini
|
@ -18,10 +18,10 @@ envlist =
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof -ra {posargs:testing}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof
|
||||||
coverage: coverage combine
|
coverage: coverage combine
|
||||||
coverage: coverage report
|
coverage: coverage report
|
||||||
passenv = USER USERNAME
|
passenv = USER USERNAME COVERAGE_*
|
||||||
setenv =
|
setenv =
|
||||||
# configuration if a user runs tox with a "coverage" factor, for example "tox -e py36-coverage"
|
# configuration if a user runs tox with a "coverage" factor, for example "tox -e py36-coverage"
|
||||||
coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m
|
coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m
|
||||||
|
@ -36,14 +36,12 @@ deps =
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||||
|
|
||||||
[testenv:py27-subprocess]
|
[testenv:py27-subprocess]
|
||||||
changedir = .
|
|
||||||
deps =
|
deps =
|
||||||
pytest-xdist>=1.13
|
pytest-xdist>=1.13
|
||||||
py27: mock
|
py27: mock
|
||||||
nose
|
nose
|
||||||
passenv = USER USERNAME TRAVIS
|
|
||||||
commands =
|
commands =
|
||||||
pytest -n auto -ra --runpytest=subprocess {posargs:testing}
|
pytest -n auto --runpytest=subprocess
|
||||||
|
|
||||||
|
|
||||||
[testenv:linting]
|
[testenv:linting]
|
||||||
|
@ -59,9 +57,8 @@ deps =
|
||||||
nose
|
nose
|
||||||
hypothesis>=3.56
|
hypothesis>=3.56
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||||
passenv = USER USERNAME TRAVIS
|
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:testing}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto
|
||||||
|
|
||||||
[testenv:py36-xdist]
|
[testenv:py36-xdist]
|
||||||
# NOTE: copied from above due to https://github.com/tox-dev/tox/issues/706.
|
# NOTE: copied from above due to https://github.com/tox-dev/tox/issues/706.
|
||||||
|
@ -74,16 +71,14 @@ deps =
|
||||||
commands = {[testenv:py27-xdist]commands}
|
commands = {[testenv:py27-xdist]commands}
|
||||||
|
|
||||||
[testenv:py27-pexpect]
|
[testenv:py27-pexpect]
|
||||||
changedir = testing
|
|
||||||
platform = linux|darwin
|
platform = linux|darwin
|
||||||
deps =
|
deps =
|
||||||
pexpect
|
pexpect
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra test_pdb.py test_terminal.py test_unittest.py
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest testing/test_pdb.py testing/test_terminal.py testing/test_unittest.py {posargs}
|
||||||
|
|
||||||
[testenv:py36-pexpect]
|
[testenv:py36-pexpect]
|
||||||
changedir = {[testenv:py27-pexpect]changedir}
|
|
||||||
platform = {[testenv:py27-pexpect]platform}
|
platform = {[testenv:py27-pexpect]platform}
|
||||||
deps = {[testenv:py27-pexpect]deps}
|
deps = {[testenv:py27-pexpect]deps}
|
||||||
commands = {[testenv:py27-pexpect]commands}
|
commands = {[testenv:py27-pexpect]commands}
|
||||||
|
@ -95,20 +90,18 @@ deps =
|
||||||
py27: mock
|
py27: mock
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||||
distribute = true
|
distribute = true
|
||||||
changedir=testing
|
|
||||||
setenv =
|
setenv =
|
||||||
{[testenv]setenv}
|
{[testenv]setenv}
|
||||||
PYTHONDONTWRITEBYTECODE=1
|
PYTHONDONTWRITEBYTECODE=1
|
||||||
passenv = USER USERNAME TRAVIS
|
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:.}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto {posargs}
|
||||||
|
|
||||||
[testenv:py27-trial]
|
[testenv:py27-trial]
|
||||||
deps =
|
deps =
|
||||||
twisted
|
twisted
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra {posargs:testing/test_unittest.py}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/test_unittest.py}
|
||||||
|
|
||||||
[testenv:py36-trial]
|
[testenv:py36-trial]
|
||||||
deps = {[testenv:py27-trial]deps}
|
deps = {[testenv:py27-trial]deps}
|
||||||
|
@ -119,7 +112,7 @@ deps =
|
||||||
numpy
|
numpy
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||||
commands=
|
commands=
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra {posargs:testing/python/approx.py}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/python/approx.py}
|
||||||
|
|
||||||
[testenv:py36-numpy]
|
[testenv:py36-numpy]
|
||||||
deps = {[testenv:py27-numpy]deps}
|
deps = {[testenv:py27-numpy]deps}
|
||||||
|
@ -154,7 +147,7 @@ deps =
|
||||||
PyYAML
|
PyYAML
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -ra doc/en
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest doc/en
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules --pyargs _pytest
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules --pyargs _pytest
|
||||||
|
|
||||||
[testenv:regen]
|
[testenv:regen]
|
||||||
|
@ -175,7 +168,7 @@ commands =
|
||||||
[testenv:jython]
|
[testenv:jython]
|
||||||
changedir = testing
|
changedir = testing
|
||||||
commands =
|
commands =
|
||||||
{envpython} {envbindir}/py.test-jython -ra {posargs}
|
{envpython} {envbindir}/py.test-jython {posargs}
|
||||||
|
|
||||||
[testenv:py36-freeze]
|
[testenv:py36-freeze]
|
||||||
changedir = testing/freeze
|
changedir = testing/freeze
|
||||||
|
|
Loading…
Reference in New Issue