Merge pull request #4785 from blueyed/merge-master-into-features
Merge master into features
This commit is contained in:
commit
1dbf440194
|
@ -45,3 +45,6 @@ coverage.xml
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
|
# generated by pip
|
||||||
|
pip-wheel-metadata/
|
||||||
|
|
|
@ -17,10 +17,10 @@ env:
|
||||||
# Specialized factors for py27.
|
# Specialized factors for py27.
|
||||||
- TOXENV=py27-nobyte
|
- TOXENV=py27-nobyte
|
||||||
- TOXENV=py27-xdist
|
- TOXENV=py27-xdist
|
||||||
- TOXENV=py27-pluggymaster PYTEST_NO_COVERAGE=1
|
- TOXENV=py27-pluggymaster
|
||||||
# Specialized factors for py37.
|
# Specialized factors for py37.
|
||||||
- TOXENV=py37-pexpect,py37-trial,py37-numpy
|
- TOXENV=py37-pexpect,py37-trial,py37-numpy
|
||||||
- TOXENV=py37-pluggymaster PYTEST_NO_COVERAGE=1
|
- TOXENV=py37-pluggymaster
|
||||||
- TOXENV=py37-freeze PYTEST_NO_COVERAGE=1
|
- TOXENV=py37-freeze PYTEST_NO_COVERAGE=1
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
|
|
@ -18,6 +18,62 @@ with advance notice in the **Deprecations** section of releases.
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
pytest 4.2.1 (2019-02-12)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#2895 <https://github.com/pytest-dev/pytest/issues/2895>`_: The ``pytest_report_collectionfinish`` hook now is also called with ``--collect-only``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#3899 <https://github.com/pytest-dev/pytest/issues/3899>`_: Do not raise ``UsageError`` when an imported package has a ``pytest_plugins.py`` child module.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4347 <https://github.com/pytest-dev/pytest/issues/4347>`_: Fix output capturing when using pdb++ with recursive debugging.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4592 <https://github.com/pytest-dev/pytest/issues/4592>`_: Fix handling of ``collect_ignore`` via parent ``conftest.py``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4700 <https://github.com/pytest-dev/pytest/issues/4700>`_: Fix regression where ``setUpClass`` would always be called in subclasses even if all tests
|
||||||
|
were skipped by a ``unittest.skip()`` decorator applied in the subclass.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4739 <https://github.com/pytest-dev/pytest/issues/4739>`_: Fix ``parametrize(... ids=<function>)`` when the function returns non-strings.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4745 <https://github.com/pytest-dev/pytest/issues/4745>`_: Fix/improve collection of args when passing in ``__init__.py`` and a test file.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4770 <https://github.com/pytest-dev/pytest/issues/4770>`_: ``more_itertools`` is now constrained to <6.0.0 when required for Python 2.7 compatibility.
|
||||||
|
|
||||||
|
|
||||||
|
- `#526 <https://github.com/pytest-dev/pytest/issues/526>`_: Fix "ValueError: Plugin already registered" exceptions when running in build directories that symlink to actual source.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Improved Documentation
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- `#3899 <https://github.com/pytest-dev/pytest/issues/3899>`_: Add note to ``plugins.rst`` that ``pytest_plugins`` should not be used as a name for a user module containing plugins.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4324 <https://github.com/pytest-dev/pytest/issues/4324>`_: Document how to use ``raises`` and ``does_not_raise`` to write parametrized tests with conditional raises.
|
||||||
|
|
||||||
|
|
||||||
|
- `#4709 <https://github.com/pytest-dev/pytest/issues/4709>`_: Document how to customize test failure messages when using
|
||||||
|
``pytest.warns``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Trivial/Internal Changes
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
- `#4741 <https://github.com/pytest-dev/pytest/issues/4741>`_: Some verbosity related attributes of the TerminalReporter plugin are now
|
||||||
|
read only properties.
|
||||||
|
|
||||||
|
|
||||||
pytest 4.2.0 (2019-01-30)
|
pytest 4.2.0 (2019-01-30)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,9 @@ environment:
|
||||||
# Specialized factors for py27.
|
# Specialized factors for py27.
|
||||||
- TOXENV: "py27-trial,py27-numpy,py27-nobyte"
|
- TOXENV: "py27-trial,py27-numpy,py27-nobyte"
|
||||||
- TOXENV: "py27-pluggymaster"
|
- TOXENV: "py27-pluggymaster"
|
||||||
PYTEST_NO_COVERAGE: "1"
|
|
||||||
# Specialized factors for py37.
|
# Specialized factors for py37.
|
||||||
- TOXENV: "py37-trial,py37-numpy"
|
- TOXENV: "py37-trial,py37-numpy"
|
||||||
- TOXENV: "py37-pluggymaster"
|
- TOXENV: "py37-pluggymaster"
|
||||||
PYTEST_NO_COVERAGE: "1"
|
|
||||||
- TOXENV: "py37-freeze"
|
- TOXENV: "py37-freeze"
|
||||||
PYTEST_NO_COVERAGE: "1"
|
PYTEST_NO_COVERAGE: "1"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
trigger:
|
||||||
|
- master
|
||||||
|
- features
|
||||||
|
|
||||||
|
variables:
|
||||||
|
PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
- job: 'Test'
|
||||||
|
pool:
|
||||||
|
vmImage: "vs2017-win2016"
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
py27:
|
||||||
|
python.version: '2.7'
|
||||||
|
tox.env: 'py27'
|
||||||
|
py35:
|
||||||
|
python.version: '3.5'
|
||||||
|
tox.env: 'py35'
|
||||||
|
py36:
|
||||||
|
python.version: '3.6'
|
||||||
|
tox.env: 'py36'
|
||||||
|
py37:
|
||||||
|
python.version: '3.7'
|
||||||
|
tox.env: 'py37'
|
||||||
|
maxParallel: 4
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- task: UsePythonVersion@0
|
||||||
|
inputs:
|
||||||
|
versionSpec: '$(python.version)'
|
||||||
|
architecture: 'x64'
|
||||||
|
|
||||||
|
- script: python -m pip install --upgrade pip && pip install tox
|
||||||
|
displayName: 'Install tox'
|
||||||
|
|
||||||
|
- script: python -m tox -e $(tox.env)
|
||||||
|
displayName: 'Run tests'
|
||||||
|
|
||||||
|
- task: PublishTestResults@2
|
||||||
|
inputs:
|
||||||
|
testResultsFiles: 'build/test-results/$(tox.env).xml'
|
||||||
|
testRunTitle: '$(tox.env)'
|
||||||
|
condition: succeededOrFailed()
|
|
@ -1 +0,0 @@
|
||||||
The ``pytest_report_collectionfinish`` hook now is also called with ``--collect-only``.
|
|
|
@ -1 +0,0 @@
|
||||||
Do not raise ``UsageError`` when an imported package has a ``pytest_plugins.py`` child module.
|
|
|
@ -1 +0,0 @@
|
||||||
Add note to ``plugins.rst`` that ``pytest_plugins`` should not be used as a name for a user module containing plugins.
|
|
|
@ -1 +0,0 @@
|
||||||
Document how to use ``raises`` and ``does_not_raise`` to write parametrized tests with conditional raises.
|
|
|
@ -1 +0,0 @@
|
||||||
Fix handling of ``collect_ignore`` via parent ``conftest.py``.
|
|
|
@ -1,2 +0,0 @@
|
||||||
Fix regression where ``setUpClass`` would always be called in subclasses even if all tests
|
|
||||||
were skipped by a ``unittest.skip()`` decorator applied in the subclass.
|
|
|
@ -1,2 +0,0 @@
|
||||||
Document how to customize test failure messages when using
|
|
||||||
``pytest.warns``.
|
|
|
@ -1 +0,0 @@
|
||||||
Fix ``parametrize(... ids=<function>)`` when the function returns non-strings.
|
|
|
@ -6,6 +6,7 @@ Release announcements
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
|
||||||
|
release-4.2.1
|
||||||
release-4.2.0
|
release-4.2.0
|
||||||
release-4.1.1
|
release-4.1.1
|
||||||
release-4.1.0
|
release-4.1.0
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
pytest-4.2.1
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 4.2.1 has just been released to PyPI.
|
||||||
|
|
||||||
|
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||||
|
|
||||||
|
pip install --upgrade pytest
|
||||||
|
|
||||||
|
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||||
|
|
||||||
|
Thanks to all who contributed to this release, among them:
|
||||||
|
|
||||||
|
* Anthony Sottile
|
||||||
|
* Arel Cordero
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Daniel Hahler
|
||||||
|
* Holger Kohr
|
||||||
|
* Kevin J. Foley
|
||||||
|
* Nick Murphy
|
||||||
|
* Paweł Stradomski
|
||||||
|
* Raphael Pierzina
|
||||||
|
* Ronny Pfannschmidt
|
||||||
|
* Sam Brightman
|
||||||
|
* Thomas Hisch
|
||||||
|
* Zac Hatfield-Dodds
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -205,8 +205,8 @@ Special comparisons are done for a number of cases:
|
||||||
|
|
||||||
See the :ref:`reporting demo <tbreportdemo>` for many more examples.
|
See the :ref:`reporting demo <tbreportdemo>` for many more examples.
|
||||||
|
|
||||||
Defining your own assertion comparison
|
Defining your own explanation for failed assertions
|
||||||
----------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
It is possible to add your own detailed explanations by implementing
|
It is possible to add your own detailed explanations by implementing
|
||||||
the ``pytest_assertrepr_compare`` hook.
|
the ``pytest_assertrepr_compare`` hook.
|
||||||
|
|
|
@ -1042,6 +1042,20 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||||
This tells pytest to ignore deprecation warnings and turn all other warnings
|
This tells pytest to ignore deprecation warnings and turn all other warnings
|
||||||
into errors. For more information please refer to :ref:`warnings`.
|
into errors. For more information please refer to :ref:`warnings`.
|
||||||
|
|
||||||
|
.. confval:: junit_family
|
||||||
|
|
||||||
|
.. versionadded:: 4.2
|
||||||
|
|
||||||
|
Configures the format of the generated JUnit XML file. The possible options are:
|
||||||
|
|
||||||
|
* ``xunit1`` (or ``legacy``): produces old style output, compatible with the xunit 1.0 format. **This is the default**.
|
||||||
|
* ``xunit2``: produces `xunit 2.0 style output <https://github.com/jenkinsci/xunit-plugin/blob/xunit-2.3.2/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd>`__,
|
||||||
|
which should be more compatible with latest Jenkins versions.
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
junit_family = xunit2
|
||||||
|
|
||||||
.. confval:: junit_suite_name
|
.. confval:: junit_suite_name
|
||||||
|
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -10,7 +10,8 @@ INSTALL_REQUIRES = [
|
||||||
"six>=1.10.0",
|
"six>=1.10.0",
|
||||||
"setuptools",
|
"setuptools",
|
||||||
"attrs>=17.4.0",
|
"attrs>=17.4.0",
|
||||||
"more-itertools>=4.0.0",
|
'more-itertools>=4.0.0,<6.0.0;python_version<="2.7"',
|
||||||
|
'more-itertools>=4.0.0;python_version>"2.7"',
|
||||||
"atomicwrites>=1.0",
|
"atomicwrites>=1.0",
|
||||||
'funcsigs;python_version<"3.0"',
|
'funcsigs;python_version<"3.0"',
|
||||||
'pathlib2>=2.2.0;python_version<"3.6"',
|
'pathlib2>=2.2.0;python_version<"3.6"',
|
||||||
|
|
|
@ -408,7 +408,10 @@ class PytestPluginManager(PluginManager):
|
||||||
continue
|
continue
|
||||||
conftestpath = parent.join("conftest.py")
|
conftestpath = parent.join("conftest.py")
|
||||||
if conftestpath.isfile():
|
if conftestpath.isfile():
|
||||||
mod = self._importconftest(conftestpath)
|
# Use realpath to avoid loading the same conftest twice
|
||||||
|
# with build systems that create build directories containing
|
||||||
|
# symlinks to actual files.
|
||||||
|
mod = self._importconftest(conftestpath.realpath())
|
||||||
clist.append(mod)
|
clist.append(mod)
|
||||||
self._dirpath2confmods[directory] = clist
|
self._dirpath2confmods[directory] = clist
|
||||||
return clist
|
return clist
|
||||||
|
|
|
@ -75,6 +75,7 @@ class pytestPDB(object):
|
||||||
_config = None
|
_config = None
|
||||||
_pdb_cls = pdb.Pdb
|
_pdb_cls = pdb.Pdb
|
||||||
_saved = []
|
_saved = []
|
||||||
|
_recursive_debug = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _init_pdb(cls, *args, **kwargs):
|
def _init_pdb(cls, *args, **kwargs):
|
||||||
|
@ -87,29 +88,37 @@ class pytestPDB(object):
|
||||||
capman.suspend_global_capture(in_=True)
|
capman.suspend_global_capture(in_=True)
|
||||||
tw = _pytest.config.create_terminal_writer(cls._config)
|
tw = _pytest.config.create_terminal_writer(cls._config)
|
||||||
tw.line()
|
tw.line()
|
||||||
# Handle header similar to pdb.set_trace in py37+.
|
if cls._recursive_debug == 0:
|
||||||
header = kwargs.pop("header", None)
|
# Handle header similar to pdb.set_trace in py37+.
|
||||||
if header is not None:
|
header = kwargs.pop("header", None)
|
||||||
tw.sep(">", header)
|
if header is not None:
|
||||||
elif capman and capman.is_globally_capturing():
|
tw.sep(">", header)
|
||||||
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
|
elif capman and capman.is_globally_capturing():
|
||||||
else:
|
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
|
||||||
tw.sep(">", "PDB set_trace")
|
else:
|
||||||
|
tw.sep(">", "PDB set_trace")
|
||||||
|
|
||||||
class _PdbWrapper(cls._pdb_cls, object):
|
class _PdbWrapper(cls._pdb_cls, object):
|
||||||
_pytest_capman = capman
|
_pytest_capman = capman
|
||||||
_continued = False
|
_continued = False
|
||||||
|
|
||||||
|
def do_debug(self, arg):
|
||||||
|
cls._recursive_debug += 1
|
||||||
|
ret = super(_PdbWrapper, self).do_debug(arg)
|
||||||
|
cls._recursive_debug -= 1
|
||||||
|
return ret
|
||||||
|
|
||||||
def do_continue(self, arg):
|
def do_continue(self, arg):
|
||||||
ret = super(_PdbWrapper, self).do_continue(arg)
|
ret = super(_PdbWrapper, self).do_continue(arg)
|
||||||
if self._pytest_capman:
|
if self._pytest_capman:
|
||||||
tw = _pytest.config.create_terminal_writer(cls._config)
|
tw = _pytest.config.create_terminal_writer(cls._config)
|
||||||
tw.line()
|
tw.line()
|
||||||
if self._pytest_capman.is_globally_capturing():
|
if cls._recursive_debug == 0:
|
||||||
tw.sep(">", "PDB continue (IO-capturing resumed)")
|
if self._pytest_capman.is_globally_capturing():
|
||||||
else:
|
tw.sep(">", "PDB continue (IO-capturing resumed)")
|
||||||
tw.sep(">", "PDB continue")
|
else:
|
||||||
self._pytest_capman.resume_global_capture()
|
tw.sep(">", "PDB continue")
|
||||||
|
self._pytest_capman.resume_global_capture()
|
||||||
cls._pluginmanager.hook.pytest_leave_pdb(
|
cls._pluginmanager.hook.pytest_leave_pdb(
|
||||||
config=cls._config, pdb=self
|
config=cls._config, pdb=self
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import print_function
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
import itertools
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
@ -13,7 +14,6 @@ from collections import OrderedDict
|
||||||
import attr
|
import attr
|
||||||
import py
|
import py
|
||||||
import six
|
import six
|
||||||
from more_itertools import flatten
|
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
from _pytest import nodes
|
from _pytest import nodes
|
||||||
|
@ -1109,7 +1109,7 @@ class FixtureManager(object):
|
||||||
argnames = getfuncargnames(func, cls=cls)
|
argnames = getfuncargnames(func, cls=cls)
|
||||||
else:
|
else:
|
||||||
argnames = ()
|
argnames = ()
|
||||||
usefixtures = flatten(
|
usefixtures = itertools.chain.from_iterable(
|
||||||
mark.args for mark in node.iter_markers(name="usefixtures")
|
mark.args for mark in node.iter_markers(name="usefixtures")
|
||||||
)
|
)
|
||||||
initialnames = tuple(usefixtures) + argnames
|
initialnames = tuple(usefixtures) + argnames
|
||||||
|
|
|
@ -435,13 +435,6 @@ class LoggingPlugin(object):
|
||||||
# terminal reporter is disabled e.g. by pytest-xdist.
|
# terminal reporter is disabled e.g. by pytest-xdist.
|
||||||
return
|
return
|
||||||
|
|
||||||
# FIXME don't set verbosity level and derived attributes of
|
|
||||||
# terminalwriter directly
|
|
||||||
terminal_reporter.verbosity = config.option.verbose
|
|
||||||
terminal_reporter.showheader = terminal_reporter.verbosity >= 0
|
|
||||||
terminal_reporter.showfspath = terminal_reporter.verbosity >= 0
|
|
||||||
terminal_reporter.showlongtestinfo = terminal_reporter.verbosity > 0
|
|
||||||
|
|
||||||
capture_manager = config.pluginmanager.get_plugin("capturemanager")
|
capture_manager = config.pluginmanager.get_plugin("capturemanager")
|
||||||
# if capturemanager plugin is disabled, live logging still works.
|
# if capturemanager plugin is disabled, live logging still works.
|
||||||
log_cli_handler = _LiveLoggingStreamHandler(terminal_reporter, capture_manager)
|
log_cli_handler = _LiveLoggingStreamHandler(terminal_reporter, capture_manager)
|
||||||
|
|
|
@ -571,19 +571,9 @@ class Session(nodes.FSCollector):
|
||||||
if argpath.check(dir=1):
|
if argpath.check(dir=1):
|
||||||
assert not names, "invalid arg %r" % (arg,)
|
assert not names, "invalid arg %r" % (arg,)
|
||||||
|
|
||||||
if six.PY2:
|
|
||||||
|
|
||||||
def filter_(f):
|
|
||||||
return f.check(file=1) and not f.strpath.endswith("*.pyc")
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
def filter_(f):
|
|
||||||
return f.check(file=1)
|
|
||||||
|
|
||||||
seen_dirs = set()
|
seen_dirs = set()
|
||||||
for path in argpath.visit(
|
for path in argpath.visit(
|
||||||
fil=filter_, rec=self._recurse, bf=True, sort=True
|
fil=self._visit_filter, rec=self._recurse, bf=True, sort=True
|
||||||
):
|
):
|
||||||
dirpath = path.dirpath()
|
dirpath = path.dirpath()
|
||||||
if dirpath not in seen_dirs:
|
if dirpath not in seen_dirs:
|
||||||
|
@ -613,7 +603,7 @@ class Session(nodes.FSCollector):
|
||||||
col = self._node_cache[argpath]
|
col = self._node_cache[argpath]
|
||||||
else:
|
else:
|
||||||
collect_root = self._pkg_roots.get(argpath.dirname, self)
|
collect_root = self._pkg_roots.get(argpath.dirname, self)
|
||||||
col = collect_root._collectfile(argpath)
|
col = collect_root._collectfile(argpath, handle_dupes=False)
|
||||||
if col:
|
if col:
|
||||||
self._node_cache[argpath] = col
|
self._node_cache[argpath] = col
|
||||||
m = self.matchnodes(col, names)
|
m = self.matchnodes(col, names)
|
||||||
|
@ -658,6 +648,18 @@ class Session(nodes.FSCollector):
|
||||||
ihook.pytest_collect_directory(path=dirpath, parent=self)
|
ihook.pytest_collect_directory(path=dirpath, parent=self)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if six.PY2:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _visit_filter(f):
|
||||||
|
return f.check(file=1) and not f.strpath.endswith("*.pyc")
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _visit_filter(f):
|
||||||
|
return f.check(file=1)
|
||||||
|
|
||||||
def _tryconvertpyarg(self, x):
|
def _tryconvertpyarg(self, x):
|
||||||
"""Convert a dotted module name to path."""
|
"""Convert a dotted module name to path."""
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -81,7 +81,11 @@ class LsofFdLeakChecker(object):
|
||||||
|
|
||||||
def _exec_lsof(self):
|
def _exec_lsof(self):
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
return subprocess.check_output(("lsof", "-Ffn0", "-p", str(pid))).decode()
|
# py3: use subprocess.DEVNULL directly.
|
||||||
|
with open(os.devnull, "wb") as devnull:
|
||||||
|
return subprocess.check_output(
|
||||||
|
("lsof", "-Ffn0", "-p", str(pid)), stderr=devnull
|
||||||
|
).decode()
|
||||||
|
|
||||||
def _parse_lsof_output(self, out):
|
def _parse_lsof_output(self, out):
|
||||||
def isopen(line):
|
def isopen(line):
|
||||||
|
|
|
@ -222,12 +222,9 @@ class TerminalReporter(object):
|
||||||
import _pytest.config
|
import _pytest.config
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.verbosity = self.config.option.verbose
|
|
||||||
self.showheader = self.verbosity >= 0
|
|
||||||
self.showfspath = self.verbosity >= 0
|
|
||||||
self.showlongtestinfo = self.verbosity > 0
|
|
||||||
self._numcollected = 0
|
self._numcollected = 0
|
||||||
self._session = None
|
self._session = None
|
||||||
|
self._showfspath = None
|
||||||
|
|
||||||
self.stats = {}
|
self.stats = {}
|
||||||
self.startdir = py.path.local()
|
self.startdir = py.path.local()
|
||||||
|
@ -255,6 +252,28 @@ class TerminalReporter(object):
|
||||||
return False
|
return False
|
||||||
return self.config.getini("console_output_style") in ("progress", "count")
|
return self.config.getini("console_output_style") in ("progress", "count")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def verbosity(self):
|
||||||
|
return self.config.option.verbose
|
||||||
|
|
||||||
|
@property
|
||||||
|
def showheader(self):
|
||||||
|
return self.verbosity >= 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def showfspath(self):
|
||||||
|
if self._showfspath is None:
|
||||||
|
return self.verbosity >= 0
|
||||||
|
return self._showfspath
|
||||||
|
|
||||||
|
@showfspath.setter
|
||||||
|
def showfspath(self, value):
|
||||||
|
self._showfspath = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def showlongtestinfo(self):
|
||||||
|
return self.verbosity > 0
|
||||||
|
|
||||||
def hasopt(self, char):
|
def hasopt(self, char):
|
||||||
char = {"xfailed": "x", "skipped": "s"}.get(char, char)
|
char = {"xfailed": "x", "skipped": "s"}.get(char, char)
|
||||||
return char in self.reportchars
|
return char in self.reportchars
|
||||||
|
|
|
@ -1177,3 +1177,32 @@ def test_collectignore_via_conftest(testdir, monkeypatch):
|
||||||
|
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
assert result.ret == EXIT_NOTESTSCOLLECTED
|
assert result.ret == EXIT_NOTESTSCOLLECTED
|
||||||
|
|
||||||
|
|
||||||
|
def test_collect_pkg_init_and_file_in_args(testdir):
|
||||||
|
subdir = testdir.mkdir("sub")
|
||||||
|
init = subdir.ensure("__init__.py")
|
||||||
|
init.write("def test_init(): pass")
|
||||||
|
p = subdir.ensure("test_file.py")
|
||||||
|
p.write("def test_file(): pass")
|
||||||
|
|
||||||
|
# NOTE: without "-o python_files=*.py" this collects test_file.py twice.
|
||||||
|
# This changed/broke with "Add package scoped fixtures #2283" (2b1410895)
|
||||||
|
# initially (causing a RecursionError).
|
||||||
|
result = testdir.runpytest("-v", str(init), str(p))
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
"sub/test_file.py::test_file PASSED*",
|
||||||
|
"sub/test_file.py::test_file PASSED*",
|
||||||
|
"*2 passed in*",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
result = testdir.runpytest("-v", "-o", "python_files=*.py", str(init), str(p))
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
"sub/__init__.py::test_init PASSED*",
|
||||||
|
"sub/test_file.py::test_file PASSED*",
|
||||||
|
"*2 passed in*",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
|
@ -244,6 +244,42 @@ def test_conftest_symlink(testdir):
|
||||||
assert result.ret == EXIT_OK
|
assert result.ret == EXIT_OK
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not hasattr(py.path.local, "mksymlinkto"),
|
||||||
|
reason="symlink not available on this platform",
|
||||||
|
)
|
||||||
|
def test_conftest_symlink_files(testdir):
|
||||||
|
"""Check conftest.py loading when running in directory with symlinks."""
|
||||||
|
real = testdir.tmpdir.mkdir("real")
|
||||||
|
source = {
|
||||||
|
"app/test_foo.py": "def test1(fixture): pass",
|
||||||
|
"app/__init__.py": "",
|
||||||
|
"app/conftest.py": textwrap.dedent(
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
print("conftest_loaded")
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fixture():
|
||||||
|
print("fixture_used")
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
}
|
||||||
|
testdir.makepyfile(**{"real/%s" % k: v for k, v in source.items()})
|
||||||
|
|
||||||
|
# Create a build directory that contains symlinks to actual files
|
||||||
|
# but doesn't symlink actual directories.
|
||||||
|
build = testdir.tmpdir.mkdir("build")
|
||||||
|
build.mkdir("app")
|
||||||
|
for f in source:
|
||||||
|
build.join(f).mksymlinkto(real.join(f))
|
||||||
|
build.chdir()
|
||||||
|
result = testdir.runpytest("-vs", "app/test_foo.py")
|
||||||
|
result.stdout.fnmatch_lines(["*conftest_loaded*", "PASSED"])
|
||||||
|
assert result.ret == EXIT_OK
|
||||||
|
|
||||||
|
|
||||||
def test_no_conftest(testdir):
|
def test_no_conftest(testdir):
|
||||||
testdir.makeconftest("assert 0")
|
testdir.makeconftest("assert 0")
|
||||||
result = testdir.runpytest("--noconftest")
|
result = testdir.runpytest("--noconftest")
|
||||||
|
|
|
@ -513,6 +513,76 @@ class TestPDB(object):
|
||||||
assert "1 failed" in rest
|
assert "1 failed" in rest
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
|
def test_pdb_interaction_continue_recursive(self, testdir):
|
||||||
|
p1 = testdir.makepyfile(
|
||||||
|
mytest="""
|
||||||
|
import pdb
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
count_continue = 0
|
||||||
|
|
||||||
|
# Simulates pdbpp, which injects Pdb into do_debug, and uses
|
||||||
|
# self.__class__ in do_continue.
|
||||||
|
class CustomPdb(pdb.Pdb, object):
|
||||||
|
def do_debug(self, arg):
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
newglobals = {
|
||||||
|
'Pdb': self.__class__, # NOTE: different with pdb.Pdb
|
||||||
|
'sys': sys,
|
||||||
|
}
|
||||||
|
if sys.version_info < (3, ):
|
||||||
|
do_debug_func = pdb.Pdb.do_debug.im_func
|
||||||
|
else:
|
||||||
|
do_debug_func = pdb.Pdb.do_debug
|
||||||
|
|
||||||
|
orig_do_debug = types.FunctionType(
|
||||||
|
do_debug_func.__code__, newglobals,
|
||||||
|
do_debug_func.__name__, do_debug_func.__defaults__,
|
||||||
|
)
|
||||||
|
return orig_do_debug(self, arg)
|
||||||
|
do_debug.__doc__ = pdb.Pdb.do_debug.__doc__
|
||||||
|
|
||||||
|
def do_continue(self, *args, **kwargs):
|
||||||
|
global count_continue
|
||||||
|
count_continue += 1
|
||||||
|
return super(CustomPdb, self).do_continue(*args, **kwargs)
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
print("print_from_foo")
|
||||||
|
|
||||||
|
def test_1():
|
||||||
|
i = 0
|
||||||
|
print("hello17")
|
||||||
|
pytest.set_trace()
|
||||||
|
x = 3
|
||||||
|
print("hello18")
|
||||||
|
|
||||||
|
assert count_continue == 2, "unexpected_failure: %d != 2" % count_continue
|
||||||
|
pytest.fail("expected_failure")
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
child = testdir.spawn_pytest("--pdbcls=mytest:CustomPdb %s" % str(p1))
|
||||||
|
child.expect(r"PDB set_trace \(IO-capturing turned off\)")
|
||||||
|
child.expect(r"\n\(Pdb")
|
||||||
|
child.sendline("debug foo()")
|
||||||
|
child.expect("ENTERING RECURSIVE DEBUGGER")
|
||||||
|
child.expect(r"\n\(\(Pdb")
|
||||||
|
child.sendline("c")
|
||||||
|
child.expect("LEAVING RECURSIVE DEBUGGER")
|
||||||
|
assert b"PDB continue" not in child.before
|
||||||
|
assert b"print_from_foo" in child.before
|
||||||
|
child.sendline("c")
|
||||||
|
child.expect(r"PDB continue \(IO-capturing resumed\)")
|
||||||
|
rest = child.read().decode("utf8")
|
||||||
|
assert "hello17" in rest # out is captured
|
||||||
|
assert "hello18" in rest # out is captured
|
||||||
|
assert "1 failed" in rest
|
||||||
|
assert "Failed: expected_failure" in rest
|
||||||
|
assert "AssertionError: unexpected_failure" not in rest
|
||||||
|
self.flush(child)
|
||||||
|
|
||||||
def test_pdb_without_capture(self, testdir):
|
def test_pdb_without_capture(self, testdir):
|
||||||
p1 = testdir.makepyfile(
|
p1 = testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
|
23
tox.ini
23
tox.ini
|
@ -1,6 +1,6 @@
|
||||||
[tox]
|
[tox]
|
||||||
isolated_build = True
|
isolated_build = True
|
||||||
minversion = 3.3
|
minversion = 3.5.3
|
||||||
distshare = {homedir}/.tox/distshare
|
distshare = {homedir}/.tox/distshare
|
||||||
# make sure to update environment list in travis.yml and appveyor.yml
|
# make sure to update environment list in travis.yml and appveyor.yml
|
||||||
envlist =
|
envlist =
|
||||||
|
@ -23,10 +23,11 @@ commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {env:_PYTEST_TOX_ARGS:} {posargs}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {env:_PYTEST_TOX_ARGS:} {posargs}
|
||||||
coverage: coverage combine
|
coverage: coverage combine
|
||||||
coverage: coverage report
|
coverage: coverage report
|
||||||
passenv = USER USERNAME COVERAGE_* TRAVIS
|
passenv = USER USERNAME COVERAGE_* TRAVIS PYTEST_ADDOPTS
|
||||||
setenv =
|
setenv =
|
||||||
_PYTEST_TOX_ARGS=--lsof
|
_PYTEST_TOX_ARGS=--lsof
|
||||||
# configuration if a user runs tox with a "coverage" factor, for example "tox -e py37-coverage"
|
# Configuration to run with coverage similar to Travis/Appveyor, e.g.
|
||||||
|
# "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
|
||||||
coverage: COVERAGE_FILE={toxinidir}/.coverage
|
coverage: COVERAGE_FILE={toxinidir}/.coverage
|
||||||
|
@ -55,8 +56,8 @@ commands = pre-commit run --all-files --show-diff-on-failure
|
||||||
[testenv:py27-pexpect]
|
[testenv:py27-pexpect]
|
||||||
platform = linux|darwin
|
platform = linux|darwin
|
||||||
deps =
|
deps =
|
||||||
|
{[testenv]deps}
|
||||||
pexpect
|
pexpect
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/test_pdb.py testing/test_terminal.py testing/test_unittest.py}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/test_pdb.py testing/test_terminal.py testing/test_unittest.py}
|
||||||
|
|
||||||
|
@ -68,8 +69,8 @@ commands = {[testenv:py27-pexpect]commands}
|
||||||
[testenv:py27-nobyte]
|
[testenv:py27-nobyte]
|
||||||
extras = testing
|
extras = testing
|
||||||
deps =
|
deps =
|
||||||
|
{[testenv]deps}
|
||||||
pytest-xdist>=1.13
|
pytest-xdist>=1.13
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
|
||||||
distribute = true
|
distribute = true
|
||||||
setenv =
|
setenv =
|
||||||
{[testenv]setenv}
|
{[testenv]setenv}
|
||||||
|
@ -79,8 +80,8 @@ commands =
|
||||||
|
|
||||||
[testenv:py27-trial]
|
[testenv:py27-trial]
|
||||||
deps =
|
deps =
|
||||||
|
{[testenv]deps}
|
||||||
twisted
|
twisted
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/test_unittest.py}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/test_unittest.py}
|
||||||
|
|
||||||
|
@ -90,8 +91,8 @@ commands = {[testenv:py27-trial]commands}
|
||||||
|
|
||||||
[testenv:py27-numpy]
|
[testenv:py27-numpy]
|
||||||
deps =
|
deps =
|
||||||
|
{[testenv]deps}
|
||||||
numpy
|
numpy
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
|
||||||
commands=
|
commands=
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/python/approx.py}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:testing/python/approx.py}
|
||||||
|
|
||||||
|
@ -103,11 +104,13 @@ commands = {[testenv:py27-numpy]commands}
|
||||||
setenv=
|
setenv=
|
||||||
{[testenv]setenv}
|
{[testenv]setenv}
|
||||||
_PYTEST_SETUP_SKIP_PLUGGY_DEP=1
|
_PYTEST_SETUP_SKIP_PLUGGY_DEP=1
|
||||||
# NOTE: using env instead of "{[testenv]deps}", because of https://github.com/tox-dev/tox/issues/706.
|
deps =
|
||||||
_PYTEST_TOX_EXTRA_DEP=git+https://github.com/pytest-dev/pluggy.git@master
|
{[testenv]deps}
|
||||||
|
git+https://github.com/pytest-dev/pluggy.git@master
|
||||||
|
|
||||||
[testenv:py37-pluggymaster]
|
[testenv:py37-pluggymaster]
|
||||||
setenv = {[testenv:py27-pluggymaster]setenv}
|
setenv = {[testenv:py27-pluggymaster]setenv}
|
||||||
|
deps = {[testenv:py27-pluggymaster]deps}
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
|
@ -123,8 +126,8 @@ commands =
|
||||||
basepython = python3
|
basepython = python3
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
deps =
|
deps =
|
||||||
|
{[testenv]deps}
|
||||||
PyYAML
|
PyYAML
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest 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
|
||||||
|
|
Loading…
Reference in New Issue