Merge remote-tracking branch 'upstream/master' into mm
Conflicts: * src/_pytest/_code/code.py * src/_pytest/main.py * testing/python/metafunc.py * testing/test_parseopt.py * testing/test_pytester.py
This commit is contained in:
commit
93b74d28d2
|
@ -140,18 +140,18 @@ jobs:
|
||||||
run: "tox -e ${{ matrix.tox_env }}"
|
run: "tox -e ${{ matrix.tox_env }}"
|
||||||
|
|
||||||
- name: Prepare coverage token
|
- name: Prepare coverage token
|
||||||
if: success() && !matrix.skip_coverage && ( github.repository == 'pytest-dev/pytest' || github.event_name == 'pull_request' )
|
if: (!matrix.skip_coverage && ( github.repository == 'pytest-dev/pytest' || github.event_name == 'pull_request' ))
|
||||||
run: |
|
run: |
|
||||||
python scripts/append_codecov_token.py
|
python scripts/append_codecov_token.py
|
||||||
|
|
||||||
- name: Combine coverage
|
- name: Combine coverage
|
||||||
if: success() && !matrix.skip_coverage
|
if: (!matrix.skip_coverage)
|
||||||
run: |
|
run: |
|
||||||
python -m coverage combine
|
python -m coverage combine
|
||||||
python -m coverage xml
|
python -m coverage xml
|
||||||
|
|
||||||
- name: Codecov upload
|
- name: Codecov upload
|
||||||
if: success() && !matrix.skip_coverage
|
if: (!matrix.skip_coverage)
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.codecov }}
|
token: ${{ secrets.codecov }}
|
||||||
|
|
1
AUTHORS
1
AUTHORS
|
@ -55,7 +55,6 @@ Charles Cloud
|
||||||
Charles Machalow
|
Charles Machalow
|
||||||
Charnjit SiNGH (CCSJ)
|
Charnjit SiNGH (CCSJ)
|
||||||
Chris Lamb
|
Chris Lamb
|
||||||
Chris NeJame
|
|
||||||
Christian Boelsen
|
Christian Boelsen
|
||||||
Christian Fetzer
|
Christian Fetzer
|
||||||
Christian Neumüller
|
Christian Neumüller
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Captured output during teardown is shown with ``-rP``.
|
|
|
@ -1,2 +0,0 @@
|
||||||
Fix a ``pytest-xdist`` crash when dealing with exceptions raised in subprocesses created by the
|
|
||||||
``multiprocessing`` module.
|
|
|
@ -1 +0,0 @@
|
||||||
Optimized automatic renaming of test parameter IDs.
|
|
|
@ -1,3 +0,0 @@
|
||||||
:class:`FixtureDef <_pytest.fixtures.FixtureDef>` objects now properly register their finalizers with autouse and
|
|
||||||
parameterized fixtures that execute before them in the fixture stack so they are torn
|
|
||||||
down at the right times, and in the right order.
|
|
|
@ -1 +0,0 @@
|
||||||
Fix parsing of outcomes containing multiple errors with ``testdir`` results (regression in 5.3.0).
|
|
|
@ -6,6 +6,8 @@ Release announcements
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
|
||||||
|
release-5.3.4
|
||||||
|
release-5.3.3
|
||||||
release-5.3.2
|
release-5.3.2
|
||||||
release-5.3.1
|
release-5.3.1
|
||||||
release-5.3.0
|
release-5.3.0
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
pytest-5.3.3
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 5.3.3 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:
|
||||||
|
|
||||||
|
* Adam Johnson
|
||||||
|
* Alexandre Mulatinho
|
||||||
|
* Anthony Sottile
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Chris NeJame
|
||||||
|
* Daniel Hahler
|
||||||
|
* Hugo van Kemenade
|
||||||
|
* Marcelo Duarte Trevisani
|
||||||
|
* PaulC
|
||||||
|
* Ran Benita
|
||||||
|
* Ryan Barner
|
||||||
|
* Seth Junot
|
||||||
|
* marc
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -0,0 +1,20 @@
|
||||||
|
pytest-5.3.4
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 5.3.4 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:
|
||||||
|
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Daniel Hahler
|
||||||
|
* Ran Benita
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -28,6 +28,44 @@ with advance notice in the **Deprecations** section of releases.
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
pytest 5.3.4 (2020-01-20)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#6496 <https://github.com/pytest-dev/pytest/issues/6496>`_: Revert `#6436 <https://github.com/pytest-dev/pytest/issues/6436>`__: unfortunately this change has caused a number of regressions in many suites,
|
||||||
|
so the team decided to revert this change and make a new release while we continue to look for a solution.
|
||||||
|
|
||||||
|
|
||||||
|
pytest 5.3.3 (2020-01-16)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#2780 <https://github.com/pytest-dev/pytest/issues/2780>`_: Captured output during teardown is shown with ``-rP``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#5971 <https://github.com/pytest-dev/pytest/issues/5971>`_: Fix a ``pytest-xdist`` crash when dealing with exceptions raised in subprocesses created by the
|
||||||
|
``multiprocessing`` module.
|
||||||
|
|
||||||
|
|
||||||
|
- `#6436 <https://github.com/pytest-dev/pytest/issues/6436>`_: :class:`FixtureDef <_pytest.fixtures.FixtureDef>` objects now properly register their finalizers with autouse and
|
||||||
|
parameterized fixtures that execute before them in the fixture stack so they are torn
|
||||||
|
down at the right times, and in the right order.
|
||||||
|
|
||||||
|
|
||||||
|
- `#6532 <https://github.com/pytest-dev/pytest/issues/6532>`_: Fix parsing of outcomes containing multiple errors with ``testdir`` results (regression in 5.3.0).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Trivial/Internal Changes
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
- `#6350 <https://github.com/pytest-dev/pytest/issues/6350>`_: Optimized automatic renaming of test parameter IDs.
|
||||||
|
|
||||||
|
|
||||||
pytest 5.3.2 (2019-12-13)
|
pytest 5.3.2 (2019-12-13)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
@ -4842,7 +4880,7 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||||
* Updated docstrings with a more uniform style.
|
* Updated docstrings with a more uniform style.
|
||||||
|
|
||||||
* Add stderr write for ``pytest.exit(msg)`` during startup. Previously the message was never shown.
|
* Add stderr write for ``pytest.exit(msg)`` during startup. Previously the message was never shown.
|
||||||
Thanks `@BeyondEvil`_ for reporting `#1210`_. Thanks to `@jgsonesen`_ and
|
Thanks `@BeyondEvil`_ for reporting `#1210`_. Thanks to @jgsonesen and
|
||||||
`@tomviner`_ for the PR.
|
`@tomviner`_ for the PR.
|
||||||
|
|
||||||
* No longer display the incorrect test deselection reason (`#1372`_).
|
* No longer display the incorrect test deselection reason (`#1372`_).
|
||||||
|
@ -4974,7 +5012,6 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||||
.. _@gprasad84: https://github.com/gprasad84
|
.. _@gprasad84: https://github.com/gprasad84
|
||||||
.. _@graingert: https://github.com/graingert
|
.. _@graingert: https://github.com/graingert
|
||||||
.. _@hartym: https://github.com/hartym
|
.. _@hartym: https://github.com/hartym
|
||||||
.. _@jgsonesen: https://github.com/jgsonesen
|
|
||||||
.. _@kalekundert: https://github.com/kalekundert
|
.. _@kalekundert: https://github.com/kalekundert
|
||||||
.. _@kvas-it: https://github.com/kvas-it
|
.. _@kvas-it: https://github.com/kvas-it
|
||||||
.. _@marscher: https://github.com/marscher
|
.. _@marscher: https://github.com/marscher
|
||||||
|
|
|
@ -20,8 +20,6 @@ which were registered by installed plugins.
|
||||||
Initialization: determining rootdir and inifile
|
Initialization: determining rootdir and inifile
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pytest determines a ``rootdir`` for each test run which depends on
|
pytest determines a ``rootdir`` for each test run which depends on
|
||||||
the command line arguments (specified test files, paths) and on
|
the command line arguments (specified test files, paths) and on
|
||||||
the existence of *ini-files*. The determined ``rootdir`` and *ini-file* are
|
the existence of *ini-files*. The determined ``rootdir`` and *ini-file* are
|
||||||
|
@ -30,17 +28,17 @@ printed as part of the pytest header during startup.
|
||||||
Here's a summary what ``pytest`` uses ``rootdir`` for:
|
Here's a summary what ``pytest`` uses ``rootdir`` for:
|
||||||
|
|
||||||
* Construct *nodeids* during collection; each test is assigned
|
* Construct *nodeids* during collection; each test is assigned
|
||||||
a unique *nodeid* which is rooted at the ``rootdir`` and takes in account full path,
|
a unique *nodeid* which is rooted at the ``rootdir`` and takes into account
|
||||||
class name, function name and parametrization (if any).
|
the full path, class name, function name and parametrization (if any).
|
||||||
|
|
||||||
* Is used by plugins as a stable location to store project/test run specific information;
|
* Is used by plugins as a stable location to store project/test run specific information;
|
||||||
for example, the internal :ref:`cache <cache>` plugin creates a ``.pytest_cache`` subdirectory
|
for example, the internal :ref:`cache <cache>` plugin creates a ``.pytest_cache`` subdirectory
|
||||||
in ``rootdir`` to store its cross-test run state.
|
in ``rootdir`` to store its cross-test run state.
|
||||||
|
|
||||||
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
|
``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
|
||||||
influence how modules are imported. See :ref:`pythonpath` for more details.
|
influence how modules are imported. See :ref:`pythonpath` for more details.
|
||||||
|
|
||||||
``--rootdir=path`` command-line option can be used to force a specific directory.
|
The ``--rootdir=path`` command-line option can be used to force a specific directory.
|
||||||
The directory passed may contain environment variables when it is used in conjunction
|
The directory passed may contain environment variables when it is used in conjunction
|
||||||
with ``addopts`` in a ``pytest.ini`` file.
|
with ``addopts`` in a ``pytest.ini`` file.
|
||||||
|
|
||||||
|
|
|
@ -443,7 +443,7 @@ Now we can profile which test functions execute the slowest:
|
||||||
========================= slowest 3 test durations =========================
|
========================= slowest 3 test durations =========================
|
||||||
0.30s call test_some_are_slow.py::test_funcslow2
|
0.30s call test_some_are_slow.py::test_funcslow2
|
||||||
0.20s call test_some_are_slow.py::test_funcslow1
|
0.20s call test_some_are_slow.py::test_funcslow1
|
||||||
0.10s call test_some_are_slow.py::test_funcfast
|
0.11s call test_some_are_slow.py::test_funcfast
|
||||||
============================ 3 passed in 0.12s =============================
|
============================ 3 passed in 0.12s =============================
|
||||||
|
|
||||||
incremental testing - test steps
|
incremental testing - test steps
|
||||||
|
|
|
@ -100,7 +100,7 @@ def pre_release(version, *, skip_check_links):
|
||||||
print()
|
print()
|
||||||
print(f"{Fore.CYAN}[generate.pre_release] {Fore.GREEN}All done!")
|
print(f"{Fore.CYAN}[generate.pre_release] {Fore.GREEN}All done!")
|
||||||
print()
|
print()
|
||||||
print(f"Please push your branch and open a PR.")
|
print("Please push your branch and open a PR.")
|
||||||
|
|
||||||
|
|
||||||
def changelog(version, write_out=False):
|
def changelog(version, write_out=False):
|
||||||
|
|
|
@ -41,7 +41,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
from _pytest._code import Source
|
from _pytest._code import Source
|
||||||
|
|
||||||
_TracebackStyle = Literal["long", "short", "no", "native"]
|
_TracebackStyle = Literal["long", "short", "line", "no", "native"]
|
||||||
|
|
||||||
|
|
||||||
class Code:
|
class Code:
|
||||||
|
@ -67,9 +67,10 @@ class Code:
|
||||||
return not self == other
|
return not self == other
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self) -> Union[py.path.local, str]:
|
||||||
""" return a path object pointing to source code (note that it
|
""" return a path object pointing to source code (or a str in case
|
||||||
might not point to an actually existing file). """
|
of OSError / non-existing file).
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
p = py.path.local(self.raw.co_filename)
|
p = py.path.local(self.raw.co_filename)
|
||||||
# maybe don't try this checking
|
# maybe don't try this checking
|
||||||
|
@ -335,7 +336,7 @@ class Traceback(List[TracebackEntry]):
|
||||||
(path is None or codepath == path)
|
(path is None or codepath == path)
|
||||||
and (
|
and (
|
||||||
excludepath is None
|
excludepath is None
|
||||||
or not hasattr(codepath, "relto")
|
or not isinstance(codepath, py.path.local)
|
||||||
or not codepath.relto(excludepath)
|
or not codepath.relto(excludepath)
|
||||||
)
|
)
|
||||||
and (lineno is None or x.lineno == lineno)
|
and (lineno is None or x.lineno == lineno)
|
||||||
|
@ -919,7 +920,7 @@ class TerminalRepr:
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "<{} instance at {:0x}>".format(self.__class__, id(self))
|
return "<{} instance at {:0x}>".format(self.__class__, id(self))
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
@ -930,7 +931,7 @@ class ExceptionRepr(TerminalRepr):
|
||||||
def addsection(self, name: str, content: str, sep: str = "-") -> None:
|
def addsection(self, name: str, content: str, sep: str = "-") -> None:
|
||||||
self.sections.append((name, content, sep))
|
self.sections.append((name, content, sep))
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
for name, content, sep in self.sections:
|
for name, content, sep in self.sections:
|
||||||
tw.sep(sep, name)
|
tw.sep(sep, name)
|
||||||
tw.line(content)
|
tw.line(content)
|
||||||
|
@ -950,7 +951,7 @@ class ExceptionChainRepr(ExceptionRepr):
|
||||||
self.reprtraceback = chain[-1][0]
|
self.reprtraceback = chain[-1][0]
|
||||||
self.reprcrash = chain[-1][1]
|
self.reprcrash = chain[-1][1]
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
for element in self.chain:
|
for element in self.chain:
|
||||||
element[0].toterminal(tw)
|
element[0].toterminal(tw)
|
||||||
if element[2] is not None:
|
if element[2] is not None:
|
||||||
|
@ -967,7 +968,7 @@ class ReprExceptionInfo(ExceptionRepr):
|
||||||
self.reprtraceback = reprtraceback
|
self.reprtraceback = reprtraceback
|
||||||
self.reprcrash = reprcrash
|
self.reprcrash = reprcrash
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
self.reprtraceback.toterminal(tw)
|
self.reprtraceback.toterminal(tw)
|
||||||
super().toterminal(tw)
|
super().toterminal(tw)
|
||||||
|
|
||||||
|
@ -985,7 +986,7 @@ class ReprTraceback(TerminalRepr):
|
||||||
self.extraline = extraline
|
self.extraline = extraline
|
||||||
self.style = style
|
self.style = style
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
# the entries might have different styles
|
# the entries might have different styles
|
||||||
for i, entry in enumerate(self.reprentries):
|
for i, entry in enumerate(self.reprentries):
|
||||||
if entry.style == "long":
|
if entry.style == "long":
|
||||||
|
@ -1017,7 +1018,7 @@ class ReprEntryNative(TerminalRepr):
|
||||||
def __init__(self, tblines: Sequence[str]) -> None:
|
def __init__(self, tblines: Sequence[str]) -> None:
|
||||||
self.lines = tblines
|
self.lines = tblines
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
tw.write("".join(self.lines))
|
tw.write("".join(self.lines))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1036,7 +1037,7 @@ class ReprEntry(TerminalRepr):
|
||||||
self.reprfileloc = filelocrepr
|
self.reprfileloc = filelocrepr
|
||||||
self.style = style
|
self.style = style
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
if self.style == "short":
|
if self.style == "short":
|
||||||
assert self.reprfileloc is not None
|
assert self.reprfileloc is not None
|
||||||
self.reprfileloc.toterminal(tw)
|
self.reprfileloc.toterminal(tw)
|
||||||
|
@ -1071,7 +1072,7 @@ class ReprFileLocation(TerminalRepr):
|
||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
# filename and lineno output for each entry,
|
# filename and lineno output for each entry,
|
||||||
# using an output format that most editors understand
|
# using an output format that most editors understand
|
||||||
msg = self.message
|
msg = self.message
|
||||||
|
@ -1086,7 +1087,7 @@ class ReprLocals(TerminalRepr):
|
||||||
def __init__(self, lines: Sequence[str]) -> None:
|
def __init__(self, lines: Sequence[str]) -> None:
|
||||||
self.lines = lines
|
self.lines = lines
|
||||||
|
|
||||||
def toterminal(self, tw, indent="") -> None:
|
def toterminal(self, tw: py.io.TerminalWriter, indent="") -> None:
|
||||||
for line in self.lines:
|
for line in self.lines:
|
||||||
tw.line(indent + line)
|
tw.line(indent + line)
|
||||||
|
|
||||||
|
@ -1095,7 +1096,7 @@ class ReprFuncArgs(TerminalRepr):
|
||||||
def __init__(self, args: Sequence[Tuple[str, object]]) -> None:
|
def __init__(self, args: Sequence[Tuple[str, object]]) -> None:
|
||||||
self.args = args
|
self.args = args
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
if self.args:
|
if self.args:
|
||||||
linesofar = ""
|
linesofar = ""
|
||||||
for name, value in self.args:
|
for name, value in self.args:
|
||||||
|
|
|
@ -5,8 +5,8 @@ import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
import tokenize
|
import tokenize
|
||||||
import warnings
|
import warnings
|
||||||
from ast import PyCF_ONLY_AST as _AST_FLAG
|
|
||||||
from bisect import bisect_right
|
from bisect import bisect_right
|
||||||
|
from types import CodeType
|
||||||
from types import FrameType
|
from types import FrameType
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
from typing import List
|
from typing import List
|
||||||
|
@ -18,6 +18,10 @@ from typing import Union
|
||||||
import py
|
import py
|
||||||
|
|
||||||
from _pytest.compat import overload
|
from _pytest.compat import overload
|
||||||
|
from _pytest.compat import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
|
||||||
class Source:
|
class Source:
|
||||||
|
@ -121,7 +125,7 @@ class Source:
|
||||||
start, end = self.getstatementrange(lineno)
|
start, end = self.getstatementrange(lineno)
|
||||||
return self[start:end]
|
return self[start:end]
|
||||||
|
|
||||||
def getstatementrange(self, lineno: int):
|
def getstatementrange(self, lineno: int) -> Tuple[int, int]:
|
||||||
""" return (start, end) tuple which spans the minimal
|
""" return (start, end) tuple which spans the minimal
|
||||||
statement region which containing the given lineno.
|
statement region which containing the given lineno.
|
||||||
"""
|
"""
|
||||||
|
@ -154,14 +158,36 @@ class Source:
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return "\n".join(self.lines)
|
return "\n".join(self.lines)
|
||||||
|
|
||||||
|
@overload
|
||||||
def compile(
|
def compile(
|
||||||
self,
|
self,
|
||||||
filename=None,
|
filename: Optional[str] = ...,
|
||||||
mode="exec",
|
mode: str = ...,
|
||||||
|
flag: "Literal[0]" = ...,
|
||||||
|
dont_inherit: int = ...,
|
||||||
|
_genframe: Optional[FrameType] = ...,
|
||||||
|
) -> CodeType:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@overload # noqa: F811
|
||||||
|
def compile( # noqa: F811
|
||||||
|
self,
|
||||||
|
filename: Optional[str] = ...,
|
||||||
|
mode: str = ...,
|
||||||
|
flag: int = ...,
|
||||||
|
dont_inherit: int = ...,
|
||||||
|
_genframe: Optional[FrameType] = ...,
|
||||||
|
) -> Union[CodeType, ast.AST]:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def compile( # noqa: F811
|
||||||
|
self,
|
||||||
|
filename: Optional[str] = None,
|
||||||
|
mode: str = "exec",
|
||||||
flag: int = 0,
|
flag: int = 0,
|
||||||
dont_inherit: int = 0,
|
dont_inherit: int = 0,
|
||||||
_genframe: Optional[FrameType] = None,
|
_genframe: Optional[FrameType] = None,
|
||||||
):
|
) -> Union[CodeType, ast.AST]:
|
||||||
""" return compiled code object. if filename is None
|
""" return compiled code object. if filename is None
|
||||||
invent an artificial filename which displays
|
invent an artificial filename which displays
|
||||||
the source/line position of the caller frame.
|
the source/line position of the caller frame.
|
||||||
|
@ -191,8 +217,10 @@ class Source:
|
||||||
newex.text = ex.text
|
newex.text = ex.text
|
||||||
raise newex
|
raise newex
|
||||||
else:
|
else:
|
||||||
if flag & _AST_FLAG:
|
if flag & ast.PyCF_ONLY_AST:
|
||||||
|
assert isinstance(co, ast.AST)
|
||||||
return co
|
return co
|
||||||
|
assert isinstance(co, CodeType)
|
||||||
lines = [(x + "\n") for x in self.lines]
|
lines = [(x + "\n") for x in self.lines]
|
||||||
# Type ignored because linecache.cache is private.
|
# Type ignored because linecache.cache is private.
|
||||||
linecache.cache[filename] = (1, None, lines, filename) # type: ignore
|
linecache.cache[filename] = (1, None, lines, filename) # type: ignore
|
||||||
|
@ -204,7 +232,35 @@ class Source:
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def compile_(source, filename=None, mode="exec", flags: int = 0, dont_inherit: int = 0):
|
@overload
|
||||||
|
def compile_(
|
||||||
|
source: Union[str, bytes, ast.mod, ast.AST],
|
||||||
|
filename: Optional[str] = ...,
|
||||||
|
mode: str = ...,
|
||||||
|
flags: "Literal[0]" = ...,
|
||||||
|
dont_inherit: int = ...,
|
||||||
|
) -> CodeType:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
@overload # noqa: F811
|
||||||
|
def compile_( # noqa: F811
|
||||||
|
source: Union[str, bytes, ast.mod, ast.AST],
|
||||||
|
filename: Optional[str] = ...,
|
||||||
|
mode: str = ...,
|
||||||
|
flags: int = ...,
|
||||||
|
dont_inherit: int = ...,
|
||||||
|
) -> Union[CodeType, ast.AST]:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
def compile_( # noqa: F811
|
||||||
|
source: Union[str, bytes, ast.mod, ast.AST],
|
||||||
|
filename: Optional[str] = None,
|
||||||
|
mode: str = "exec",
|
||||||
|
flags: int = 0,
|
||||||
|
dont_inherit: int = 0,
|
||||||
|
) -> Union[CodeType, ast.AST]:
|
||||||
""" compile the given source to a raw code object,
|
""" compile the given source to a raw code object,
|
||||||
and maintain an internal cache which allows later
|
and maintain an internal cache which allows later
|
||||||
retrieval of the source code for the code object
|
retrieval of the source code for the code object
|
||||||
|
@ -212,14 +268,16 @@ def compile_(source, filename=None, mode="exec", flags: int = 0, dont_inherit: i
|
||||||
"""
|
"""
|
||||||
if isinstance(source, ast.AST):
|
if isinstance(source, ast.AST):
|
||||||
# XXX should Source support having AST?
|
# XXX should Source support having AST?
|
||||||
return compile(source, filename, mode, flags, dont_inherit)
|
assert filename is not None
|
||||||
|
co = compile(source, filename, mode, flags, dont_inherit)
|
||||||
|
assert isinstance(co, (CodeType, ast.AST))
|
||||||
|
return co
|
||||||
_genframe = sys._getframe(1) # the caller
|
_genframe = sys._getframe(1) # the caller
|
||||||
s = Source(source)
|
s = Source(source)
|
||||||
co = s.compile(filename, mode, flags, _genframe=_genframe)
|
return s.compile(filename, mode, flags, _genframe=_genframe)
|
||||||
return co
|
|
||||||
|
|
||||||
|
|
||||||
def getfslineno(obj):
|
def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]:
|
||||||
""" Return source location (path, lineno) for the given object.
|
""" Return source location (path, lineno) for the given object.
|
||||||
If the source cannot be determined return ("", -1).
|
If the source cannot be determined return ("", -1).
|
||||||
|
|
||||||
|
@ -316,7 +374,7 @@ def getstatementrange_ast(
|
||||||
# don't produce duplicate warnings when compiling source to find ast
|
# don't produce duplicate warnings when compiling source to find ast
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.simplefilter("ignore")
|
warnings.simplefilter("ignore")
|
||||||
astnode = compile(content, "source", "exec", _AST_FLAG)
|
astnode = ast.parse(content, "source", "exec")
|
||||||
|
|
||||||
start, end = get_statement_startend2(lineno, astnode)
|
start, end = get_statement_startend2(lineno, astnode)
|
||||||
# we need to correct the end:
|
# we need to correct the end:
|
||||||
|
|
|
@ -98,8 +98,10 @@ def getlocation(function, curdir=None) -> str:
|
||||||
function = get_real_func(function)
|
function = get_real_func(function)
|
||||||
fn = py.path.local(inspect.getfile(function))
|
fn = py.path.local(inspect.getfile(function))
|
||||||
lineno = function.__code__.co_firstlineno
|
lineno = function.__code__.co_firstlineno
|
||||||
if curdir is not None and fn.relto(curdir):
|
if curdir is not None:
|
||||||
fn = fn.relto(curdir)
|
relfn = fn.relto(curdir)
|
||||||
|
if relfn:
|
||||||
|
return "%s:%d" % (relfn, lineno + 1)
|
||||||
return "%s:%d" % (fn, lineno + 1)
|
return "%s:%d" % (fn, lineno + 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,9 @@ def determine_setup(
|
||||||
sections = ["tool:pytest", "pytest"] if is_cfg_file else ["pytest"]
|
sections = ["tool:pytest", "pytest"] if is_cfg_file else ["pytest"]
|
||||||
for section in sections:
|
for section in sections:
|
||||||
try:
|
try:
|
||||||
inicfg = iniconfig[section]
|
inicfg = iniconfig[
|
||||||
|
section
|
||||||
|
] # type: Optional[py.iniconfig._SectionWrapper]
|
||||||
if is_cfg_file and section == "pytest" and config is not None:
|
if is_cfg_file and section == "pytest" and config is not None:
|
||||||
fail(
|
fail(
|
||||||
CFG_PYTEST_SECTION.format(filename=str(inifile)), pytrace=False
|
CFG_PYTEST_SECTION.format(filename=str(inifile)), pytrace=False
|
||||||
|
|
|
@ -13,6 +13,8 @@ from typing import Sequence
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
import py
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import outcomes
|
from _pytest import outcomes
|
||||||
from _pytest._code.code import ExceptionInfo
|
from _pytest._code.code import ExceptionInfo
|
||||||
|
@ -137,7 +139,7 @@ class ReprFailDoctest(TerminalRepr):
|
||||||
):
|
):
|
||||||
self.reprlocation_lines = reprlocation_lines
|
self.reprlocation_lines = reprlocation_lines
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
for reprlocation, lines in self.reprlocation_lines:
|
for reprlocation, lines in self.reprlocation_lines:
|
||||||
for line in lines:
|
for line in lines:
|
||||||
tw.line(line)
|
tw.line(line)
|
||||||
|
|
|
@ -425,7 +425,7 @@ class FixtureRequest:
|
||||||
return self._pyfuncitem.getparent(_pytest.python.Module).obj
|
return self._pyfuncitem.getparent(_pytest.python.Module).obj
|
||||||
|
|
||||||
@scopeproperty()
|
@scopeproperty()
|
||||||
def fspath(self):
|
def fspath(self) -> py.path.local:
|
||||||
""" the file system path of the test module which collected this test. """
|
""" the file system path of the test module which collected this test. """
|
||||||
return self._pyfuncitem.fspath
|
return self._pyfuncitem.fspath
|
||||||
|
|
||||||
|
@ -749,7 +749,7 @@ class FixtureLookupErrorRepr(TerminalRepr):
|
||||||
self.firstlineno = firstlineno
|
self.firstlineno = firstlineno
|
||||||
self.argname = argname
|
self.argname = argname
|
||||||
|
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
# tw.line("FixtureLookupError: %s" %(self.argname), red=True)
|
# tw.line("FixtureLookupError: %s" %(self.argname), red=True)
|
||||||
for tbline in self.tblines:
|
for tbline in self.tblines:
|
||||||
tw.line(tbline.rstrip())
|
tw.line(tbline.rstrip())
|
||||||
|
@ -881,7 +881,9 @@ class FixtureDef:
|
||||||
self._finalizers = []
|
self._finalizers = []
|
||||||
|
|
||||||
def execute(self, request):
|
def execute(self, request):
|
||||||
for argname in self._dependee_fixture_argnames(request):
|
# get required arguments and register our own finish()
|
||||||
|
# with their finalization
|
||||||
|
for argname in self.argnames:
|
||||||
fixturedef = request._get_active_fixturedef(argname)
|
fixturedef = request._get_active_fixturedef(argname)
|
||||||
if argname != "request":
|
if argname != "request":
|
||||||
fixturedef.addfinalizer(functools.partial(self.finish, request=request))
|
fixturedef.addfinalizer(functools.partial(self.finish, request=request))
|
||||||
|
@ -904,61 +906,6 @@ class FixtureDef:
|
||||||
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
|
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
|
||||||
return hook.pytest_fixture_setup(fixturedef=self, request=request)
|
return hook.pytest_fixture_setup(fixturedef=self, request=request)
|
||||||
|
|
||||||
def _dependee_fixture_argnames(self, request):
|
|
||||||
"""A list of argnames for fixtures that this fixture depends on.
|
|
||||||
|
|
||||||
Given a request, this looks at the currently known list of fixture argnames, and
|
|
||||||
attempts to determine what slice of the list contains fixtures that it can know
|
|
||||||
should execute before it. This information is necessary so that this fixture can
|
|
||||||
know what fixtures to register its finalizer with to make sure that if they
|
|
||||||
would be torn down, they would tear down this fixture before themselves. It's
|
|
||||||
crucial for fixtures to be torn down in the inverse order that they were set up
|
|
||||||
in so that they don't try to clean up something that another fixture is still
|
|
||||||
depending on.
|
|
||||||
|
|
||||||
When autouse fixtures are involved, it can be tricky to figure out when fixtures
|
|
||||||
should be torn down. To solve this, this method leverages the ``fixturenames``
|
|
||||||
list provided by the ``request`` object, as this list is at least somewhat
|
|
||||||
sorted (in terms of the order fixtures are set up in) by the time this method is
|
|
||||||
reached. It's sorted enough that the starting point of fixtures that depend on
|
|
||||||
this one can be found using the ``self._parent_request`` stack.
|
|
||||||
|
|
||||||
If a request in the ``self._parent_request`` stack has a ``:class:FixtureDef``
|
|
||||||
associated with it, then that fixture is dependent on this one, so any fixture
|
|
||||||
names that appear in the list of fixture argnames that come after it can also be
|
|
||||||
ruled out. The argnames of all fixtures associated with a request in the
|
|
||||||
``self._parent_request`` stack are found, and the lowest index argname is
|
|
||||||
considered the earliest point in the list of fixture argnames where everything
|
|
||||||
from that point onward can be considered to execute after this fixture.
|
|
||||||
Everything before this point can be considered fixtures that this fixture
|
|
||||||
depends on, and so this fixture should register its finalizer with all of them
|
|
||||||
to ensure that if any of them are to be torn down, they will tear this fixture
|
|
||||||
down first.
|
|
||||||
|
|
||||||
This is the first part of the list of fixture argnames that is returned. The last
|
|
||||||
part of the list is everything in ``self.argnames`` as those are explicit
|
|
||||||
dependees of this fixture, so this fixture should definitely register its
|
|
||||||
finalizer with them.
|
|
||||||
"""
|
|
||||||
all_fix_names = request.fixturenames
|
|
||||||
try:
|
|
||||||
current_fix_index = all_fix_names.index(self.argname)
|
|
||||||
except ValueError:
|
|
||||||
current_fix_index = len(request.fixturenames)
|
|
||||||
parent_fixture_indexes = set()
|
|
||||||
|
|
||||||
parent_request = request._parent_request
|
|
||||||
while hasattr(parent_request, "_parent_request"):
|
|
||||||
if hasattr(parent_request, "_fixturedef"):
|
|
||||||
parent_fix_name = parent_request._fixturedef.argname
|
|
||||||
if parent_fix_name in all_fix_names:
|
|
||||||
parent_fixture_indexes.add(all_fix_names.index(parent_fix_name))
|
|
||||||
parent_request = parent_request._parent_request
|
|
||||||
|
|
||||||
stack_slice_index = min([current_fix_index, *parent_fixture_indexes])
|
|
||||||
active_fixture_argnames = all_fix_names[:stack_slice_index]
|
|
||||||
return {*active_fixture_argnames, *self.argnames}
|
|
||||||
|
|
||||||
def cache_key(self, request):
|
def cache_key(self, request):
|
||||||
return request.param_index if not hasattr(request, "param") else request.param
|
return request.param_index if not hasattr(request, "param") else request.param
|
||||||
|
|
||||||
|
|
|
@ -378,7 +378,9 @@ class _bestrelpath_cache(dict):
|
||||||
class Session(nodes.FSCollector):
|
class Session(nodes.FSCollector):
|
||||||
Interrupted = Interrupted
|
Interrupted = Interrupted
|
||||||
Failed = Failed
|
Failed = Failed
|
||||||
|
# Set on the session by runner.pytest_sessionstart.
|
||||||
_setupstate = None # type: SetupState
|
_setupstate = None # type: SetupState
|
||||||
|
# Set on the session by fixtures.pytest_sessionstart.
|
||||||
_fixturemanager = None # type: FixtureManager
|
_fixturemanager = None # type: FixtureManager
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
|
|
@ -90,7 +90,7 @@ class Node(metaclass=NodeMeta):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name,
|
name: str,
|
||||||
parent: Optional["Node"] = None,
|
parent: Optional["Node"] = None,
|
||||||
config: Optional[Config] = None,
|
config: Optional[Config] = None,
|
||||||
session: Optional["Session"] = None,
|
session: Optional["Session"] = None,
|
||||||
|
@ -476,7 +476,7 @@ class Item(Node):
|
||||||
if content:
|
if content:
|
||||||
self._report_sections.append((when, key, content))
|
self._report_sections.append((when, key, content))
|
||||||
|
|
||||||
def reportinfo(self) -> Tuple[str, Optional[int], str]:
|
def reportinfo(self) -> Tuple[Union[py.path.local, str], Optional[int], str]:
|
||||||
return self.fspath, None, ""
|
return self.fspath, None, ""
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
|
@ -39,6 +39,8 @@ from _pytest.reports import TestReport
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
|
import pexpect
|
||||||
|
|
||||||
|
|
||||||
IGNORE_PAM = [ # filenames added when obtaining details about the current user
|
IGNORE_PAM = [ # filenames added when obtaining details about the current user
|
||||||
"/var/lib/sss/mc/passwd"
|
"/var/lib/sss/mc/passwd"
|
||||||
|
@ -1235,7 +1237,9 @@ class Testdir:
|
||||||
args = self._getpytestargs() + args
|
args = self._getpytestargs() + args
|
||||||
return self.run(*args, timeout=timeout)
|
return self.run(*args, timeout=timeout)
|
||||||
|
|
||||||
def spawn_pytest(self, string, expect_timeout=10.0):
|
def spawn_pytest(
|
||||||
|
self, string: str, expect_timeout: float = 10.0
|
||||||
|
) -> "pexpect.spawn":
|
||||||
"""Run pytest using pexpect.
|
"""Run pytest using pexpect.
|
||||||
|
|
||||||
This makes sure to use the right pytest and sets up the temporary
|
This makes sure to use the right pytest and sets up the temporary
|
||||||
|
@ -1249,7 +1253,7 @@ class Testdir:
|
||||||
cmd = "{} --basetemp={} {}".format(invoke, basetemp, string)
|
cmd = "{} --basetemp={} {}".format(invoke, basetemp, string)
|
||||||
return self.spawn(cmd, expect_timeout=expect_timeout)
|
return self.spawn(cmd, expect_timeout=expect_timeout)
|
||||||
|
|
||||||
def spawn(self, cmd, expect_timeout=10.0):
|
def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn":
|
||||||
"""Run a command using pexpect.
|
"""Run a command using pexpect.
|
||||||
|
|
||||||
The pexpect child is returned.
|
The pexpect child is returned.
|
||||||
|
|
|
@ -57,7 +57,7 @@ def deprecated_call(func=None, *args, **kwargs):
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def warns(
|
def warns(
|
||||||
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
|
expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
|
||||||
*,
|
*,
|
||||||
match: "Optional[Union[str, Pattern]]" = ...
|
match: "Optional[Union[str, Pattern]]" = ...
|
||||||
) -> "WarningsChecker":
|
) -> "WarningsChecker":
|
||||||
|
@ -66,7 +66,7 @@ def warns(
|
||||||
|
|
||||||
@overload # noqa: F811
|
@overload # noqa: F811
|
||||||
def warns( # noqa: F811
|
def warns( # noqa: F811
|
||||||
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
|
expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
|
||||||
func: Callable,
|
func: Callable,
|
||||||
*args: Any,
|
*args: Any,
|
||||||
match: Optional[Union[str, "Pattern"]] = ...,
|
match: Optional[Union[str, "Pattern"]] = ...,
|
||||||
|
@ -76,7 +76,7 @@ def warns( # noqa: F811
|
||||||
|
|
||||||
|
|
||||||
def warns( # noqa: F811
|
def warns( # noqa: F811
|
||||||
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
|
expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
|
||||||
*args: Any,
|
*args: Any,
|
||||||
match: Optional[Union[str, "Pattern"]] = None,
|
match: Optional[Union[str, "Pattern"]] = None,
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
from typing import Any
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
@ -17,6 +18,7 @@ from _pytest._code.code import ReprFuncArgs
|
||||||
from _pytest._code.code import ReprLocals
|
from _pytest._code.code import ReprLocals
|
||||||
from _pytest._code.code import ReprTraceback
|
from _pytest._code.code import ReprTraceback
|
||||||
from _pytest._code.code import TerminalRepr
|
from _pytest._code.code import TerminalRepr
|
||||||
|
from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.nodes import Node
|
from _pytest.nodes import Node
|
||||||
from _pytest.outcomes import skip
|
from _pytest.outcomes import skip
|
||||||
from _pytest.pathlib import Path
|
from _pytest.pathlib import Path
|
||||||
|
@ -41,9 +43,14 @@ class BaseReport:
|
||||||
sections = [] # type: List[Tuple[str, str]]
|
sections = [] # type: List[Tuple[str, str]]
|
||||||
nodeid = None # type: str
|
nodeid = None # type: str
|
||||||
|
|
||||||
def __init__(self, **kw):
|
def __init__(self, **kw: Any) -> None:
|
||||||
self.__dict__.update(kw)
|
self.__dict__.update(kw)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
# Can have arbitrary fields given to __init__().
|
||||||
|
def __getattr__(self, key: str) -> Any:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
def toterminal(self, out) -> None:
|
def toterminal(self, out) -> None:
|
||||||
if hasattr(self, "node"):
|
if hasattr(self, "node"):
|
||||||
out.line(getslaveinfoline(self.node)) # type: ignore
|
out.line(getslaveinfoline(self.node)) # type: ignore
|
||||||
|
@ -114,7 +121,7 @@ class BaseReport:
|
||||||
skipped = property(lambda x: x.outcome == "skipped")
|
skipped = property(lambda x: x.outcome == "skipped")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fspath(self):
|
def fspath(self) -> str:
|
||||||
return self.nodeid.split("::")[0]
|
return self.nodeid.split("::")[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -15,7 +15,9 @@ from .reports import CollectErrorRepr
|
||||||
from .reports import CollectReport
|
from .reports import CollectReport
|
||||||
from .reports import TestReport
|
from .reports import TestReport
|
||||||
from _pytest._code.code import ExceptionInfo
|
from _pytest._code.code import ExceptionInfo
|
||||||
|
from _pytest._code.code import ExceptionRepr
|
||||||
from _pytest.compat import TYPE_CHECKING
|
from _pytest.compat import TYPE_CHECKING
|
||||||
|
from _pytest.nodes import Collector
|
||||||
from _pytest.nodes import Node
|
from _pytest.nodes import Node
|
||||||
from _pytest.outcomes import Exit
|
from _pytest.outcomes import Exit
|
||||||
from _pytest.outcomes import Skipped
|
from _pytest.outcomes import Skipped
|
||||||
|
@ -251,7 +253,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
return TestReport.from_item_and_call(item, call)
|
return TestReport.from_item_and_call(item, call)
|
||||||
|
|
||||||
|
|
||||||
def pytest_make_collect_report(collector) -> CollectReport:
|
def pytest_make_collect_report(collector: Collector) -> CollectReport:
|
||||||
call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
|
call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
|
||||||
longrepr = None
|
longrepr = None
|
||||||
if not call.excinfo:
|
if not call.excinfo:
|
||||||
|
@ -264,7 +266,10 @@ def pytest_make_collect_report(collector) -> CollectReport:
|
||||||
skip_exceptions.append(unittest.SkipTest) # type: ignore
|
skip_exceptions.append(unittest.SkipTest) # type: ignore
|
||||||
if call.excinfo.errisinstance(tuple(skip_exceptions)):
|
if call.excinfo.errisinstance(tuple(skip_exceptions)):
|
||||||
outcome = "skipped"
|
outcome = "skipped"
|
||||||
r = collector._repr_failure_py(call.excinfo, "line").reprcrash
|
r_ = collector._repr_failure_py(call.excinfo, "line")
|
||||||
|
assert isinstance(r_, ExceptionRepr), r_
|
||||||
|
r = r_.reprcrash
|
||||||
|
assert r
|
||||||
longrepr = (str(r.path), r.lineno, r.message)
|
longrepr = (str(r.path), r.lineno, r.message)
|
||||||
else:
|
else:
|
||||||
outcome = "failed"
|
outcome = "failed"
|
||||||
|
|
|
@ -606,7 +606,7 @@ class TestInvocationVariants:
|
||||||
def test_equivalence_pytest_pytest(self):
|
def test_equivalence_pytest_pytest(self):
|
||||||
assert pytest.main == py.test.cmdline.main
|
assert pytest.main == py.test.cmdline.main
|
||||||
|
|
||||||
def test_invoke_with_invalid_type(self, capsys):
|
def test_invoke_with_invalid_type(self):
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
TypeError, match="expected to be a list or tuple of strings, got: '-h'"
|
TypeError, match="expected to be a list or tuple of strings, got: '-h'"
|
||||||
):
|
):
|
||||||
|
@ -617,7 +617,7 @@ class TestInvocationVariants:
|
||||||
assert retcode == ExitCode.NO_TESTS_COLLECTED
|
assert retcode == ExitCode.NO_TESTS_COLLECTED
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
|
|
||||||
def test_invoke_plugin_api(self, testdir, capsys):
|
def test_invoke_plugin_api(self, capsys):
|
||||||
class MyPlugin:
|
class MyPlugin:
|
||||||
def pytest_addoption(self, parser):
|
def pytest_addoption(self, parser):
|
||||||
parser.addoption("--myopt")
|
parser.addoption("--myopt")
|
||||||
|
|
|
@ -21,8 +21,6 @@ except ImportError:
|
||||||
else:
|
else:
|
||||||
invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
|
invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
|
||||||
|
|
||||||
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def limited_recursion_depth():
|
def limited_recursion_depth():
|
||||||
|
@ -857,7 +855,7 @@ raise ValueError()
|
||||||
from _pytest._code.code import TerminalRepr
|
from _pytest._code.code import TerminalRepr
|
||||||
|
|
||||||
class MyRepr(TerminalRepr):
|
class MyRepr(TerminalRepr):
|
||||||
def toterminal(self, tw) -> None:
|
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||||
tw.line("я")
|
tw.line("я")
|
||||||
|
|
||||||
x = str(MyRepr())
|
x = str(MyRepr())
|
||||||
|
|
|
@ -4,10 +4,13 @@
|
||||||
import ast
|
import ast
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
|
from types import CodeType
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import py
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest._code import Source
|
from _pytest._code import Source
|
||||||
|
@ -147,6 +150,10 @@ class TestAccesses:
|
||||||
assert len(x.lines) == 2
|
assert len(x.lines) == 2
|
||||||
assert str(x) == "def f(x):\n pass"
|
assert str(x) == "def f(x):\n pass"
|
||||||
|
|
||||||
|
def test_getrange_step_not_supported(self) -> None:
|
||||||
|
with pytest.raises(IndexError, match=r"step"):
|
||||||
|
self.source[::2]
|
||||||
|
|
||||||
def test_getline(self) -> None:
|
def test_getline(self) -> None:
|
||||||
x = self.source[0]
|
x = self.source[0]
|
||||||
assert x == "def f(x):"
|
assert x == "def f(x):"
|
||||||
|
@ -449,6 +456,14 @@ def test_idem_compile_and_getsource() -> None:
|
||||||
assert src == expected
|
assert src == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_compile_ast() -> None:
|
||||||
|
# We don't necessarily want to support this.
|
||||||
|
# This test was added just for coverage.
|
||||||
|
stmt = ast.parse("def x(): pass")
|
||||||
|
co = _pytest._code.compile(stmt, filename="foo.py")
|
||||||
|
assert isinstance(co, CodeType)
|
||||||
|
|
||||||
|
|
||||||
def test_findsource_fallback() -> None:
|
def test_findsource_fallback() -> None:
|
||||||
from _pytest._code.source import findsource
|
from _pytest._code.source import findsource
|
||||||
|
|
||||||
|
@ -488,6 +503,7 @@ def test_getfslineno() -> None:
|
||||||
|
|
||||||
fspath, lineno = getfslineno(f)
|
fspath, lineno = getfslineno(f)
|
||||||
|
|
||||||
|
assert isinstance(fspath, py.path.local)
|
||||||
assert fspath.basename == "test_source.py"
|
assert fspath.basename == "test_source.py"
|
||||||
assert lineno == f.__code__.co_firstlineno - 1 # see findsource
|
assert lineno == f.__code__.co_firstlineno - 1 # see findsource
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ if sys.gettrace():
|
||||||
|
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||||||
def pytest_collection_modifyitems(config, items):
|
def pytest_collection_modifyitems(items):
|
||||||
"""Prefer faster tests.
|
"""Prefer faster tests.
|
||||||
|
|
||||||
Use a hookwrapper to do this in the beginning, so e.g. --ff still works
|
Use a hookwrapper to do this in the beginning, so e.g. --ff still works
|
||||||
|
|
|
@ -626,7 +626,7 @@ def test_log_cli_ini_level(testdir):
|
||||||
"cli_args",
|
"cli_args",
|
||||||
["", "--log-level=WARNING", "--log-file-level=WARNING", "--log-cli-level=WARNING"],
|
["", "--log-level=WARNING", "--log-file-level=WARNING", "--log-cli-level=WARNING"],
|
||||||
)
|
)
|
||||||
def test_log_cli_auto_enable(testdir, request, cli_args):
|
def test_log_cli_auto_enable(testdir, cli_args):
|
||||||
"""Check that live logs are enabled if --log-level or --log-cli-level is passed on the CLI.
|
"""Check that live logs are enabled if --log-level or --log-cli-level is passed on the CLI.
|
||||||
It should not be auto enabled if the same configs are set on the INI file.
|
It should not be auto enabled if the same configs are set on the INI file.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -286,7 +286,7 @@ class TestFunction:
|
||||||
|
|
||||||
return pytest.Function.from_parent(config=config, parent=session, **kwargs)
|
return pytest.Function.from_parent(config=config, parent=session, **kwargs)
|
||||||
|
|
||||||
def test_function_equality(self, testdir, tmpdir):
|
def test_function_equality(self, testdir):
|
||||||
def func1():
|
def func1():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -492,7 +492,7 @@ class TestFunction:
|
||||||
)
|
)
|
||||||
assert "foo" in keywords[1] and "bar" in keywords[1] and "baz" in keywords[1]
|
assert "foo" in keywords[1] and "bar" in keywords[1] and "baz" in keywords[1]
|
||||||
|
|
||||||
def test_function_equality_with_callspec(self, testdir, tmpdir):
|
def test_function_equality_with_callspec(self, testdir):
|
||||||
items = testdir.getitems(
|
items = testdir.getitems(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -509,11 +509,11 @@ class TestFunction:
|
||||||
config = item.config
|
config = item.config
|
||||||
|
|
||||||
class MyPlugin1:
|
class MyPlugin1:
|
||||||
def pytest_pyfunc_call(self, pyfuncitem):
|
def pytest_pyfunc_call(self):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
class MyPlugin2:
|
class MyPlugin2:
|
||||||
def pytest_pyfunc_call(self, pyfuncitem):
|
def pytest_pyfunc_call(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
config.pluginmanager.register(MyPlugin1())
|
config.pluginmanager.register(MyPlugin1())
|
||||||
|
@ -1015,7 +1015,7 @@ class TestTracebackCutting:
|
||||||
|
|
||||||
|
|
||||||
class TestReportInfo:
|
class TestReportInfo:
|
||||||
def test_itemreport_reportinfo(self, testdir, linecomp):
|
def test_itemreport_reportinfo(self, testdir):
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
|
@ -1716,138 +1716,6 @@ class TestAutouseDiscovery:
|
||||||
reprec.assertoutcome(passed=3)
|
reprec.assertoutcome(passed=3)
|
||||||
|
|
||||||
|
|
||||||
class TestMultiLevelAutouseAndParameterization:
|
|
||||||
def test_setup_and_teardown_order(self, testdir):
|
|
||||||
"""Tests that parameterized fixtures effect subsequent fixtures. (#6436)
|
|
||||||
|
|
||||||
If a fixture uses a parameterized fixture, or, for any other reason, is executed
|
|
||||||
after the parameterized fixture in the fixture stack, then it should be affected
|
|
||||||
by the parameterization, and as a result, should be torn down before the
|
|
||||||
parameterized fixture, every time the parameterized fixture is torn down. This
|
|
||||||
should be the case even if autouse is involved and/or the linear order of
|
|
||||||
fixture execution isn't deterministic. In other words, before any fixture can be
|
|
||||||
torn down, every fixture that was executed after it must also be torn down.
|
|
||||||
"""
|
|
||||||
testdir.makepyfile(
|
|
||||||
test_auto="""
|
|
||||||
import pytest
|
|
||||||
def f(param):
|
|
||||||
return param
|
|
||||||
@pytest.fixture(scope="session", autouse=True)
|
|
||||||
def s_fix(request):
|
|
||||||
yield
|
|
||||||
@pytest.fixture(scope="package", params=["p1", "p2"], ids=f, autouse=True)
|
|
||||||
def p_fix(request):
|
|
||||||
yield
|
|
||||||
@pytest.fixture(scope="module", params=["m1", "m2"], ids=f, autouse=True)
|
|
||||||
def m_fix(request):
|
|
||||||
yield
|
|
||||||
@pytest.fixture(scope="class", autouse=True)
|
|
||||||
def another_c_fix(m_fix):
|
|
||||||
yield
|
|
||||||
@pytest.fixture(scope="class")
|
|
||||||
def c_fix():
|
|
||||||
yield
|
|
||||||
@pytest.fixture(scope="function", params=["f1", "f2"], ids=f, autouse=True)
|
|
||||||
def f_fix(request):
|
|
||||||
yield
|
|
||||||
class TestFixtures:
|
|
||||||
def test_a(self, c_fix):
|
|
||||||
pass
|
|
||||||
def test_b(self, c_fix):
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
result = testdir.runpytest("--setup-plan")
|
|
||||||
test_fixtures_used = (
|
|
||||||
"(fixtures used: another_c_fix, c_fix, f_fix, m_fix, p_fix, request, s_fix)"
|
|
||||||
)
|
|
||||||
result.stdout.fnmatch_lines(
|
|
||||||
"""
|
|
||||||
SETUP S s_fix
|
|
||||||
SETUP P p_fix[p1]
|
|
||||||
SETUP M m_fix[m1]
|
|
||||||
SETUP C another_c_fix (fixtures used: m_fix)
|
|
||||||
SETUP C c_fix
|
|
||||||
SETUP F f_fix[f1]
|
|
||||||
test_auto.py::TestFixtures::test_a[p1-m1-f1] {0}
|
|
||||||
TEARDOWN F f_fix[f1]
|
|
||||||
SETUP F f_fix[f2]
|
|
||||||
test_auto.py::TestFixtures::test_a[p1-m1-f2] {0}
|
|
||||||
TEARDOWN F f_fix[f2]
|
|
||||||
SETUP F f_fix[f1]
|
|
||||||
test_auto.py::TestFixtures::test_b[p1-m1-f1] {0}
|
|
||||||
TEARDOWN F f_fix[f1]
|
|
||||||
SETUP F f_fix[f2]
|
|
||||||
test_auto.py::TestFixtures::test_b[p1-m1-f2] {0}
|
|
||||||
TEARDOWN F f_fix[f2]
|
|
||||||
TEARDOWN C c_fix
|
|
||||||
TEARDOWN C another_c_fix
|
|
||||||
TEARDOWN M m_fix[m1]
|
|
||||||
SETUP M m_fix[m2]
|
|
||||||
SETUP C another_c_fix (fixtures used: m_fix)
|
|
||||||
SETUP C c_fix
|
|
||||||
SETUP F f_fix[f1]
|
|
||||||
test_auto.py::TestFixtures::test_a[p1-m2-f1] {0}
|
|
||||||
TEARDOWN F f_fix[f1]
|
|
||||||
SETUP F f_fix[f2]
|
|
||||||
test_auto.py::TestFixtures::test_a[p1-m2-f2] {0}
|
|
||||||
TEARDOWN F f_fix[f2]
|
|
||||||
SETUP F f_fix[f1]
|
|
||||||
test_auto.py::TestFixtures::test_b[p1-m2-f1] {0}
|
|
||||||
TEARDOWN F f_fix[f1]
|
|
||||||
SETUP F f_fix[f2]
|
|
||||||
test_auto.py::TestFixtures::test_b[p1-m2-f2] {0}
|
|
||||||
TEARDOWN F f_fix[f2]
|
|
||||||
TEARDOWN C c_fix
|
|
||||||
TEARDOWN C another_c_fix
|
|
||||||
TEARDOWN M m_fix[m2]
|
|
||||||
TEARDOWN P p_fix[p1]
|
|
||||||
SETUP P p_fix[p2]
|
|
||||||
SETUP M m_fix[m1]
|
|
||||||
SETUP C another_c_fix (fixtures used: m_fix)
|
|
||||||
SETUP C c_fix
|
|
||||||
SETUP F f_fix[f1]
|
|
||||||
test_auto.py::TestFixtures::test_a[p2-m1-f1] {0}
|
|
||||||
TEARDOWN F f_fix[f1]
|
|
||||||
SETUP F f_fix[f2]
|
|
||||||
test_auto.py::TestFixtures::test_a[p2-m1-f2] {0}
|
|
||||||
TEARDOWN F f_fix[f2]
|
|
||||||
SETUP F f_fix[f1]
|
|
||||||
test_auto.py::TestFixtures::test_b[p2-m1-f1] {0}
|
|
||||||
TEARDOWN F f_fix[f1]
|
|
||||||
SETUP F f_fix[f2]
|
|
||||||
test_auto.py::TestFixtures::test_b[p2-m1-f2] {0}
|
|
||||||
TEARDOWN F f_fix[f2]
|
|
||||||
TEARDOWN C c_fix
|
|
||||||
TEARDOWN C another_c_fix
|
|
||||||
TEARDOWN M m_fix[m1]
|
|
||||||
SETUP M m_fix[m2]
|
|
||||||
SETUP C another_c_fix (fixtures used: m_fix)
|
|
||||||
SETUP C c_fix
|
|
||||||
SETUP F f_fix[f1]
|
|
||||||
test_auto.py::TestFixtures::test_a[p2-m2-f1] {0}
|
|
||||||
TEARDOWN F f_fix[f1]
|
|
||||||
SETUP F f_fix[f2]
|
|
||||||
test_auto.py::TestFixtures::test_a[p2-m2-f2] {0}
|
|
||||||
TEARDOWN F f_fix[f2]
|
|
||||||
SETUP F f_fix[f1]
|
|
||||||
test_auto.py::TestFixtures::test_b[p2-m2-f1] {0}
|
|
||||||
TEARDOWN F f_fix[f1]
|
|
||||||
SETUP F f_fix[f2]
|
|
||||||
test_auto.py::TestFixtures::test_b[p2-m2-f2] {0}
|
|
||||||
TEARDOWN F f_fix[f2]
|
|
||||||
TEARDOWN C c_fix
|
|
||||||
TEARDOWN C another_c_fix
|
|
||||||
TEARDOWN M m_fix[m2]
|
|
||||||
TEARDOWN P p_fix[p2]
|
|
||||||
TEARDOWN S s_fix
|
|
||||||
""".format(
|
|
||||||
test_fixtures_used
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestAutouseManagement:
|
class TestAutouseManagement:
|
||||||
def test_autouse_conftest_mid_directory(self, testdir):
|
def test_autouse_conftest_mid_directory(self, testdir):
|
||||||
pkgdir = testdir.mkpydir("xyz123")
|
pkgdir = testdir.mkpydir("xyz123")
|
||||||
|
@ -4238,7 +4106,7 @@ def test_fixture_named_request(testdir):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_fixture_duplicated_arguments(testdir):
|
def test_fixture_duplicated_arguments():
|
||||||
"""Raise error if there are positional and keyword arguments for the same parameter (#1682)."""
|
"""Raise error if there are positional and keyword arguments for the same parameter (#1682)."""
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
|
|
||||||
|
@ -4253,7 +4121,7 @@ def test_fixture_duplicated_arguments(testdir):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_fixture_with_positionals(testdir):
|
def test_fixture_with_positionals():
|
||||||
"""Raise warning, but the positionals should still works (#1682)."""
|
"""Raise warning, but the positionals should still works (#1682)."""
|
||||||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ class TestMetafunc:
|
||||||
definition = DefinitionMock._create(func)
|
definition = DefinitionMock._create(func)
|
||||||
return python.Metafunc(definition, fixtureinfo, config)
|
return python.Metafunc(definition, fixtureinfo, config)
|
||||||
|
|
||||||
def test_no_funcargs(self, testdir):
|
def test_no_funcargs(self):
|
||||||
def function():
|
def function():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ class TestMetafunc:
|
||||||
):
|
):
|
||||||
metafunc.parametrize("x", [1, 2, 3], ids=gen())
|
metafunc.parametrize("x", [1, 2, 3], ids=gen())
|
||||||
|
|
||||||
def test_parametrize_bad_scope(self, testdir):
|
def test_parametrize_bad_scope(self):
|
||||||
def func(x):
|
def func(x):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ class TestMetafunc:
|
||||||
ids = [x.id for x in metafunc._calls]
|
ids = [x.id for x in metafunc._calls]
|
||||||
assert ids == ["basic", "advanced"]
|
assert ids == ["basic", "advanced"]
|
||||||
|
|
||||||
def test_parametrize_with_wrong_number_of_ids(self, testdir):
|
def test_parametrize_with_wrong_number_of_ids(self):
|
||||||
def func(x, y):
|
def func(x, y):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -712,7 +712,7 @@ class TestMetafunc:
|
||||||
result = testdir.runpytest("-v")
|
result = testdir.runpytest("-v")
|
||||||
result.stdout.fnmatch_lines(["*test_simple*a-b*", "*1 passed*"])
|
result.stdout.fnmatch_lines(["*test_simple*a-b*", "*1 passed*"])
|
||||||
|
|
||||||
def test_parametrize_indirect_list_error(self, testdir):
|
def test_parametrize_indirect_list_error(self):
|
||||||
"""#714"""
|
"""#714"""
|
||||||
|
|
||||||
def func(x, y):
|
def func(x, y):
|
||||||
|
|
|
@ -1299,7 +1299,7 @@ def test_AssertionError_message(testdir):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_diff_newline_at_end(monkeypatch, testdir):
|
def test_diff_newline_at_end(testdir):
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
r"""
|
r"""
|
||||||
def test_diff():
|
def test_diff():
|
||||||
|
@ -1354,7 +1354,7 @@ def test_assert_indirect_tuple_no_warning(testdir):
|
||||||
assert "WR1" not in output
|
assert "WR1" not in output
|
||||||
|
|
||||||
|
|
||||||
def test_assert_with_unicode(monkeypatch, testdir):
|
def test_assert_with_unicode(testdir):
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""\
|
"""\
|
||||||
def test_unicode():
|
def test_unicode():
|
||||||
|
|
|
@ -268,9 +268,11 @@ class TestLastFailed:
|
||||||
"*1 failed*2 passed*",
|
"*1 failed*2 passed*",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
testdir.tmpdir.join(".pytest_cache").mkdir(".git")
|
||||||
result = testdir.runpytest(str(p), "--lf", "--cache-clear")
|
result = testdir.runpytest(str(p), "--lf", "--cache-clear")
|
||||||
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
|
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
|
||||||
assert testdir.tmpdir.join(".pytest_cache", "README.md").isfile()
|
assert testdir.tmpdir.join(".pytest_cache", "README.md").isfile()
|
||||||
|
assert testdir.tmpdir.join(".pytest_cache", ".git").isdir()
|
||||||
|
|
||||||
# Run this again to make sure clear-cache is robust
|
# Run this again to make sure clear-cache is robust
|
||||||
if os.path.isdir(".pytest_cache"):
|
if os.path.isdir(".pytest_cache"):
|
||||||
|
|
|
@ -960,7 +960,7 @@ class TestFDCapture:
|
||||||
cap.done()
|
cap.done()
|
||||||
assert s == "hello\n"
|
assert s == "hello\n"
|
||||||
|
|
||||||
def test_stdin(self, tmpfile):
|
def test_stdin(self):
|
||||||
cap = capture.FDCapture(0)
|
cap = capture.FDCapture(0)
|
||||||
cap.start()
|
cap.start()
|
||||||
x = os.read(0, 100).strip()
|
x = os.read(0, 100).strip()
|
||||||
|
@ -981,7 +981,7 @@ class TestFDCapture:
|
||||||
stmp = stmp_file.read()
|
stmp = stmp_file.read()
|
||||||
assert stmp == data2
|
assert stmp == data2
|
||||||
|
|
||||||
def test_simple_resume_suspend(self, tmpfile):
|
def test_simple_resume_suspend(self):
|
||||||
with saved_fd(1):
|
with saved_fd(1):
|
||||||
cap = capture.FDCapture(1)
|
cap = capture.FDCapture(1)
|
||||||
cap.start()
|
cap.start()
|
||||||
|
|
|
@ -243,7 +243,7 @@ class TestCollectPluginHookRelay:
|
||||||
wascalled = []
|
wascalled = []
|
||||||
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def pytest_collect_file(self, path, parent):
|
def pytest_collect_file(self, path):
|
||||||
if not path.basename.startswith("."):
|
if not path.basename.startswith("."):
|
||||||
# Ignore hidden files, e.g. .testmondata.
|
# Ignore hidden files, e.g. .testmondata.
|
||||||
wascalled.append(path)
|
wascalled.append(path)
|
||||||
|
@ -257,7 +257,7 @@ class TestCollectPluginHookRelay:
|
||||||
wascalled = []
|
wascalled = []
|
||||||
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def pytest_collect_directory(self, path, parent):
|
def pytest_collect_directory(self, path):
|
||||||
wascalled.append(path.basename)
|
wascalled.append(path.basename)
|
||||||
|
|
||||||
testdir.mkdir("hello")
|
testdir.mkdir("hello")
|
||||||
|
@ -1210,7 +1210,7 @@ def test_collect_symlink_out_of_tree(testdir):
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_collectignore_via_conftest(testdir, monkeypatch):
|
def test_collectignore_via_conftest(testdir):
|
||||||
"""collect_ignore in parent conftest skips importing child (issue #4592)."""
|
"""collect_ignore in parent conftest skips importing child (issue #4592)."""
|
||||||
tests = testdir.mkpydir("tests")
|
tests = testdir.mkpydir("tests")
|
||||||
tests.ensure("conftest.py").write("collect_ignore = ['ignore_me']")
|
tests.ensure("conftest.py").write("collect_ignore = ['ignore_me']")
|
||||||
|
|
|
@ -32,7 +32,7 @@ class TestParseIni:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
rootdir, inifile, cfg = getcfg([sub])
|
_, _, cfg = getcfg([sub])
|
||||||
assert cfg["name"] == "value"
|
assert cfg["name"] == "value"
|
||||||
config = testdir.parseconfigure(sub)
|
config = testdir.parseconfigure(sub)
|
||||||
assert config.inicfg["name"] == "value"
|
assert config.inicfg["name"] == "value"
|
||||||
|
@ -441,8 +441,6 @@ class TestConfigAPI:
|
||||||
|
|
||||||
class TestConfigFromdictargs:
|
class TestConfigFromdictargs:
|
||||||
def test_basic_behavior(self, _sys_snapshot):
|
def test_basic_behavior(self, _sys_snapshot):
|
||||||
from _pytest.config import Config
|
|
||||||
|
|
||||||
option_dict = {"verbose": 444, "foo": "bar", "capture": "no"}
|
option_dict = {"verbose": 444, "foo": "bar", "capture": "no"}
|
||||||
args = ["a", "b"]
|
args = ["a", "b"]
|
||||||
|
|
||||||
|
@ -456,8 +454,6 @@ class TestConfigFromdictargs:
|
||||||
|
|
||||||
def test_invocation_params_args(self, _sys_snapshot):
|
def test_invocation_params_args(self, _sys_snapshot):
|
||||||
"""Show that fromdictargs can handle args in their "orig" format"""
|
"""Show that fromdictargs can handle args in their "orig" format"""
|
||||||
from _pytest.config import Config
|
|
||||||
|
|
||||||
option_dict = {}
|
option_dict = {}
|
||||||
args = ["-vvvv", "-s", "a", "b"]
|
args = ["-vvvv", "-s", "a", "b"]
|
||||||
|
|
||||||
|
@ -477,8 +473,6 @@ class TestConfigFromdictargs:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
from _pytest.config import Config
|
|
||||||
|
|
||||||
inifile = "../../foo/bar.ini"
|
inifile = "../../foo/bar.ini"
|
||||||
option_dict = {"inifilename": inifile, "capture": "no"}
|
option_dict = {"inifilename": inifile, "capture": "no"}
|
||||||
|
|
||||||
|
@ -771,23 +765,23 @@ def test_notify_exception(testdir, capfd):
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
raise ValueError(1)
|
raise ValueError(1)
|
||||||
config.notify_exception(excinfo, config.option)
|
config.notify_exception(excinfo, config.option)
|
||||||
out, err = capfd.readouterr()
|
_, err = capfd.readouterr()
|
||||||
assert "ValueError" in err
|
assert "ValueError" in err
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
def pytest_internalerror(self, excrepr):
|
def pytest_internalerror(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
config.pluginmanager.register(A())
|
config.pluginmanager.register(A())
|
||||||
config.notify_exception(excinfo, config.option)
|
config.notify_exception(excinfo, config.option)
|
||||||
out, err = capfd.readouterr()
|
_, err = capfd.readouterr()
|
||||||
assert not err
|
assert not err
|
||||||
|
|
||||||
config = testdir.parseconfig("-p", "no:terminal")
|
config = testdir.parseconfig("-p", "no:terminal")
|
||||||
with pytest.raises(ValueError) as excinfo:
|
with pytest.raises(ValueError) as excinfo:
|
||||||
raise ValueError(1)
|
raise ValueError(1)
|
||||||
config.notify_exception(excinfo, config.option)
|
config.notify_exception(excinfo, config.option)
|
||||||
out, err = capfd.readouterr()
|
_, err = capfd.readouterr()
|
||||||
assert "ValueError" in err
|
assert "ValueError" in err
|
||||||
|
|
||||||
|
|
||||||
|
@ -797,7 +791,7 @@ def test_no_terminal_discovery_error(testdir):
|
||||||
assert result.ret == ExitCode.INTERRUPTED
|
assert result.ret == ExitCode.INTERRUPTED
|
||||||
|
|
||||||
|
|
||||||
def test_load_initial_conftest_last_ordering(testdir, _config_for_test):
|
def test_load_initial_conftest_last_ordering(_config_for_test):
|
||||||
pm = _config_for_test.pluginmanager
|
pm = _config_for_test.pluginmanager
|
||||||
|
|
||||||
class My:
|
class My:
|
||||||
|
@ -866,21 +860,21 @@ class TestRootdir:
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
b = a.mkdir("b")
|
b = a.mkdir("b")
|
||||||
for args in ([tmpdir], [a], [b]):
|
for args in ([tmpdir], [a], [b]):
|
||||||
rootdir, inifile, inicfg = determine_setup(None, args)
|
rootdir, parsed_inifile, _ = determine_setup(None, args)
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile == inifile
|
assert parsed_inifile == inifile
|
||||||
rootdir, inifile, inicfg = determine_setup(None, [b, a])
|
rootdir, parsed_inifile, _ = determine_setup(None, [b, a])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile == inifile
|
assert parsed_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) -> None:
|
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)
|
||||||
rootdir, inifile, inicfg = determine_setup(None, [a])
|
rootdir, parsed_inifile, _ = determine_setup(None, [a])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile == inifile
|
assert parsed_inifile == inifile
|
||||||
|
|
||||||
def test_setuppy_fallback(self, tmpdir) -> None:
|
def test_setuppy_fallback(self, tmpdir) -> None:
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
|
@ -900,7 +894,7 @@ class TestRootdir:
|
||||||
|
|
||||||
def test_with_specific_inifile(self, tmpdir) -> None:
|
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, _, _ = determine_setup(inifile, [tmpdir])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
|
|
||||||
|
|
||||||
|
@ -1043,7 +1037,7 @@ class TestOverrideIniArgs:
|
||||||
monkeypatch.chdir(str(tmpdir))
|
monkeypatch.chdir(str(tmpdir))
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
b = tmpdir.mkdir("b")
|
b = tmpdir.mkdir("b")
|
||||||
rootdir, inifile, inicfg = determine_setup(None, [a, b])
|
rootdir, inifile, _ = determine_setup(None, [a, b])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
|
|
||||||
|
@ -1051,14 +1045,14 @@ class TestOverrideIniArgs:
|
||||||
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")
|
||||||
rootdir, parsed_inifile, inicfg = determine_setup(None, [a, b])
|
rootdir, parsed_inifile, _ = determine_setup(None, [a, b])
|
||||||
assert rootdir == a
|
assert rootdir == a
|
||||||
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) -> None:
|
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, _ = determine_setup(None, dirs)
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
|
|
||||||
|
@ -1066,7 +1060,7 @@ class TestOverrideIniArgs:
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
a.ensure("exist")
|
a.ensure("exist")
|
||||||
with tmpdir.as_cwd():
|
with tmpdir.as_cwd():
|
||||||
rootdir, inifile, inicfg = determine_setup(None, ["a/exist"])
|
rootdir, inifile, _ = determine_setup(None, ["a/exist"])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
assert inifile is None
|
assert inifile is None
|
||||||
|
|
||||||
|
@ -1111,7 +1105,7 @@ class TestOverrideIniArgs:
|
||||||
config._preparse(["-o", "cache_dir=/cache", "/some/test/path"])
|
config._preparse(["-o", "cache_dir=/cache", "/some/test/path"])
|
||||||
assert config._override_ini == ["cache_dir=/cache"]
|
assert config._override_ini == ["cache_dir=/cache"]
|
||||||
|
|
||||||
def test_multiple_override_ini_options(self, testdir, request):
|
def test_multiple_override_ini_options(self, testdir):
|
||||||
"""Ensure a file path following a '-o' option does not generate an error (#3103)"""
|
"""Ensure a file path following a '-o' option does not generate an error (#3103)"""
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
**{
|
**{
|
||||||
|
@ -1201,7 +1195,7 @@ def test_help_and_version_after_argument_error(testdir):
|
||||||
assert result.ret == ExitCode.USAGE_ERROR
|
assert result.ret == ExitCode.USAGE_ERROR
|
||||||
|
|
||||||
|
|
||||||
def test_help_formatter_uses_py_get_terminal_width(testdir, monkeypatch):
|
def test_help_formatter_uses_py_get_terminal_width(monkeypatch):
|
||||||
from _pytest.config.argparsing import DropShorterLongHelpFormatter
|
from _pytest.config.argparsing import DropShorterLongHelpFormatter
|
||||||
|
|
||||||
monkeypatch.setenv("COLUMNS", "90")
|
monkeypatch.setenv("COLUMNS", "90")
|
||||||
|
|
|
@ -357,7 +357,7 @@ def test_conftest_import_order(testdir, monkeypatch):
|
||||||
assert conftest._getconftestmodules(sub) == [ct1, ct2]
|
assert conftest._getconftestmodules(sub) == [ct1, ct2]
|
||||||
|
|
||||||
|
|
||||||
def test_fixture_dependency(testdir, monkeypatch):
|
def test_fixture_dependency(testdir):
|
||||||
ct1 = testdir.makeconftest("")
|
ct1 = testdir.makeconftest("")
|
||||||
ct1 = testdir.makepyfile("__init__.py")
|
ct1 = testdir.makepyfile("__init__.py")
|
||||||
ct1.write("")
|
ct1.write("")
|
||||||
|
|
|
@ -463,7 +463,7 @@ class TestPDB:
|
||||||
child.read()
|
child.read()
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
def test_pdb_interaction_doctest(self, testdir, monkeypatch):
|
def test_pdb_interaction_doctest(self, testdir):
|
||||||
p1 = testdir.makepyfile(
|
p1 = testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
def function_1():
|
def function_1():
|
||||||
|
@ -489,7 +489,7 @@ class TestPDB:
|
||||||
assert "1 failed" in rest
|
assert "1 failed" in rest
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
def test_doctest_set_trace_quit(self, testdir, monkeypatch):
|
def test_doctest_set_trace_quit(self, testdir):
|
||||||
p1 = testdir.makepyfile(
|
p1 = testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
def function_1():
|
def function_1():
|
||||||
|
@ -833,7 +833,7 @@ class TestPDB:
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_pdb_validate_usepdb_cls(self, testdir):
|
def test_pdb_validate_usepdb_cls(self):
|
||||||
assert _validate_usepdb_cls("os.path:dirname.__name__") == (
|
assert _validate_usepdb_cls("os.path:dirname.__name__") == (
|
||||||
"os.path",
|
"os.path",
|
||||||
"dirname.__name__",
|
"dirname.__name__",
|
||||||
|
|
|
@ -82,7 +82,7 @@ def test_timeout(testdir, enabled):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("hook_name", ["pytest_enter_pdb", "pytest_exception_interact"])
|
@pytest.mark.parametrize("hook_name", ["pytest_enter_pdb", "pytest_exception_interact"])
|
||||||
def test_cancel_timeout_on_hook(monkeypatch, pytestconfig, hook_name):
|
def test_cancel_timeout_on_hook(monkeypatch, hook_name):
|
||||||
"""Make sure that we are cancelling any scheduled traceback dumping due
|
"""Make sure that we are cancelling any scheduled traceback dumping due
|
||||||
to timeout before entering pdb (pytest-dev/pytest-faulthandler#12) or any other interactive
|
to timeout before entering pdb (pytest-dev/pytest-faulthandler#12) or any other interactive
|
||||||
exception (pytest-dev/pytest-faulthandler#14).
|
exception (pytest-dev/pytest-faulthandler#14).
|
||||||
|
|
|
@ -57,7 +57,7 @@ def test_traceconfig(testdir):
|
||||||
result.stdout.fnmatch_lines(["*using*pytest*py*", "*active plugins*"])
|
result.stdout.fnmatch_lines(["*using*pytest*py*", "*active plugins*"])
|
||||||
|
|
||||||
|
|
||||||
def test_debug(testdir, monkeypatch):
|
def test_debug(testdir):
|
||||||
result = testdir.runpytest_subprocess("--debug")
|
result = testdir.runpytest_subprocess("--debug")
|
||||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||||
p = testdir.tmpdir.join("pytestdebug.log")
|
p = testdir.tmpdir.join("pytestdebug.log")
|
||||||
|
|
|
@ -41,7 +41,7 @@ class TestMark:
|
||||||
mark._some_name
|
mark._some_name
|
||||||
|
|
||||||
|
|
||||||
def test_marked_class_run_twice(testdir, request):
|
def test_marked_class_run_twice(testdir):
|
||||||
"""Test fails file is run twice that contains marked class.
|
"""Test fails file is run twice that contains marked class.
|
||||||
See issue#683.
|
See issue#683.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -375,3 +375,17 @@ def test_skip_test_with_unicode(testdir):
|
||||||
)
|
)
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(["* 1 skipped *"])
|
result.stdout.fnmatch_lines(["* 1 skipped *"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_issue_6517(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
from nose.tools import raises
|
||||||
|
|
||||||
|
@raises(RuntimeError)
|
||||||
|
def test_fail_without_tcp():
|
||||||
|
raise RuntimeError
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(["* 1 passed *"])
|
||||||
|
|
|
@ -251,14 +251,14 @@ class TestParser:
|
||||||
assert args.func_arg is False
|
assert args.func_arg is False
|
||||||
assert args.file_or_dir == ["abcd"]
|
assert args.file_or_dir == ["abcd"]
|
||||||
|
|
||||||
def test_drop_short_help0(self, parser: parseopt.Parser, capsys) -> None:
|
def test_drop_short_help0(self, parser: parseopt.Parser) -> None:
|
||||||
parser.addoption("--func-args", "--doit", help="foo", action="store_true")
|
parser.addoption("--func-args", "--doit", help="foo", action="store_true")
|
||||||
parser.parse([])
|
parser.parse([])
|
||||||
help = parser.optparser.format_help()
|
help = parser.optparser.format_help()
|
||||||
assert "--func-args, --doit foo" in help
|
assert "--func-args, --doit foo" in help
|
||||||
|
|
||||||
# testing would be more helpful with all help generated
|
# testing would be more helpful with all help generated
|
||||||
def test_drop_short_help1(self, parser: parseopt.Parser, capsys) -> None:
|
def test_drop_short_help1(self, parser: parseopt.Parser) -> None:
|
||||||
group = parser.getgroup("general")
|
group = parser.getgroup("general")
|
||||||
group.addoption("--doit", "--func-args", action="store_true", help="foo")
|
group.addoption("--doit", "--func-args", action="store_true", help="foo")
|
||||||
group._addoption(
|
group._addoption(
|
||||||
|
|
|
@ -71,7 +71,7 @@ class TestPytestPluginInteractions:
|
||||||
values = []
|
values = []
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
def pytest_configure(self, config):
|
def pytest_configure(self):
|
||||||
values.append(self)
|
values.append(self)
|
||||||
|
|
||||||
config.pluginmanager.register(A())
|
config.pluginmanager.register(A())
|
||||||
|
|
|
@ -2,6 +2,7 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from typing import List
|
||||||
|
|
||||||
import py.path
|
import py.path
|
||||||
|
|
||||||
|
@ -9,6 +10,7 @@ import _pytest.pytester as pytester
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.config import PytestPluginManager
|
from _pytest.config import PytestPluginManager
|
||||||
from _pytest.main import ExitCode
|
from _pytest.main import ExitCode
|
||||||
|
from _pytest.outcomes import Failed
|
||||||
from _pytest.pytester import CwdSnapshot
|
from _pytest.pytester import CwdSnapshot
|
||||||
from _pytest.pytester import HookRecorder
|
from _pytest.pytester import HookRecorder
|
||||||
from _pytest.pytester import LineMatcher
|
from _pytest.pytester import LineMatcher
|
||||||
|
@ -16,7 +18,7 @@ from _pytest.pytester import SysModulesSnapshot
|
||||||
from _pytest.pytester import SysPathsSnapshot
|
from _pytest.pytester import SysPathsSnapshot
|
||||||
|
|
||||||
|
|
||||||
def test_make_hook_recorder(testdir):
|
def test_make_hook_recorder(testdir) -> None:
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
recorder = testdir.make_hook_recorder(item.config.pluginmanager)
|
recorder = testdir.make_hook_recorder(item.config.pluginmanager)
|
||||||
assert not recorder.getfailures()
|
assert not recorder.getfailures()
|
||||||
|
@ -36,23 +38,23 @@ def test_make_hook_recorder(testdir):
|
||||||
failures = recorder.getfailures()
|
failures = recorder.getfailures()
|
||||||
assert failures == [rep]
|
assert failures == [rep]
|
||||||
|
|
||||||
class rep:
|
class rep2:
|
||||||
excinfo = None
|
excinfo = None
|
||||||
passed = False
|
passed = False
|
||||||
failed = False
|
failed = False
|
||||||
skipped = True
|
skipped = True
|
||||||
when = "call"
|
when = "call"
|
||||||
|
|
||||||
rep.passed = False
|
rep2.passed = False
|
||||||
rep.skipped = True
|
rep2.skipped = True
|
||||||
recorder.hook.pytest_runtest_logreport(report=rep)
|
recorder.hook.pytest_runtest_logreport(report=rep2)
|
||||||
|
|
||||||
modcol = testdir.getmodulecol("")
|
modcol = testdir.getmodulecol("")
|
||||||
rep = modcol.config.hook.pytest_make_collect_report(collector=modcol)
|
rep3 = modcol.config.hook.pytest_make_collect_report(collector=modcol)
|
||||||
rep.passed = False
|
rep3.passed = False
|
||||||
rep.failed = True
|
rep3.failed = True
|
||||||
rep.skipped = False
|
rep3.skipped = False
|
||||||
recorder.hook.pytest_collectreport(report=rep)
|
recorder.hook.pytest_collectreport(report=rep3)
|
||||||
|
|
||||||
passed, skipped, failed = recorder.listoutcomes()
|
passed, skipped, failed = recorder.listoutcomes()
|
||||||
assert not passed and skipped and failed
|
assert not passed and skipped and failed
|
||||||
|
@ -65,17 +67,17 @@ def test_make_hook_recorder(testdir):
|
||||||
|
|
||||||
recorder.unregister()
|
recorder.unregister()
|
||||||
recorder.clear()
|
recorder.clear()
|
||||||
recorder.hook.pytest_runtest_logreport(report=rep)
|
recorder.hook.pytest_runtest_logreport(report=rep3)
|
||||||
pytest.raises(ValueError, recorder.getfailures)
|
pytest.raises(ValueError, recorder.getfailures)
|
||||||
|
|
||||||
|
|
||||||
def test_parseconfig(testdir):
|
def test_parseconfig(testdir) -> None:
|
||||||
config1 = testdir.parseconfig()
|
config1 = testdir.parseconfig()
|
||||||
config2 = testdir.parseconfig()
|
config2 = testdir.parseconfig()
|
||||||
assert config2 is not config1
|
assert config2 is not config1
|
||||||
|
|
||||||
|
|
||||||
def test_testdir_runs_with_plugin(testdir):
|
def test_testdir_runs_with_plugin(testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
pytest_plugins = "pytester"
|
pytest_plugins = "pytester"
|
||||||
|
@ -87,7 +89,7 @@ def test_testdir_runs_with_plugin(testdir):
|
||||||
result.assert_outcomes(passed=1)
|
result.assert_outcomes(passed=1)
|
||||||
|
|
||||||
|
|
||||||
def test_runresult_assertion_on_xfail(testdir):
|
def test_runresult_assertion_on_xfail(testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -104,7 +106,7 @@ def test_runresult_assertion_on_xfail(testdir):
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_runresult_assertion_on_xpassed(testdir):
|
def test_runresult_assertion_on_xpassed(testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -121,7 +123,7 @@ def test_runresult_assertion_on_xpassed(testdir):
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_xpassed_with_strict_is_considered_a_failure(testdir):
|
def test_xpassed_with_strict_is_considered_a_failure(testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -154,13 +156,13 @@ def make_holder():
|
||||||
def pytest_xyz_noarg():
|
def pytest_xyz_noarg():
|
||||||
"x"
|
"x"
|
||||||
|
|
||||||
apimod.pytest_xyz = pytest_xyz
|
apimod.pytest_xyz = pytest_xyz # type: ignore
|
||||||
apimod.pytest_xyz_noarg = pytest_xyz_noarg
|
apimod.pytest_xyz_noarg = pytest_xyz_noarg # type: ignore
|
||||||
return apiclass, apimod
|
return apiclass, apimod
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("holder", make_holder())
|
@pytest.mark.parametrize("holder", make_holder())
|
||||||
def test_hookrecorder_basic(holder):
|
def test_hookrecorder_basic(holder) -> None:
|
||||||
pm = PytestPluginManager()
|
pm = PytestPluginManager()
|
||||||
pm.add_hookspecs(holder)
|
pm.add_hookspecs(holder)
|
||||||
rec = HookRecorder(pm)
|
rec = HookRecorder(pm)
|
||||||
|
@ -168,17 +170,17 @@ def test_hookrecorder_basic(holder):
|
||||||
call = rec.popcall("pytest_xyz")
|
call = rec.popcall("pytest_xyz")
|
||||||
assert call.arg == 123
|
assert call.arg == 123
|
||||||
assert call._name == "pytest_xyz"
|
assert call._name == "pytest_xyz"
|
||||||
pytest.raises(pytest.fail.Exception, rec.popcall, "abc")
|
pytest.raises(Failed, rec.popcall, "abc")
|
||||||
pm.hook.pytest_xyz_noarg()
|
pm.hook.pytest_xyz_noarg()
|
||||||
call = rec.popcall("pytest_xyz_noarg")
|
call = rec.popcall("pytest_xyz_noarg")
|
||||||
assert call._name == "pytest_xyz_noarg"
|
assert call._name == "pytest_xyz_noarg"
|
||||||
|
|
||||||
|
|
||||||
def test_makepyfile_unicode(testdir):
|
def test_makepyfile_unicode(testdir) -> None:
|
||||||
testdir.makepyfile(chr(0xFFFD))
|
testdir.makepyfile(chr(0xFFFD))
|
||||||
|
|
||||||
|
|
||||||
def test_makepyfile_utf8(testdir):
|
def test_makepyfile_utf8(testdir) -> None:
|
||||||
"""Ensure makepyfile accepts utf-8 bytes as input (#2738)"""
|
"""Ensure makepyfile accepts utf-8 bytes as input (#2738)"""
|
||||||
utf8_contents = """
|
utf8_contents = """
|
||||||
def setup_function(function):
|
def setup_function(function):
|
||||||
|
@ -189,7 +191,7 @@ def test_makepyfile_utf8(testdir):
|
||||||
|
|
||||||
|
|
||||||
class TestInlineRunModulesCleanup:
|
class TestInlineRunModulesCleanup:
|
||||||
def test_inline_run_test_module_not_cleaned_up(self, testdir):
|
def test_inline_run_test_module_not_cleaned_up(self, testdir) -> None:
|
||||||
test_mod = testdir.makepyfile("def test_foo(): assert True")
|
test_mod = testdir.makepyfile("def test_foo(): assert True")
|
||||||
result = testdir.inline_run(str(test_mod))
|
result = testdir.inline_run(str(test_mod))
|
||||||
assert result.ret == ExitCode.OK
|
assert result.ret == ExitCode.OK
|
||||||
|
@ -200,9 +202,9 @@ class TestInlineRunModulesCleanup:
|
||||||
|
|
||||||
def spy_factory(self):
|
def spy_factory(self):
|
||||||
class SysModulesSnapshotSpy:
|
class SysModulesSnapshotSpy:
|
||||||
instances = []
|
instances = [] # type: List[SysModulesSnapshotSpy]
|
||||||
|
|
||||||
def __init__(self, preserve=None):
|
def __init__(self, preserve=None) -> None:
|
||||||
SysModulesSnapshotSpy.instances.append(self)
|
SysModulesSnapshotSpy.instances.append(self)
|
||||||
self._spy_restore_count = 0
|
self._spy_restore_count = 0
|
||||||
self._spy_preserve = preserve
|
self._spy_preserve = preserve
|
||||||
|
@ -216,7 +218,7 @@ class TestInlineRunModulesCleanup:
|
||||||
|
|
||||||
def test_inline_run_taking_and_restoring_a_sys_modules_snapshot(
|
def test_inline_run_taking_and_restoring_a_sys_modules_snapshot(
|
||||||
self, testdir, monkeypatch
|
self, testdir, monkeypatch
|
||||||
):
|
) -> None:
|
||||||
spy_factory = self.spy_factory()
|
spy_factory = self.spy_factory()
|
||||||
monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory)
|
monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory)
|
||||||
testdir.syspathinsert()
|
testdir.syspathinsert()
|
||||||
|
@ -237,7 +239,7 @@ class TestInlineRunModulesCleanup:
|
||||||
|
|
||||||
def test_inline_run_sys_modules_snapshot_restore_preserving_modules(
|
def test_inline_run_sys_modules_snapshot_restore_preserving_modules(
|
||||||
self, testdir, monkeypatch
|
self, testdir, monkeypatch
|
||||||
):
|
) -> None:
|
||||||
spy_factory = self.spy_factory()
|
spy_factory = self.spy_factory()
|
||||||
monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory)
|
monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory)
|
||||||
test_mod = testdir.makepyfile("def test_foo(): pass")
|
test_mod = testdir.makepyfile("def test_foo(): pass")
|
||||||
|
@ -248,7 +250,7 @@ class TestInlineRunModulesCleanup:
|
||||||
assert spy._spy_preserve("zope.interface")
|
assert spy._spy_preserve("zope.interface")
|
||||||
assert spy._spy_preserve("zopelicious")
|
assert spy._spy_preserve("zopelicious")
|
||||||
|
|
||||||
def test_external_test_module_imports_not_cleaned_up(self, testdir):
|
def test_external_test_module_imports_not_cleaned_up(self, testdir) -> None:
|
||||||
testdir.syspathinsert()
|
testdir.syspathinsert()
|
||||||
testdir.makepyfile(imported="data = 'you son of a silly person'")
|
testdir.makepyfile(imported="data = 'you son of a silly person'")
|
||||||
import imported
|
import imported
|
||||||
|
@ -263,7 +265,7 @@ class TestInlineRunModulesCleanup:
|
||||||
assert imported.data == 42
|
assert imported.data == 42
|
||||||
|
|
||||||
|
|
||||||
def test_assert_outcomes_after_pytest_error(testdir):
|
def test_assert_outcomes_after_pytest_error(testdir) -> None:
|
||||||
testdir.makepyfile("def test_foo(): assert True")
|
testdir.makepyfile("def test_foo(): assert True")
|
||||||
|
|
||||||
result = testdir.runpytest("--unexpected-argument")
|
result = testdir.runpytest("--unexpected-argument")
|
||||||
|
@ -271,7 +273,7 @@ def test_assert_outcomes_after_pytest_error(testdir):
|
||||||
result.assert_outcomes(passed=0)
|
result.assert_outcomes(passed=0)
|
||||||
|
|
||||||
|
|
||||||
def test_cwd_snapshot(tmpdir):
|
def test_cwd_snapshot(tmpdir) -> None:
|
||||||
foo = tmpdir.ensure("foo", dir=1)
|
foo = tmpdir.ensure("foo", dir=1)
|
||||||
bar = tmpdir.ensure("bar", dir=1)
|
bar = tmpdir.ensure("bar", dir=1)
|
||||||
foo.chdir()
|
foo.chdir()
|
||||||
|
@ -285,16 +287,16 @@ def test_cwd_snapshot(tmpdir):
|
||||||
class TestSysModulesSnapshot:
|
class TestSysModulesSnapshot:
|
||||||
key = "my-test-module"
|
key = "my-test-module"
|
||||||
|
|
||||||
def test_remove_added(self):
|
def test_remove_added(self) -> None:
|
||||||
original = dict(sys.modules)
|
original = dict(sys.modules)
|
||||||
assert self.key not in sys.modules
|
assert self.key not in sys.modules
|
||||||
snapshot = SysModulesSnapshot()
|
snapshot = SysModulesSnapshot()
|
||||||
sys.modules[self.key] = "something"
|
sys.modules[self.key] = "something" # type: ignore
|
||||||
assert self.key in sys.modules
|
assert self.key in sys.modules
|
||||||
snapshot.restore()
|
snapshot.restore()
|
||||||
assert sys.modules == original
|
assert sys.modules == original
|
||||||
|
|
||||||
def test_add_removed(self, monkeypatch):
|
def test_add_removed(self, monkeypatch) -> None:
|
||||||
assert self.key not in sys.modules
|
assert self.key not in sys.modules
|
||||||
monkeypatch.setitem(sys.modules, self.key, "something")
|
monkeypatch.setitem(sys.modules, self.key, "something")
|
||||||
assert self.key in sys.modules
|
assert self.key in sys.modules
|
||||||
|
@ -305,17 +307,17 @@ class TestSysModulesSnapshot:
|
||||||
snapshot.restore()
|
snapshot.restore()
|
||||||
assert sys.modules == original
|
assert sys.modules == original
|
||||||
|
|
||||||
def test_restore_reloaded(self, monkeypatch):
|
def test_restore_reloaded(self, monkeypatch) -> None:
|
||||||
assert self.key not in sys.modules
|
assert self.key not in sys.modules
|
||||||
monkeypatch.setitem(sys.modules, self.key, "something")
|
monkeypatch.setitem(sys.modules, self.key, "something")
|
||||||
assert self.key in sys.modules
|
assert self.key in sys.modules
|
||||||
original = dict(sys.modules)
|
original = dict(sys.modules)
|
||||||
snapshot = SysModulesSnapshot()
|
snapshot = SysModulesSnapshot()
|
||||||
sys.modules[self.key] = "something else"
|
sys.modules[self.key] = "something else" # type: ignore
|
||||||
snapshot.restore()
|
snapshot.restore()
|
||||||
assert sys.modules == original
|
assert sys.modules == original
|
||||||
|
|
||||||
def test_preserve_modules(self, monkeypatch):
|
def test_preserve_modules(self, monkeypatch) -> None:
|
||||||
key = [self.key + str(i) for i in range(3)]
|
key = [self.key + str(i) for i in range(3)]
|
||||||
assert not any(k in sys.modules for k in key)
|
assert not any(k in sys.modules for k in key)
|
||||||
for i, k in enumerate(key):
|
for i, k in enumerate(key):
|
||||||
|
@ -326,17 +328,17 @@ class TestSysModulesSnapshot:
|
||||||
return name in (key[0], key[1], "some-other-key")
|
return name in (key[0], key[1], "some-other-key")
|
||||||
|
|
||||||
snapshot = SysModulesSnapshot(preserve=preserve)
|
snapshot = SysModulesSnapshot(preserve=preserve)
|
||||||
sys.modules[key[0]] = original[key[0]] = "something else0"
|
sys.modules[key[0]] = original[key[0]] = "something else0" # type: ignore
|
||||||
sys.modules[key[1]] = original[key[1]] = "something else1"
|
sys.modules[key[1]] = original[key[1]] = "something else1" # type: ignore
|
||||||
sys.modules[key[2]] = "something else2"
|
sys.modules[key[2]] = "something else2" # type: ignore
|
||||||
snapshot.restore()
|
snapshot.restore()
|
||||||
assert sys.modules == original
|
assert sys.modules == original
|
||||||
|
|
||||||
def test_preserve_container(self, monkeypatch):
|
def test_preserve_container(self, monkeypatch) -> None:
|
||||||
original = dict(sys.modules)
|
original = dict(sys.modules)
|
||||||
assert self.key not in original
|
assert self.key not in original
|
||||||
replacement = dict(sys.modules)
|
replacement = dict(sys.modules)
|
||||||
replacement[self.key] = "life of brian"
|
replacement[self.key] = "life of brian" # type: ignore
|
||||||
snapshot = SysModulesSnapshot()
|
snapshot = SysModulesSnapshot()
|
||||||
monkeypatch.setattr(sys, "modules", replacement)
|
monkeypatch.setattr(sys, "modules", replacement)
|
||||||
snapshot.restore()
|
snapshot.restore()
|
||||||
|
@ -349,10 +351,10 @@ class TestSysPathsSnapshot:
|
||||||
other_path = {"path": "meta_path", "meta_path": "path"}
|
other_path = {"path": "meta_path", "meta_path": "path"}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def path(n):
|
def path(n: int) -> str:
|
||||||
return "my-dirty-little-secret-" + str(n)
|
return "my-dirty-little-secret-" + str(n)
|
||||||
|
|
||||||
def test_restore(self, monkeypatch, path_type):
|
def test_restore(self, monkeypatch, path_type) -> None:
|
||||||
other_path_type = self.other_path[path_type]
|
other_path_type = self.other_path[path_type]
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
assert self.path(i) not in getattr(sys, path_type)
|
assert self.path(i) not in getattr(sys, path_type)
|
||||||
|
@ -375,12 +377,12 @@ class TestSysPathsSnapshot:
|
||||||
assert getattr(sys, path_type) == original
|
assert getattr(sys, path_type) == original
|
||||||
assert getattr(sys, other_path_type) == original_other
|
assert getattr(sys, other_path_type) == original_other
|
||||||
|
|
||||||
def test_preserve_container(self, monkeypatch, path_type):
|
def test_preserve_container(self, monkeypatch, path_type) -> None:
|
||||||
other_path_type = self.other_path[path_type]
|
other_path_type = self.other_path[path_type]
|
||||||
original_data = list(getattr(sys, path_type))
|
original_data = list(getattr(sys, path_type))
|
||||||
original_other = getattr(sys, other_path_type)
|
original_other = getattr(sys, other_path_type)
|
||||||
original_other_data = list(original_other)
|
original_other_data = list(original_other)
|
||||||
new = []
|
new = [] # type: List[object]
|
||||||
snapshot = SysPathsSnapshot()
|
snapshot = SysPathsSnapshot()
|
||||||
monkeypatch.setattr(sys, path_type, new)
|
monkeypatch.setattr(sys, path_type, new)
|
||||||
snapshot.restore()
|
snapshot.restore()
|
||||||
|
@ -390,7 +392,7 @@ class TestSysPathsSnapshot:
|
||||||
assert getattr(sys, other_path_type) == original_other_data
|
assert getattr(sys, other_path_type) == original_other_data
|
||||||
|
|
||||||
|
|
||||||
def test_testdir_subprocess(testdir):
|
def test_testdir_subprocess(testdir) -> None:
|
||||||
testfile = testdir.makepyfile("def test_one(): pass")
|
testfile = testdir.makepyfile("def test_one(): pass")
|
||||||
assert testdir.runpytest_subprocess(testfile).ret == 0
|
assert testdir.runpytest_subprocess(testfile).ret == 0
|
||||||
|
|
||||||
|
@ -416,17 +418,17 @@ def test_testdir_subprocess_via_runpytest_arg(testdir) -> None:
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_unicode_args(testdir):
|
def test_unicode_args(testdir) -> None:
|
||||||
result = testdir.runpytest("-k", "💩")
|
result = testdir.runpytest("-k", "💩")
|
||||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||||
|
|
||||||
|
|
||||||
def test_testdir_run_no_timeout(testdir):
|
def test_testdir_run_no_timeout(testdir) -> None:
|
||||||
testfile = testdir.makepyfile("def test_no_timeout(): pass")
|
testfile = testdir.makepyfile("def test_no_timeout(): pass")
|
||||||
assert testdir.runpytest_subprocess(testfile).ret == ExitCode.OK
|
assert testdir.runpytest_subprocess(testfile).ret == ExitCode.OK
|
||||||
|
|
||||||
|
|
||||||
def test_testdir_run_with_timeout(testdir):
|
def test_testdir_run_with_timeout(testdir) -> None:
|
||||||
testfile = testdir.makepyfile("def test_no_timeout(): pass")
|
testfile = testdir.makepyfile("def test_no_timeout(): pass")
|
||||||
|
|
||||||
timeout = 120
|
timeout = 120
|
||||||
|
@ -440,7 +442,7 @@ def test_testdir_run_with_timeout(testdir):
|
||||||
assert duration < timeout
|
assert duration < timeout
|
||||||
|
|
||||||
|
|
||||||
def test_testdir_run_timeout_expires(testdir):
|
def test_testdir_run_timeout_expires(testdir) -> None:
|
||||||
testfile = testdir.makepyfile(
|
testfile = testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
|
@ -452,7 +454,7 @@ def test_testdir_run_timeout_expires(testdir):
|
||||||
testdir.runpytest_subprocess(testfile, timeout=1)
|
testdir.runpytest_subprocess(testfile, timeout=1)
|
||||||
|
|
||||||
|
|
||||||
def test_linematcher_with_nonlist():
|
def test_linematcher_with_nonlist() -> None:
|
||||||
"""Test LineMatcher with regard to passing in a set (accidentally)."""
|
"""Test LineMatcher with regard to passing in a set (accidentally)."""
|
||||||
lm = LineMatcher([])
|
lm = LineMatcher([])
|
||||||
|
|
||||||
|
@ -467,10 +469,11 @@ def test_linematcher_with_nonlist():
|
||||||
assert lm._getlines(set()) == set()
|
assert lm._getlines(set()) == set()
|
||||||
|
|
||||||
|
|
||||||
def test_linematcher_match_failure():
|
def test_linematcher_match_failure() -> None:
|
||||||
lm = LineMatcher(["foo", "foo", "bar"])
|
lm = LineMatcher(["foo", "foo", "bar"])
|
||||||
with pytest.raises(pytest.fail.Exception) as e:
|
with pytest.raises(Failed) as e:
|
||||||
lm.fnmatch_lines(["foo", "f*", "baz"])
|
lm.fnmatch_lines(["foo", "f*", "baz"])
|
||||||
|
assert e.value.msg is not None
|
||||||
assert e.value.msg.splitlines() == [
|
assert e.value.msg.splitlines() == [
|
||||||
"exact match: 'foo'",
|
"exact match: 'foo'",
|
||||||
"fnmatch: 'f*'",
|
"fnmatch: 'f*'",
|
||||||
|
@ -481,8 +484,9 @@ def test_linematcher_match_failure():
|
||||||
]
|
]
|
||||||
|
|
||||||
lm = LineMatcher(["foo", "foo", "bar"])
|
lm = LineMatcher(["foo", "foo", "bar"])
|
||||||
with pytest.raises(pytest.fail.Exception) as e:
|
with pytest.raises(Failed) as e:
|
||||||
lm.re_match_lines(["foo", "^f.*", "baz"])
|
lm.re_match_lines(["foo", "^f.*", "baz"])
|
||||||
|
assert e.value.msg is not None
|
||||||
assert e.value.msg.splitlines() == [
|
assert e.value.msg.splitlines() == [
|
||||||
"exact match: 'foo'",
|
"exact match: 'foo'",
|
||||||
"re.match: '^f.*'",
|
"re.match: '^f.*'",
|
||||||
|
@ -494,7 +498,7 @@ def test_linematcher_match_failure():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"])
|
@pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"])
|
||||||
def test_no_matching(function):
|
def test_no_matching(function) -> None:
|
||||||
if function == "no_fnmatch_line":
|
if function == "no_fnmatch_line":
|
||||||
good_pattern = "*.py OK*"
|
good_pattern = "*.py OK*"
|
||||||
bad_pattern = "*X.py OK*"
|
bad_pattern = "*X.py OK*"
|
||||||
|
@ -515,7 +519,7 @@ def test_no_matching(function):
|
||||||
|
|
||||||
# check the function twice to ensure we don't accumulate the internal buffer
|
# check the function twice to ensure we don't accumulate the internal buffer
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
with pytest.raises(pytest.fail.Exception) as e:
|
with pytest.raises(Failed) as e:
|
||||||
func = getattr(lm, function)
|
func = getattr(lm, function)
|
||||||
func(good_pattern)
|
func(good_pattern)
|
||||||
obtained = str(e.value).splitlines()
|
obtained = str(e.value).splitlines()
|
||||||
|
@ -542,15 +546,15 @@ def test_no_matching(function):
|
||||||
func(bad_pattern) # bad pattern does not match any line: passes
|
func(bad_pattern) # bad pattern does not match any line: passes
|
||||||
|
|
||||||
|
|
||||||
def test_no_matching_after_match():
|
def test_no_matching_after_match() -> None:
|
||||||
lm = LineMatcher(["1", "2", "3"])
|
lm = LineMatcher(["1", "2", "3"])
|
||||||
lm.fnmatch_lines(["1", "3"])
|
lm.fnmatch_lines(["1", "3"])
|
||||||
with pytest.raises(pytest.fail.Exception) as e:
|
with pytest.raises(Failed) as e:
|
||||||
lm.no_fnmatch_line("*")
|
lm.no_fnmatch_line("*")
|
||||||
assert str(e.value).splitlines() == ["fnmatch: '*'", " with: '1'"]
|
assert str(e.value).splitlines() == ["fnmatch: '*'", " with: '1'"]
|
||||||
|
|
||||||
|
|
||||||
def test_pytester_addopts_before_testdir(request, monkeypatch):
|
def test_pytester_addopts_before_testdir(request, monkeypatch) -> None:
|
||||||
orig = os.environ.get("PYTEST_ADDOPTS", None)
|
orig = os.environ.get("PYTEST_ADDOPTS", None)
|
||||||
monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused")
|
monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused")
|
||||||
testdir = request.getfixturevalue("testdir")
|
testdir = request.getfixturevalue("testdir")
|
||||||
|
@ -561,7 +565,7 @@ def test_pytester_addopts_before_testdir(request, monkeypatch):
|
||||||
assert os.environ.get("PYTEST_ADDOPTS") == orig
|
assert os.environ.get("PYTEST_ADDOPTS") == orig
|
||||||
|
|
||||||
|
|
||||||
def test_run_stdin(testdir):
|
def test_run_stdin(testdir) -> None:
|
||||||
with pytest.raises(testdir.TimeoutExpired):
|
with pytest.raises(testdir.TimeoutExpired):
|
||||||
testdir.run(
|
testdir.run(
|
||||||
sys.executable,
|
sys.executable,
|
||||||
|
@ -591,7 +595,7 @@ def test_run_stdin(testdir):
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_popen_stdin_pipe(testdir):
|
def test_popen_stdin_pipe(testdir) -> None:
|
||||||
proc = testdir.popen(
|
proc = testdir.popen(
|
||||||
[sys.executable, "-c", "import sys; print(sys.stdin.read())"],
|
[sys.executable, "-c", "import sys; print(sys.stdin.read())"],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
|
@ -605,7 +609,7 @@ def test_popen_stdin_pipe(testdir):
|
||||||
assert proc.returncode == 0
|
assert proc.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def test_popen_stdin_bytes(testdir):
|
def test_popen_stdin_bytes(testdir) -> None:
|
||||||
proc = testdir.popen(
|
proc = testdir.popen(
|
||||||
[sys.executable, "-c", "import sys; print(sys.stdin.read())"],
|
[sys.executable, "-c", "import sys; print(sys.stdin.read())"],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
|
@ -618,7 +622,7 @@ def test_popen_stdin_bytes(testdir):
|
||||||
assert proc.returncode == 0
|
assert proc.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def test_popen_default_stdin_stderr_and_stdin_None(testdir):
|
def test_popen_default_stdin_stderr_and_stdin_None(testdir) -> None:
|
||||||
# stdout, stderr default to pipes,
|
# stdout, stderr default to pipes,
|
||||||
# stdin can be None to not close the pipe, avoiding
|
# stdin can be None to not close the pipe, avoiding
|
||||||
# "ValueError: flush of closed file" with `communicate()`.
|
# "ValueError: flush of closed file" with `communicate()`.
|
||||||
|
@ -637,7 +641,7 @@ def test_popen_default_stdin_stderr_and_stdin_None(testdir):
|
||||||
assert proc.returncode == 0
|
assert proc.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def test_spawn_uses_tmphome(testdir):
|
def test_spawn_uses_tmphome(testdir) -> None:
|
||||||
tmphome = str(testdir.tmpdir)
|
tmphome = str(testdir.tmpdir)
|
||||||
assert os.environ.get("HOME") == tmphome
|
assert os.environ.get("HOME") == tmphome
|
||||||
|
|
||||||
|
@ -659,7 +663,7 @@ def test_spawn_uses_tmphome(testdir):
|
||||||
assert child.wait() == 0, out.decode("utf8")
|
assert child.wait() == 0, out.decode("utf8")
|
||||||
|
|
||||||
|
|
||||||
def test_run_result_repr():
|
def test_run_result_repr() -> None:
|
||||||
outlines = ["some", "normal", "output"]
|
outlines = ["some", "normal", "output"]
|
||||||
errlines = ["some", "nasty", "errors", "happened"]
|
errlines = ["some", "nasty", "errors", "happened"]
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from _pytest.outcomes import Failed
|
||||||
from _pytest.recwarn import WarningsRecorder
|
from _pytest.recwarn import WarningsRecorder
|
||||||
|
|
||||||
|
|
||||||
def test_recwarn_stacklevel(recwarn):
|
def test_recwarn_stacklevel(recwarn: WarningsRecorder) -> None:
|
||||||
warnings.warn("hello")
|
warnings.warn("hello")
|
||||||
warn = recwarn.pop()
|
warn = recwarn.pop()
|
||||||
assert warn.filename == __file__
|
assert warn.filename == __file__
|
||||||
|
|
||||||
|
|
||||||
def test_recwarn_functional(testdir):
|
def test_recwarn_functional(testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -26,7 +28,7 @@ def test_recwarn_functional(testdir):
|
||||||
|
|
||||||
|
|
||||||
class TestWarningsRecorderChecker:
|
class TestWarningsRecorderChecker:
|
||||||
def test_recording(self):
|
def test_recording(self) -> None:
|
||||||
rec = WarningsRecorder()
|
rec = WarningsRecorder()
|
||||||
with rec:
|
with rec:
|
||||||
assert not rec.list
|
assert not rec.list
|
||||||
|
@ -42,23 +44,23 @@ class TestWarningsRecorderChecker:
|
||||||
assert values is rec.list
|
assert values is rec.list
|
||||||
pytest.raises(AssertionError, rec.pop)
|
pytest.raises(AssertionError, rec.pop)
|
||||||
|
|
||||||
def test_warn_stacklevel(self):
|
def test_warn_stacklevel(self) -> None:
|
||||||
"""#4243"""
|
"""#4243"""
|
||||||
rec = WarningsRecorder()
|
rec = WarningsRecorder()
|
||||||
with rec:
|
with rec:
|
||||||
warnings.warn("test", DeprecationWarning, 2)
|
warnings.warn("test", DeprecationWarning, 2)
|
||||||
|
|
||||||
def test_typechecking(self):
|
def test_typechecking(self) -> None:
|
||||||
from _pytest.recwarn import WarningsChecker
|
from _pytest.recwarn import WarningsChecker
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
WarningsChecker(5)
|
WarningsChecker(5) # type: ignore
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
WarningsChecker(("hi", RuntimeWarning))
|
WarningsChecker(("hi", RuntimeWarning)) # type: ignore
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
WarningsChecker([DeprecationWarning, RuntimeWarning])
|
WarningsChecker([DeprecationWarning, RuntimeWarning]) # type: ignore
|
||||||
|
|
||||||
def test_invalid_enter_exit(self):
|
def test_invalid_enter_exit(self) -> None:
|
||||||
# wrap this test in WarningsRecorder to ensure warning state gets reset
|
# wrap this test in WarningsRecorder to ensure warning state gets reset
|
||||||
with WarningsRecorder():
|
with WarningsRecorder():
|
||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
|
@ -75,50 +77,52 @@ class TestWarningsRecorderChecker:
|
||||||
class TestDeprecatedCall:
|
class TestDeprecatedCall:
|
||||||
"""test pytest.deprecated_call()"""
|
"""test pytest.deprecated_call()"""
|
||||||
|
|
||||||
def dep(self, i, j=None):
|
def dep(self, i: int, j: Optional[int] = None) -> int:
|
||||||
if i == 0:
|
if i == 0:
|
||||||
warnings.warn("is deprecated", DeprecationWarning, stacklevel=1)
|
warnings.warn("is deprecated", DeprecationWarning, stacklevel=1)
|
||||||
return 42
|
return 42
|
||||||
|
|
||||||
def dep_explicit(self, i):
|
def dep_explicit(self, i: int) -> None:
|
||||||
if i == 0:
|
if i == 0:
|
||||||
warnings.warn_explicit(
|
warnings.warn_explicit(
|
||||||
"dep_explicit", category=DeprecationWarning, filename="hello", lineno=3
|
"dep_explicit", category=DeprecationWarning, filename="hello", lineno=3
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_deprecated_call_raises(self):
|
def test_deprecated_call_raises(self) -> None:
|
||||||
with pytest.raises(pytest.fail.Exception, match="No warnings of type"):
|
with pytest.raises(Failed, match="No warnings of type"):
|
||||||
pytest.deprecated_call(self.dep, 3, 5)
|
pytest.deprecated_call(self.dep, 3, 5)
|
||||||
|
|
||||||
def test_deprecated_call(self):
|
def test_deprecated_call(self) -> None:
|
||||||
pytest.deprecated_call(self.dep, 0, 5)
|
pytest.deprecated_call(self.dep, 0, 5)
|
||||||
|
|
||||||
def test_deprecated_call_ret(self):
|
def test_deprecated_call_ret(self) -> None:
|
||||||
ret = pytest.deprecated_call(self.dep, 0)
|
ret = pytest.deprecated_call(self.dep, 0)
|
||||||
assert ret == 42
|
assert ret == 42
|
||||||
|
|
||||||
def test_deprecated_call_preserves(self):
|
def test_deprecated_call_preserves(self) -> None:
|
||||||
onceregistry = warnings.onceregistry.copy()
|
# Type ignored because `onceregistry` and `filters` are not
|
||||||
filters = warnings.filters[:]
|
# documented API.
|
||||||
|
onceregistry = warnings.onceregistry.copy() # type: ignore
|
||||||
|
filters = warnings.filters[:] # type: ignore
|
||||||
warn = warnings.warn
|
warn = warnings.warn
|
||||||
warn_explicit = warnings.warn_explicit
|
warn_explicit = warnings.warn_explicit
|
||||||
self.test_deprecated_call_raises()
|
self.test_deprecated_call_raises()
|
||||||
self.test_deprecated_call()
|
self.test_deprecated_call()
|
||||||
assert onceregistry == warnings.onceregistry
|
assert onceregistry == warnings.onceregistry # type: ignore
|
||||||
assert filters == warnings.filters
|
assert filters == warnings.filters # type: ignore
|
||||||
assert warn is warnings.warn
|
assert warn is warnings.warn
|
||||||
assert warn_explicit is warnings.warn_explicit
|
assert warn_explicit is warnings.warn_explicit
|
||||||
|
|
||||||
def test_deprecated_explicit_call_raises(self):
|
def test_deprecated_explicit_call_raises(self) -> None:
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(Failed):
|
||||||
pytest.deprecated_call(self.dep_explicit, 3)
|
pytest.deprecated_call(self.dep_explicit, 3)
|
||||||
|
|
||||||
def test_deprecated_explicit_call(self):
|
def test_deprecated_explicit_call(self) -> None:
|
||||||
pytest.deprecated_call(self.dep_explicit, 0)
|
pytest.deprecated_call(self.dep_explicit, 0)
|
||||||
pytest.deprecated_call(self.dep_explicit, 0)
|
pytest.deprecated_call(self.dep_explicit, 0)
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ["context_manager", "call"])
|
@pytest.mark.parametrize("mode", ["context_manager", "call"])
|
||||||
def test_deprecated_call_no_warning(self, mode):
|
def test_deprecated_call_no_warning(self, mode) -> None:
|
||||||
"""Ensure deprecated_call() raises the expected failure when its block/function does
|
"""Ensure deprecated_call() raises the expected failure when its block/function does
|
||||||
not raise a deprecation warning.
|
not raise a deprecation warning.
|
||||||
"""
|
"""
|
||||||
|
@ -127,7 +131,7 @@ class TestDeprecatedCall:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
msg = "No warnings of type (.*DeprecationWarning.*, .*PendingDeprecationWarning.*)"
|
msg = "No warnings of type (.*DeprecationWarning.*, .*PendingDeprecationWarning.*)"
|
||||||
with pytest.raises(pytest.fail.Exception, match=msg):
|
with pytest.raises(Failed, match=msg):
|
||||||
if mode == "call":
|
if mode == "call":
|
||||||
pytest.deprecated_call(f)
|
pytest.deprecated_call(f)
|
||||||
else:
|
else:
|
||||||
|
@ -140,7 +144,7 @@ class TestDeprecatedCall:
|
||||||
@pytest.mark.parametrize("mode", ["context_manager", "call"])
|
@pytest.mark.parametrize("mode", ["context_manager", "call"])
|
||||||
@pytest.mark.parametrize("call_f_first", [True, False])
|
@pytest.mark.parametrize("call_f_first", [True, False])
|
||||||
@pytest.mark.filterwarnings("ignore")
|
@pytest.mark.filterwarnings("ignore")
|
||||||
def test_deprecated_call_modes(self, warning_type, mode, call_f_first):
|
def test_deprecated_call_modes(self, warning_type, mode, call_f_first) -> None:
|
||||||
"""Ensure deprecated_call() captures a deprecation warning as expected inside its
|
"""Ensure deprecated_call() captures a deprecation warning as expected inside its
|
||||||
block/function.
|
block/function.
|
||||||
"""
|
"""
|
||||||
|
@ -159,7 +163,7 @@ class TestDeprecatedCall:
|
||||||
assert f() == 10
|
assert f() == 10
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", ["context_manager", "call"])
|
@pytest.mark.parametrize("mode", ["context_manager", "call"])
|
||||||
def test_deprecated_call_exception_is_raised(self, mode):
|
def test_deprecated_call_exception_is_raised(self, mode) -> None:
|
||||||
"""If the block of the code being tested by deprecated_call() raises an exception,
|
"""If the block of the code being tested by deprecated_call() raises an exception,
|
||||||
it must raise the exception undisturbed.
|
it must raise the exception undisturbed.
|
||||||
"""
|
"""
|
||||||
|
@ -174,7 +178,7 @@ class TestDeprecatedCall:
|
||||||
with pytest.deprecated_call():
|
with pytest.deprecated_call():
|
||||||
f()
|
f()
|
||||||
|
|
||||||
def test_deprecated_call_specificity(self):
|
def test_deprecated_call_specificity(self) -> None:
|
||||||
other_warnings = [
|
other_warnings = [
|
||||||
Warning,
|
Warning,
|
||||||
UserWarning,
|
UserWarning,
|
||||||
|
@ -189,40 +193,40 @@ class TestDeprecatedCall:
|
||||||
def f():
|
def f():
|
||||||
warnings.warn(warning("hi"))
|
warnings.warn(warning("hi"))
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(Failed):
|
||||||
pytest.deprecated_call(f)
|
pytest.deprecated_call(f)
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(Failed):
|
||||||
with pytest.deprecated_call():
|
with pytest.deprecated_call():
|
||||||
f()
|
f()
|
||||||
|
|
||||||
def test_deprecated_call_supports_match(self):
|
def test_deprecated_call_supports_match(self) -> None:
|
||||||
with pytest.deprecated_call(match=r"must be \d+$"):
|
with pytest.deprecated_call(match=r"must be \d+$"):
|
||||||
warnings.warn("value must be 42", DeprecationWarning)
|
warnings.warn("value must be 42", DeprecationWarning)
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(Failed):
|
||||||
with pytest.deprecated_call(match=r"must be \d+$"):
|
with pytest.deprecated_call(match=r"must be \d+$"):
|
||||||
warnings.warn("this is not here", DeprecationWarning)
|
warnings.warn("this is not here", DeprecationWarning)
|
||||||
|
|
||||||
|
|
||||||
class TestWarns:
|
class TestWarns:
|
||||||
def test_check_callable(self):
|
def test_check_callable(self) -> None:
|
||||||
source = "warnings.warn('w1', RuntimeWarning)"
|
source = "warnings.warn('w1', RuntimeWarning)"
|
||||||
with pytest.raises(TypeError, match=r".* must be callable"):
|
with pytest.raises(TypeError, match=r".* must be callable"):
|
||||||
pytest.warns(RuntimeWarning, source)
|
pytest.warns(RuntimeWarning, source) # type: ignore
|
||||||
|
|
||||||
def test_several_messages(self):
|
def test_several_messages(self) -> None:
|
||||||
# different messages, b/c Python suppresses multiple identical warnings
|
# different messages, b/c Python suppresses multiple identical warnings
|
||||||
pytest.warns(RuntimeWarning, lambda: warnings.warn("w1", RuntimeWarning))
|
pytest.warns(RuntimeWarning, lambda: warnings.warn("w1", RuntimeWarning))
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(Failed):
|
||||||
pytest.warns(UserWarning, lambda: warnings.warn("w2", RuntimeWarning))
|
pytest.warns(UserWarning, lambda: warnings.warn("w2", RuntimeWarning))
|
||||||
pytest.warns(RuntimeWarning, lambda: warnings.warn("w3", RuntimeWarning))
|
pytest.warns(RuntimeWarning, lambda: warnings.warn("w3", RuntimeWarning))
|
||||||
|
|
||||||
def test_function(self):
|
def test_function(self) -> None:
|
||||||
pytest.warns(
|
pytest.warns(
|
||||||
SyntaxWarning, lambda msg: warnings.warn(msg, SyntaxWarning), "syntax"
|
SyntaxWarning, lambda msg: warnings.warn(msg, SyntaxWarning), "syntax"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_warning_tuple(self):
|
def test_warning_tuple(self) -> None:
|
||||||
pytest.warns(
|
pytest.warns(
|
||||||
(RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w1", RuntimeWarning)
|
(RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w1", RuntimeWarning)
|
||||||
)
|
)
|
||||||
|
@ -230,21 +234,21 @@ class TestWarns:
|
||||||
(RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w2", SyntaxWarning)
|
(RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w2", SyntaxWarning)
|
||||||
)
|
)
|
||||||
pytest.raises(
|
pytest.raises(
|
||||||
pytest.fail.Exception,
|
Failed,
|
||||||
lambda: pytest.warns(
|
lambda: pytest.warns(
|
||||||
(RuntimeWarning, SyntaxWarning),
|
(RuntimeWarning, SyntaxWarning),
|
||||||
lambda: warnings.warn("w3", UserWarning),
|
lambda: warnings.warn("w3", UserWarning),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_as_contextmanager(self):
|
def test_as_contextmanager(self) -> None:
|
||||||
with pytest.warns(RuntimeWarning):
|
with pytest.warns(RuntimeWarning):
|
||||||
warnings.warn("runtime", RuntimeWarning)
|
warnings.warn("runtime", RuntimeWarning)
|
||||||
|
|
||||||
with pytest.warns(UserWarning):
|
with pytest.warns(UserWarning):
|
||||||
warnings.warn("user", UserWarning)
|
warnings.warn("user", UserWarning)
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception) as excinfo:
|
with pytest.raises(Failed) as excinfo:
|
||||||
with pytest.warns(RuntimeWarning):
|
with pytest.warns(RuntimeWarning):
|
||||||
warnings.warn("user", UserWarning)
|
warnings.warn("user", UserWarning)
|
||||||
excinfo.match(
|
excinfo.match(
|
||||||
|
@ -252,7 +256,7 @@ class TestWarns:
|
||||||
r"The list of emitted warnings is: \[UserWarning\('user',?\)\]."
|
r"The list of emitted warnings is: \[UserWarning\('user',?\)\]."
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception) as excinfo:
|
with pytest.raises(Failed) as excinfo:
|
||||||
with pytest.warns(UserWarning):
|
with pytest.warns(UserWarning):
|
||||||
warnings.warn("runtime", RuntimeWarning)
|
warnings.warn("runtime", RuntimeWarning)
|
||||||
excinfo.match(
|
excinfo.match(
|
||||||
|
@ -260,7 +264,7 @@ class TestWarns:
|
||||||
r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\]."
|
r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\]."
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception) as excinfo:
|
with pytest.raises(Failed) as excinfo:
|
||||||
with pytest.warns(UserWarning):
|
with pytest.warns(UserWarning):
|
||||||
pass
|
pass
|
||||||
excinfo.match(
|
excinfo.match(
|
||||||
|
@ -269,7 +273,7 @@ class TestWarns:
|
||||||
)
|
)
|
||||||
|
|
||||||
warning_classes = (UserWarning, FutureWarning)
|
warning_classes = (UserWarning, FutureWarning)
|
||||||
with pytest.raises(pytest.fail.Exception) as excinfo:
|
with pytest.raises(Failed) as excinfo:
|
||||||
with pytest.warns(warning_classes) as warninfo:
|
with pytest.warns(warning_classes) as warninfo:
|
||||||
warnings.warn("runtime", RuntimeWarning)
|
warnings.warn("runtime", RuntimeWarning)
|
||||||
warnings.warn("import", ImportWarning)
|
warnings.warn("import", ImportWarning)
|
||||||
|
@ -286,14 +290,14 @@ class TestWarns:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_record(self):
|
def test_record(self) -> None:
|
||||||
with pytest.warns(UserWarning) as record:
|
with pytest.warns(UserWarning) as record:
|
||||||
warnings.warn("user", UserWarning)
|
warnings.warn("user", UserWarning)
|
||||||
|
|
||||||
assert len(record) == 1
|
assert len(record) == 1
|
||||||
assert str(record[0].message) == "user"
|
assert str(record[0].message) == "user"
|
||||||
|
|
||||||
def test_record_only(self):
|
def test_record_only(self) -> None:
|
||||||
with pytest.warns(None) as record:
|
with pytest.warns(None) as record:
|
||||||
warnings.warn("user", UserWarning)
|
warnings.warn("user", UserWarning)
|
||||||
warnings.warn("runtime", RuntimeWarning)
|
warnings.warn("runtime", RuntimeWarning)
|
||||||
|
@ -302,7 +306,7 @@ class TestWarns:
|
||||||
assert str(record[0].message) == "user"
|
assert str(record[0].message) == "user"
|
||||||
assert str(record[1].message) == "runtime"
|
assert str(record[1].message) == "runtime"
|
||||||
|
|
||||||
def test_record_by_subclass(self):
|
def test_record_by_subclass(self) -> None:
|
||||||
with pytest.warns(Warning) as record:
|
with pytest.warns(Warning) as record:
|
||||||
warnings.warn("user", UserWarning)
|
warnings.warn("user", UserWarning)
|
||||||
warnings.warn("runtime", RuntimeWarning)
|
warnings.warn("runtime", RuntimeWarning)
|
||||||
|
@ -325,7 +329,7 @@ class TestWarns:
|
||||||
assert str(record[0].message) == "user"
|
assert str(record[0].message) == "user"
|
||||||
assert str(record[1].message) == "runtime"
|
assert str(record[1].message) == "runtime"
|
||||||
|
|
||||||
def test_double_test(self, testdir):
|
def test_double_test(self, testdir) -> None:
|
||||||
"""If a test is run again, the warning should still be raised"""
|
"""If a test is run again, the warning should still be raised"""
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -341,32 +345,32 @@ class TestWarns:
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*2 passed in*"])
|
result.stdout.fnmatch_lines(["*2 passed in*"])
|
||||||
|
|
||||||
def test_match_regex(self):
|
def test_match_regex(self) -> None:
|
||||||
with pytest.warns(UserWarning, match=r"must be \d+$"):
|
with pytest.warns(UserWarning, match=r"must be \d+$"):
|
||||||
warnings.warn("value must be 42", UserWarning)
|
warnings.warn("value must be 42", UserWarning)
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(Failed):
|
||||||
with pytest.warns(UserWarning, match=r"must be \d+$"):
|
with pytest.warns(UserWarning, match=r"must be \d+$"):
|
||||||
warnings.warn("this is not here", UserWarning)
|
warnings.warn("this is not here", UserWarning)
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(Failed):
|
||||||
with pytest.warns(FutureWarning, match=r"must be \d+$"):
|
with pytest.warns(FutureWarning, match=r"must be \d+$"):
|
||||||
warnings.warn("value must be 42", UserWarning)
|
warnings.warn("value must be 42", UserWarning)
|
||||||
|
|
||||||
def test_one_from_multiple_warns(self):
|
def test_one_from_multiple_warns(self) -> None:
|
||||||
with pytest.warns(UserWarning, match=r"aaa"):
|
with pytest.warns(UserWarning, match=r"aaa"):
|
||||||
warnings.warn("cccccccccc", UserWarning)
|
warnings.warn("cccccccccc", UserWarning)
|
||||||
warnings.warn("bbbbbbbbbb", UserWarning)
|
warnings.warn("bbbbbbbbbb", UserWarning)
|
||||||
warnings.warn("aaaaaaaaaa", UserWarning)
|
warnings.warn("aaaaaaaaaa", UserWarning)
|
||||||
|
|
||||||
def test_none_of_multiple_warns(self):
|
def test_none_of_multiple_warns(self) -> None:
|
||||||
with pytest.raises(pytest.fail.Exception):
|
with pytest.raises(Failed):
|
||||||
with pytest.warns(UserWarning, match=r"aaa"):
|
with pytest.warns(UserWarning, match=r"aaa"):
|
||||||
warnings.warn("bbbbbbbbbb", UserWarning)
|
warnings.warn("bbbbbbbbbb", UserWarning)
|
||||||
warnings.warn("cccccccccc", UserWarning)
|
warnings.warn("cccccccccc", UserWarning)
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore")
|
@pytest.mark.filterwarnings("ignore")
|
||||||
def test_can_capture_previously_warned(self):
|
def test_can_capture_previously_warned(self) -> None:
|
||||||
def f():
|
def f():
|
||||||
warnings.warn(UserWarning("ohai"))
|
warnings.warn(UserWarning("ohai"))
|
||||||
return 10
|
return 10
|
||||||
|
@ -375,8 +379,8 @@ class TestWarns:
|
||||||
assert pytest.warns(UserWarning, f) == 10
|
assert pytest.warns(UserWarning, f) == 10
|
||||||
assert pytest.warns(UserWarning, f) == 10
|
assert pytest.warns(UserWarning, f) == 10
|
||||||
|
|
||||||
def test_warns_context_manager_with_kwargs(self):
|
def test_warns_context_manager_with_kwargs(self) -> None:
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
with pytest.warns(UserWarning, foo="bar"):
|
with pytest.warns(UserWarning, foo="bar"): # type: ignore
|
||||||
pass
|
pass
|
||||||
assert "Unexpected keyword arguments" in str(excinfo.value)
|
assert "Unexpected keyword arguments" in str(excinfo.value)
|
||||||
|
|
|
@ -2,6 +2,9 @@ import inspect
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -11,11 +14,17 @@ from _pytest import main
|
||||||
from _pytest import outcomes
|
from _pytest import outcomes
|
||||||
from _pytest import reports
|
from _pytest import reports
|
||||||
from _pytest import runner
|
from _pytest import runner
|
||||||
|
from _pytest.outcomes import Exit
|
||||||
|
from _pytest.outcomes import Failed
|
||||||
from _pytest.outcomes import OutcomeException
|
from _pytest.outcomes import OutcomeException
|
||||||
|
from _pytest.outcomes import Skipped
|
||||||
|
|
||||||
|
if False: # TYPE_CHECKING
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
|
|
||||||
class TestSetupState:
|
class TestSetupState:
|
||||||
def test_setup(self, testdir):
|
def test_setup(self, testdir) -> None:
|
||||||
ss = runner.SetupState()
|
ss = runner.SetupState()
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
values = [1]
|
values = [1]
|
||||||
|
@ -25,14 +34,14 @@ class TestSetupState:
|
||||||
ss._pop_and_teardown()
|
ss._pop_and_teardown()
|
||||||
assert not values
|
assert not values
|
||||||
|
|
||||||
def test_teardown_exact_stack_empty(self, testdir):
|
def test_teardown_exact_stack_empty(self, testdir) -> None:
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
ss = runner.SetupState()
|
ss = runner.SetupState()
|
||||||
ss.teardown_exact(item, None)
|
ss.teardown_exact(item, None)
|
||||||
ss.teardown_exact(item, None)
|
ss.teardown_exact(item, None)
|
||||||
ss.teardown_exact(item, None)
|
ss.teardown_exact(item, None)
|
||||||
|
|
||||||
def test_setup_fails_and_failure_is_cached(self, testdir):
|
def test_setup_fails_and_failure_is_cached(self, testdir) -> None:
|
||||||
item = testdir.getitem(
|
item = testdir.getitem(
|
||||||
"""
|
"""
|
||||||
def setup_module(mod):
|
def setup_module(mod):
|
||||||
|
@ -44,7 +53,7 @@ class TestSetupState:
|
||||||
pytest.raises(ValueError, lambda: ss.prepare(item))
|
pytest.raises(ValueError, lambda: ss.prepare(item))
|
||||||
pytest.raises(ValueError, lambda: ss.prepare(item))
|
pytest.raises(ValueError, lambda: ss.prepare(item))
|
||||||
|
|
||||||
def test_teardown_multiple_one_fails(self, testdir):
|
def test_teardown_multiple_one_fails(self, testdir) -> None:
|
||||||
r = []
|
r = []
|
||||||
|
|
||||||
def fin1():
|
def fin1():
|
||||||
|
@ -66,7 +75,7 @@ class TestSetupState:
|
||||||
assert err.value.args == ("oops",)
|
assert err.value.args == ("oops",)
|
||||||
assert r == ["fin3", "fin1"]
|
assert r == ["fin3", "fin1"]
|
||||||
|
|
||||||
def test_teardown_multiple_fail(self, testdir):
|
def test_teardown_multiple_fail(self, testdir) -> None:
|
||||||
# Ensure the first exception is the one which is re-raised.
|
# Ensure the first exception is the one which is re-raised.
|
||||||
# Ideally both would be reported however.
|
# Ideally both would be reported however.
|
||||||
def fin1():
|
def fin1():
|
||||||
|
@ -83,7 +92,7 @@ class TestSetupState:
|
||||||
ss._callfinalizers(item)
|
ss._callfinalizers(item)
|
||||||
assert err.value.args == ("oops2",)
|
assert err.value.args == ("oops2",)
|
||||||
|
|
||||||
def test_teardown_multiple_scopes_one_fails(self, testdir):
|
def test_teardown_multiple_scopes_one_fails(self, testdir) -> None:
|
||||||
module_teardown = []
|
module_teardown = []
|
||||||
|
|
||||||
def fin_func():
|
def fin_func():
|
||||||
|
@ -103,7 +112,7 @@ class TestSetupState:
|
||||||
|
|
||||||
|
|
||||||
class BaseFunctionalTests:
|
class BaseFunctionalTests:
|
||||||
def test_passfunction(self, testdir):
|
def test_passfunction(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
def test_func():
|
def test_func():
|
||||||
|
@ -116,7 +125,7 @@ class BaseFunctionalTests:
|
||||||
assert rep.outcome == "passed"
|
assert rep.outcome == "passed"
|
||||||
assert not rep.longrepr
|
assert not rep.longrepr
|
||||||
|
|
||||||
def test_failfunction(self, testdir):
|
def test_failfunction(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
def test_func():
|
def test_func():
|
||||||
|
@ -131,7 +140,7 @@ class BaseFunctionalTests:
|
||||||
assert rep.outcome == "failed"
|
assert rep.outcome == "failed"
|
||||||
# assert isinstance(rep.longrepr, ReprExceptionInfo)
|
# assert isinstance(rep.longrepr, ReprExceptionInfo)
|
||||||
|
|
||||||
def test_skipfunction(self, testdir):
|
def test_skipfunction(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -151,7 +160,7 @@ class BaseFunctionalTests:
|
||||||
# assert rep.skipped.location.path
|
# assert rep.skipped.location.path
|
||||||
# assert not rep.skipped.failurerepr
|
# assert not rep.skipped.failurerepr
|
||||||
|
|
||||||
def test_skip_in_setup_function(self, testdir):
|
def test_skip_in_setup_function(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -172,7 +181,7 @@ class BaseFunctionalTests:
|
||||||
assert len(reports) == 2
|
assert len(reports) == 2
|
||||||
assert reports[1].passed # teardown
|
assert reports[1].passed # teardown
|
||||||
|
|
||||||
def test_failure_in_setup_function(self, testdir):
|
def test_failure_in_setup_function(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -189,7 +198,7 @@ class BaseFunctionalTests:
|
||||||
assert rep.when == "setup"
|
assert rep.when == "setup"
|
||||||
assert len(reports) == 2
|
assert len(reports) == 2
|
||||||
|
|
||||||
def test_failure_in_teardown_function(self, testdir):
|
def test_failure_in_teardown_function(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -209,7 +218,7 @@ class BaseFunctionalTests:
|
||||||
# assert rep.longrepr.reprcrash.lineno == 3
|
# assert rep.longrepr.reprcrash.lineno == 3
|
||||||
# assert rep.longrepr.reprtraceback.reprentries
|
# assert rep.longrepr.reprtraceback.reprentries
|
||||||
|
|
||||||
def test_custom_failure_repr(self, testdir):
|
def test_custom_failure_repr(self, testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
conftest="""
|
conftest="""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -234,7 +243,7 @@ class BaseFunctionalTests:
|
||||||
# assert rep.failed.where.path.basename == "test_func.py"
|
# assert rep.failed.where.path.basename == "test_func.py"
|
||||||
# assert rep.failed.failurerepr == "hello"
|
# assert rep.failed.failurerepr == "hello"
|
||||||
|
|
||||||
def test_teardown_final_returncode(self, testdir):
|
def test_teardown_final_returncode(self, testdir) -> None:
|
||||||
rec = testdir.inline_runsource(
|
rec = testdir.inline_runsource(
|
||||||
"""
|
"""
|
||||||
def test_func():
|
def test_func():
|
||||||
|
@ -245,7 +254,7 @@ class BaseFunctionalTests:
|
||||||
)
|
)
|
||||||
assert rec.ret == 1
|
assert rec.ret == 1
|
||||||
|
|
||||||
def test_logstart_logfinish_hooks(self, testdir):
|
def test_logstart_logfinish_hooks(self, testdir) -> None:
|
||||||
rec = testdir.inline_runsource(
|
rec = testdir.inline_runsource(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -262,7 +271,7 @@ class BaseFunctionalTests:
|
||||||
assert rep.nodeid == "test_logstart_logfinish_hooks.py::test_func"
|
assert rep.nodeid == "test_logstart_logfinish_hooks.py::test_func"
|
||||||
assert rep.location == ("test_logstart_logfinish_hooks.py", 1, "test_func")
|
assert rep.location == ("test_logstart_logfinish_hooks.py", 1, "test_func")
|
||||||
|
|
||||||
def test_exact_teardown_issue90(self, testdir):
|
def test_exact_teardown_issue90(self, testdir) -> None:
|
||||||
rec = testdir.inline_runsource(
|
rec = testdir.inline_runsource(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -302,7 +311,7 @@ class BaseFunctionalTests:
|
||||||
assert reps[5].nodeid.endswith("test_func")
|
assert reps[5].nodeid.endswith("test_func")
|
||||||
assert reps[5].failed
|
assert reps[5].failed
|
||||||
|
|
||||||
def test_exact_teardown_issue1206(self, testdir):
|
def test_exact_teardown_issue1206(self, testdir) -> None:
|
||||||
"""issue shadowing error with wrong number of arguments on teardown_method."""
|
"""issue shadowing error with wrong number of arguments on teardown_method."""
|
||||||
rec = testdir.inline_runsource(
|
rec = testdir.inline_runsource(
|
||||||
"""
|
"""
|
||||||
|
@ -338,7 +347,7 @@ class BaseFunctionalTests:
|
||||||
"TypeError: teardown_method() takes exactly 4 arguments (2 given)",
|
"TypeError: teardown_method() takes exactly 4 arguments (2 given)",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_failure_in_setup_function_ignores_custom_repr(self, testdir):
|
def test_failure_in_setup_function_ignores_custom_repr(self, testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
conftest="""
|
conftest="""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -366,7 +375,7 @@ class BaseFunctionalTests:
|
||||||
# assert rep.outcome.where.path.basename == "test_func.py"
|
# assert rep.outcome.where.path.basename == "test_func.py"
|
||||||
# assert instanace(rep.failed.failurerepr, PythonFailureRepr)
|
# assert instanace(rep.failed.failurerepr, PythonFailureRepr)
|
||||||
|
|
||||||
def test_systemexit_does_not_bail_out(self, testdir):
|
def test_systemexit_does_not_bail_out(self, testdir) -> None:
|
||||||
try:
|
try:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
|
@ -380,7 +389,7 @@ class BaseFunctionalTests:
|
||||||
assert rep.failed
|
assert rep.failed
|
||||||
assert rep.when == "call"
|
assert rep.when == "call"
|
||||||
|
|
||||||
def test_exit_propagates(self, testdir):
|
def test_exit_propagates(self, testdir) -> None:
|
||||||
try:
|
try:
|
||||||
testdir.runitem(
|
testdir.runitem(
|
||||||
"""
|
"""
|
||||||
|
@ -389,7 +398,7 @@ class BaseFunctionalTests:
|
||||||
raise pytest.exit.Exception()
|
raise pytest.exit.Exception()
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
except pytest.exit.Exception:
|
except Exit:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
pytest.fail("did not raise")
|
pytest.fail("did not raise")
|
||||||
|
@ -402,7 +411,7 @@ class TestExecutionNonForked(BaseFunctionalTests):
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def test_keyboardinterrupt_propagates(self, testdir):
|
def test_keyboardinterrupt_propagates(self, testdir) -> None:
|
||||||
try:
|
try:
|
||||||
testdir.runitem(
|
testdir.runitem(
|
||||||
"""
|
"""
|
||||||
|
@ -424,7 +433,7 @@ class TestExecutionForked(BaseFunctionalTests):
|
||||||
boxed = pytest.importorskip("xdist.boxed")
|
boxed = pytest.importorskip("xdist.boxed")
|
||||||
return boxed.forked_run_report
|
return boxed.forked_run_report
|
||||||
|
|
||||||
def test_suicide(self, testdir):
|
def test_suicide(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
def test_func():
|
def test_func():
|
||||||
|
@ -438,7 +447,7 @@ class TestExecutionForked(BaseFunctionalTests):
|
||||||
|
|
||||||
|
|
||||||
class TestSessionReports:
|
class TestSessionReports:
|
||||||
def test_collect_result(self, testdir):
|
def test_collect_result(self, testdir) -> None:
|
||||||
col = testdir.getmodulecol(
|
col = testdir.getmodulecol(
|
||||||
"""
|
"""
|
||||||
def test_func1():
|
def test_func1():
|
||||||
|
@ -461,20 +470,24 @@ class TestSessionReports:
|
||||||
assert res[1].name == "TestClass"
|
assert res[1].name == "TestClass"
|
||||||
|
|
||||||
|
|
||||||
reporttypes = [reports.BaseReport, reports.TestReport, reports.CollectReport]
|
reporttypes = [
|
||||||
|
reports.BaseReport,
|
||||||
|
reports.TestReport,
|
||||||
|
reports.CollectReport,
|
||||||
|
] # type: List[Type[reports.BaseReport]]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"reporttype", reporttypes, ids=[x.__name__ for x in reporttypes]
|
"reporttype", reporttypes, ids=[x.__name__ for x in reporttypes]
|
||||||
)
|
)
|
||||||
def test_report_extra_parameters(reporttype):
|
def test_report_extra_parameters(reporttype: "Type[reports.BaseReport]") -> None:
|
||||||
args = list(inspect.signature(reporttype.__init__).parameters.keys())[1:]
|
args = list(inspect.signature(reporttype.__init__).parameters.keys())[1:]
|
||||||
basekw = dict.fromkeys(args, [])
|
basekw = dict.fromkeys(args, []) # type: Dict[str, List[object]]
|
||||||
report = reporttype(newthing=1, **basekw)
|
report = reporttype(newthing=1, **basekw)
|
||||||
assert report.newthing == 1
|
assert report.newthing == 1
|
||||||
|
|
||||||
|
|
||||||
def test_callinfo():
|
def test_callinfo() -> None:
|
||||||
ci = runner.CallInfo.from_call(lambda: 0, "123")
|
ci = runner.CallInfo.from_call(lambda: 0, "123")
|
||||||
assert ci.when == "123"
|
assert ci.when == "123"
|
||||||
assert ci.result == 0
|
assert ci.result == 0
|
||||||
|
@ -503,7 +516,7 @@ def test_callinfo():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail
|
@pytest.mark.xfail
|
||||||
def test_runtest_in_module_ordering(testdir):
|
def test_runtest_in_module_ordering(testdir) -> None:
|
||||||
p1 = testdir.makepyfile(
|
p1 = testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -534,12 +547,12 @@ def test_runtest_in_module_ordering(testdir):
|
||||||
result.stdout.fnmatch_lines(["*2 passed*"])
|
result.stdout.fnmatch_lines(["*2 passed*"])
|
||||||
|
|
||||||
|
|
||||||
def test_outcomeexception_exceptionattributes():
|
def test_outcomeexception_exceptionattributes() -> None:
|
||||||
outcome = outcomes.OutcomeException("test")
|
outcome = outcomes.OutcomeException("test")
|
||||||
assert outcome.args[0] == outcome.msg
|
assert outcome.args[0] == outcome.msg
|
||||||
|
|
||||||
|
|
||||||
def test_outcomeexception_passes_except_Exception():
|
def test_outcomeexception_passes_except_Exception() -> None:
|
||||||
with pytest.raises(outcomes.OutcomeException):
|
with pytest.raises(outcomes.OutcomeException):
|
||||||
try:
|
try:
|
||||||
raise outcomes.OutcomeException("test")
|
raise outcomes.OutcomeException("test")
|
||||||
|
@ -547,20 +560,22 @@ def test_outcomeexception_passes_except_Exception():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_exit():
|
def test_pytest_exit() -> None:
|
||||||
with pytest.raises(pytest.exit.Exception) as excinfo:
|
assert Exit == pytest.exit.Exception # type: ignore
|
||||||
|
with pytest.raises(Exit) as excinfo:
|
||||||
pytest.exit("hello")
|
pytest.exit("hello")
|
||||||
assert excinfo.errisinstance(pytest.exit.Exception)
|
assert excinfo.errisinstance(Exit)
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_fail():
|
def test_pytest_fail() -> None:
|
||||||
with pytest.raises(pytest.fail.Exception) as excinfo:
|
assert Failed == pytest.fail.Exception # type: ignore
|
||||||
|
with pytest.raises(Failed) as excinfo:
|
||||||
pytest.fail("hello")
|
pytest.fail("hello")
|
||||||
s = excinfo.exconly(tryshort=True)
|
s = excinfo.exconly(tryshort=True)
|
||||||
assert s.startswith("Failed")
|
assert s.startswith("Failed")
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_exit_msg(testdir):
|
def test_pytest_exit_msg(testdir) -> None:
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -583,7 +598,7 @@ def _strip_resource_warnings(lines):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_exit_returncode(testdir):
|
def test_pytest_exit_returncode(testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""\
|
"""\
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -614,7 +629,7 @@ def test_pytest_exit_returncode(testdir):
|
||||||
assert result.ret == 98
|
assert result.ret == 98
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_fail_notrace_runtest(testdir):
|
def test_pytest_fail_notrace_runtest(testdir) -> None:
|
||||||
"""Test pytest.fail(..., pytrace=False) does not show tracebacks during test run."""
|
"""Test pytest.fail(..., pytrace=False) does not show tracebacks during test run."""
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -630,7 +645,7 @@ def test_pytest_fail_notrace_runtest(testdir):
|
||||||
result.stdout.no_fnmatch_line("*def teardown_function*")
|
result.stdout.no_fnmatch_line("*def teardown_function*")
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_fail_notrace_collection(testdir):
|
def test_pytest_fail_notrace_collection(testdir) -> None:
|
||||||
"""Test pytest.fail(..., pytrace=False) does not show tracebacks during collection."""
|
"""Test pytest.fail(..., pytrace=False) does not show tracebacks during collection."""
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -645,7 +660,7 @@ def test_pytest_fail_notrace_collection(testdir):
|
||||||
result.stdout.no_fnmatch_line("*def some_internal_function()*")
|
result.stdout.no_fnmatch_line("*def some_internal_function()*")
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_fail_notrace_non_ascii(testdir):
|
def test_pytest_fail_notrace_non_ascii(testdir) -> None:
|
||||||
"""Fix pytest.fail with pytrace=False with non-ascii characters (#1178).
|
"""Fix pytest.fail with pytrace=False with non-ascii characters (#1178).
|
||||||
|
|
||||||
This tests with native and unicode strings containing non-ascii chars.
|
This tests with native and unicode strings containing non-ascii chars.
|
||||||
|
@ -663,7 +678,7 @@ def test_pytest_fail_notrace_non_ascii(testdir):
|
||||||
result.stdout.no_fnmatch_line("*def test_hello*")
|
result.stdout.no_fnmatch_line("*def test_hello*")
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_no_tests_collected_exit_status(testdir):
|
def test_pytest_no_tests_collected_exit_status(testdir) -> None:
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*collected 0 items*"])
|
result.stdout.fnmatch_lines(["*collected 0 items*"])
|
||||||
assert result.ret == main.ExitCode.NO_TESTS_COLLECTED
|
assert result.ret == main.ExitCode.NO_TESTS_COLLECTED
|
||||||
|
@ -685,16 +700,17 @@ def test_pytest_no_tests_collected_exit_status(testdir):
|
||||||
assert result.ret == main.ExitCode.NO_TESTS_COLLECTED
|
assert result.ret == main.ExitCode.NO_TESTS_COLLECTED
|
||||||
|
|
||||||
|
|
||||||
def test_exception_printing_skip():
|
def test_exception_printing_skip() -> None:
|
||||||
|
assert Skipped == pytest.skip.Exception # type: ignore
|
||||||
try:
|
try:
|
||||||
pytest.skip("hello")
|
pytest.skip("hello")
|
||||||
except pytest.skip.Exception:
|
except Skipped:
|
||||||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
excinfo = _pytest._code.ExceptionInfo.from_current()
|
||||||
s = excinfo.exconly(tryshort=True)
|
s = excinfo.exconly(tryshort=True)
|
||||||
assert s.startswith("Skipped")
|
assert s.startswith("Skipped")
|
||||||
|
|
||||||
|
|
||||||
def test_importorskip(monkeypatch):
|
def test_importorskip(monkeypatch) -> None:
|
||||||
importorskip = pytest.importorskip
|
importorskip = pytest.importorskip
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
|
@ -705,45 +721,49 @@ def test_importorskip(monkeypatch):
|
||||||
assert sysmod is sys
|
assert sysmod is sys
|
||||||
# path = pytest.importorskip("os.path")
|
# path = pytest.importorskip("os.path")
|
||||||
# assert path == os.path
|
# assert path == os.path
|
||||||
excinfo = pytest.raises(pytest.skip.Exception, f)
|
excinfo = pytest.raises(Skipped, f)
|
||||||
path = py.path.local(excinfo.getrepr().reprcrash.path)
|
assert excinfo is not None
|
||||||
|
excrepr = excinfo.getrepr()
|
||||||
|
assert excrepr is not None
|
||||||
|
assert excrepr.reprcrash is not None
|
||||||
|
path = py.path.local(excrepr.reprcrash.path)
|
||||||
# check that importorskip reports the actual call
|
# check that importorskip reports the actual call
|
||||||
# in this test the test_runner.py file
|
# in this test the test_runner.py file
|
||||||
assert path.purebasename == "test_runner"
|
assert path.purebasename == "test_runner"
|
||||||
pytest.raises(SyntaxError, pytest.importorskip, "x y z")
|
pytest.raises(SyntaxError, pytest.importorskip, "x y z")
|
||||||
pytest.raises(SyntaxError, pytest.importorskip, "x=y")
|
pytest.raises(SyntaxError, pytest.importorskip, "x=y")
|
||||||
mod = types.ModuleType("hello123")
|
mod = types.ModuleType("hello123")
|
||||||
mod.__version__ = "1.3"
|
mod.__version__ = "1.3" # type: ignore
|
||||||
monkeypatch.setitem(sys.modules, "hello123", mod)
|
monkeypatch.setitem(sys.modules, "hello123", mod)
|
||||||
with pytest.raises(pytest.skip.Exception):
|
with pytest.raises(Skipped):
|
||||||
pytest.importorskip("hello123", minversion="1.3.1")
|
pytest.importorskip("hello123", minversion="1.3.1")
|
||||||
mod2 = pytest.importorskip("hello123", minversion="1.3")
|
mod2 = pytest.importorskip("hello123", minversion="1.3")
|
||||||
assert mod2 == mod
|
assert mod2 == mod
|
||||||
except pytest.skip.Exception:
|
except Skipped:
|
||||||
print(_pytest._code.ExceptionInfo.from_current())
|
print(_pytest._code.ExceptionInfo.from_current())
|
||||||
pytest.fail("spurious skip")
|
pytest.fail("spurious skip")
|
||||||
|
|
||||||
|
|
||||||
def test_importorskip_imports_last_module_part():
|
def test_importorskip_imports_last_module_part() -> None:
|
||||||
ospath = pytest.importorskip("os.path")
|
ospath = pytest.importorskip("os.path")
|
||||||
assert os.path == ospath
|
assert os.path == ospath
|
||||||
|
|
||||||
|
|
||||||
def test_importorskip_dev_module(monkeypatch):
|
def test_importorskip_dev_module(monkeypatch) -> None:
|
||||||
try:
|
try:
|
||||||
mod = types.ModuleType("mockmodule")
|
mod = types.ModuleType("mockmodule")
|
||||||
mod.__version__ = "0.13.0.dev-43290"
|
mod.__version__ = "0.13.0.dev-43290" # type: ignore
|
||||||
monkeypatch.setitem(sys.modules, "mockmodule", mod)
|
monkeypatch.setitem(sys.modules, "mockmodule", mod)
|
||||||
mod2 = pytest.importorskip("mockmodule", minversion="0.12.0")
|
mod2 = pytest.importorskip("mockmodule", minversion="0.12.0")
|
||||||
assert mod2 == mod
|
assert mod2 == mod
|
||||||
with pytest.raises(pytest.skip.Exception):
|
with pytest.raises(Skipped):
|
||||||
pytest.importorskip("mockmodule1", minversion="0.14.0")
|
pytest.importorskip("mockmodule1", minversion="0.14.0")
|
||||||
except pytest.skip.Exception:
|
except Skipped:
|
||||||
print(_pytest._code.ExceptionInfo.from_current())
|
print(_pytest._code.ExceptionInfo.from_current())
|
||||||
pytest.fail("spurious skip")
|
pytest.fail("spurious skip")
|
||||||
|
|
||||||
|
|
||||||
def test_importorskip_module_level(testdir):
|
def test_importorskip_module_level(testdir) -> None:
|
||||||
"""importorskip must be able to skip entire modules when used at module level"""
|
"""importorskip must be able to skip entire modules when used at module level"""
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -758,7 +778,7 @@ def test_importorskip_module_level(testdir):
|
||||||
result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"])
|
result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"])
|
||||||
|
|
||||||
|
|
||||||
def test_importorskip_custom_reason(testdir):
|
def test_importorskip_custom_reason(testdir) -> None:
|
||||||
"""make sure custom reasons are used"""
|
"""make sure custom reasons are used"""
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -774,7 +794,7 @@ def test_importorskip_custom_reason(testdir):
|
||||||
result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"])
|
result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"])
|
||||||
|
|
||||||
|
|
||||||
def test_pytest_cmdline_main(testdir):
|
def test_pytest_cmdline_main(testdir) -> None:
|
||||||
p = testdir.makepyfile(
|
p = testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -792,7 +812,7 @@ def test_pytest_cmdline_main(testdir):
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_unicode_in_longrepr(testdir):
|
def test_unicode_in_longrepr(testdir) -> None:
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""\
|
"""\
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -815,7 +835,7 @@ def test_unicode_in_longrepr(testdir):
|
||||||
assert "UnicodeEncodeError" not in result.stderr.str()
|
assert "UnicodeEncodeError" not in result.stderr.str()
|
||||||
|
|
||||||
|
|
||||||
def test_failure_in_setup(testdir):
|
def test_failure_in_setup(testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
def setup_module():
|
def setup_module():
|
||||||
|
@ -828,7 +848,7 @@ def test_failure_in_setup(testdir):
|
||||||
result.stdout.no_fnmatch_line("*def setup_module*")
|
result.stdout.no_fnmatch_line("*def setup_module*")
|
||||||
|
|
||||||
|
|
||||||
def test_makereport_getsource(testdir):
|
def test_makereport_getsource(testdir) -> None:
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
def test_foo():
|
def test_foo():
|
||||||
|
@ -841,17 +861,17 @@ def test_makereport_getsource(testdir):
|
||||||
result.stdout.fnmatch_lines(["*else: assert False*"])
|
result.stdout.fnmatch_lines(["*else: assert False*"])
|
||||||
|
|
||||||
|
|
||||||
def test_makereport_getsource_dynamic_code(testdir, monkeypatch):
|
def test_makereport_getsource_dynamic_code(testdir, monkeypatch) -> None:
|
||||||
"""Test that exception in dynamically generated code doesn't break getting the source line."""
|
"""Test that exception in dynamically generated code doesn't break getting the source line."""
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
original_findsource = inspect.findsource
|
original_findsource = inspect.findsource
|
||||||
|
|
||||||
def findsource(obj, *args, **kwargs):
|
def findsource(obj):
|
||||||
# Can be triggered by dynamically created functions
|
# Can be triggered by dynamically created functions
|
||||||
if obj.__name__ == "foo":
|
if obj.__name__ == "foo":
|
||||||
raise IndexError()
|
raise IndexError()
|
||||||
return original_findsource(obj, *args, **kwargs)
|
return original_findsource(obj)
|
||||||
|
|
||||||
monkeypatch.setattr(inspect, "findsource", findsource)
|
monkeypatch.setattr(inspect, "findsource", findsource)
|
||||||
|
|
||||||
|
@ -872,7 +892,7 @@ def test_makereport_getsource_dynamic_code(testdir, monkeypatch):
|
||||||
result.stdout.fnmatch_lines(["*test_fix*", "*fixture*'missing'*not found*"])
|
result.stdout.fnmatch_lines(["*test_fix*", "*fixture*'missing'*not found*"])
|
||||||
|
|
||||||
|
|
||||||
def test_store_except_info_on_error():
|
def test_store_except_info_on_error() -> None:
|
||||||
""" Test that upon test failure, the exception info is stored on
|
""" Test that upon test failure, the exception info is stored on
|
||||||
sys.last_traceback and friends.
|
sys.last_traceback and friends.
|
||||||
"""
|
"""
|
||||||
|
@ -891,6 +911,7 @@ def test_store_except_info_on_error():
|
||||||
pass
|
pass
|
||||||
# Check that exception info is stored on sys
|
# Check that exception info is stored on sys
|
||||||
assert sys.last_type is IndexError
|
assert sys.last_type is IndexError
|
||||||
|
assert isinstance(sys.last_value, IndexError)
|
||||||
assert sys.last_value.args[0] == "TEST"
|
assert sys.last_value.args[0] == "TEST"
|
||||||
assert sys.last_traceback
|
assert sys.last_traceback
|
||||||
|
|
||||||
|
@ -902,8 +923,8 @@ def test_store_except_info_on_error():
|
||||||
assert not hasattr(sys, "last_traceback")
|
assert not hasattr(sys, "last_traceback")
|
||||||
|
|
||||||
|
|
||||||
def test_current_test_env_var(testdir, monkeypatch):
|
def test_current_test_env_var(testdir, monkeypatch) -> None:
|
||||||
pytest_current_test_vars = []
|
pytest_current_test_vars = [] # type: List[Tuple[str, str]]
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
sys, "pytest_current_test_vars", pytest_current_test_vars, raising=False
|
sys, "pytest_current_test_vars", pytest_current_test_vars, raising=False
|
||||||
)
|
)
|
||||||
|
@ -942,7 +963,7 @@ class TestReportContents:
|
||||||
def getrunner(self):
|
def getrunner(self):
|
||||||
return lambda item: runner.runtestprotocol(item, log=False)
|
return lambda item: runner.runtestprotocol(item, log=False)
|
||||||
|
|
||||||
def test_longreprtext_pass(self, testdir):
|
def test_longreprtext_pass(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
def test_func():
|
def test_func():
|
||||||
|
@ -952,7 +973,7 @@ class TestReportContents:
|
||||||
rep = reports[1]
|
rep = reports[1]
|
||||||
assert rep.longreprtext == ""
|
assert rep.longreprtext == ""
|
||||||
|
|
||||||
def test_longreprtext_failure(self, testdir):
|
def test_longreprtext_failure(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
def test_func():
|
def test_func():
|
||||||
|
@ -963,7 +984,7 @@ class TestReportContents:
|
||||||
rep = reports[1]
|
rep = reports[1]
|
||||||
assert "assert 1 == 4" in rep.longreprtext
|
assert "assert 1 == 4" in rep.longreprtext
|
||||||
|
|
||||||
def test_captured_text(self, testdir):
|
def test_captured_text(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -993,7 +1014,7 @@ class TestReportContents:
|
||||||
assert call.capstderr == "setup: stderr\ncall: stderr\n"
|
assert call.capstderr == "setup: stderr\ncall: stderr\n"
|
||||||
assert teardown.capstderr == "setup: stderr\ncall: stderr\nteardown: stderr\n"
|
assert teardown.capstderr == "setup: stderr\ncall: stderr\nteardown: stderr\n"
|
||||||
|
|
||||||
def test_no_captured_text(self, testdir):
|
def test_no_captured_text(self, testdir) -> None:
|
||||||
reports = testdir.runitem(
|
reports = testdir.runitem(
|
||||||
"""
|
"""
|
||||||
def test_func():
|
def test_func():
|
||||||
|
@ -1005,10 +1026,10 @@ class TestReportContents:
|
||||||
assert rep.capstderr == ""
|
assert rep.capstderr == ""
|
||||||
|
|
||||||
|
|
||||||
def test_outcome_exception_bad_msg():
|
def test_outcome_exception_bad_msg() -> None:
|
||||||
"""Check that OutcomeExceptions validate their input to prevent confusing errors (#5578)"""
|
"""Check that OutcomeExceptions validate their input to prevent confusing errors (#5578)"""
|
||||||
|
|
||||||
def func():
|
def func() -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
expected = (
|
expected = (
|
||||||
|
@ -1016,5 +1037,5 @@ def test_outcome_exception_bad_msg():
|
||||||
"Perhaps you meant to use a mark?"
|
"Perhaps you meant to use a mark?"
|
||||||
)
|
)
|
||||||
with pytest.raises(TypeError) as excinfo:
|
with pytest.raises(TypeError) as excinfo:
|
||||||
OutcomeException(func)
|
OutcomeException(func) # type: ignore
|
||||||
assert str(excinfo.value) == expected
|
assert str(excinfo.value) == expected
|
||||||
|
|
|
@ -167,11 +167,13 @@ def test_stop_on_collection_errors(broken_testdir, broken_first):
|
||||||
result.stdout.fnmatch_lines("*error during collection*")
|
result.stdout.fnmatch_lines("*error during collection*")
|
||||||
|
|
||||||
|
|
||||||
def test_xfail_handling(testdir):
|
def test_xfail_handling(testdir, monkeypatch):
|
||||||
"""Ensure normal xfail is ignored, and strict xfail interrupts the session in sw mode
|
"""Ensure normal xfail is ignored, and strict xfail interrupts the session in sw mode
|
||||||
|
|
||||||
(#5547)
|
(#5547)
|
||||||
"""
|
"""
|
||||||
|
monkeypatch.setattr("sys.dont_write_bytecode", True)
|
||||||
|
|
||||||
contents = """
|
contents = """
|
||||||
import pytest
|
import pytest
|
||||||
def test_a(): pass
|
def test_a(): pass
|
||||||
|
@ -205,10 +207,6 @@ def test_xfail_handling(testdir):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# because we are writing to the same file, mtime might not be affected enough to
|
|
||||||
# invalidate the cache, making this next run flaky
|
|
||||||
if testdir.tmpdir.join("__pycache__").exists():
|
|
||||||
testdir.tmpdir.join("__pycache__").remove()
|
|
||||||
testdir.makepyfile(contents.format(assert_value="0", strict="True"))
|
testdir.makepyfile(contents.format(assert_value="0", strict="True"))
|
||||||
result = testdir.runpytest("--sw", "-v")
|
result = testdir.runpytest("--sw", "-v")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
|
|
|
@ -3,6 +3,7 @@ terminal reporting of the full testing process.
|
||||||
"""
|
"""
|
||||||
import collections
|
import collections
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
@ -21,10 +22,15 @@ from _pytest.terminal import getreportopt
|
||||||
from _pytest.terminal import TerminalReporter
|
from _pytest.terminal import TerminalReporter
|
||||||
|
|
||||||
DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"])
|
DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"])
|
||||||
RED = r"\x1b\[31m"
|
|
||||||
GREEN = r"\x1b\[32m"
|
COLORS = {
|
||||||
YELLOW = r"\x1b\[33m"
|
"red": "\x1b[31m",
|
||||||
RESET = r"\x1b\[0m"
|
"green": "\x1b[32m",
|
||||||
|
"yellow": "\x1b[33m",
|
||||||
|
"bold": "\x1b[1m",
|
||||||
|
"reset": "\x1b[0m",
|
||||||
|
}
|
||||||
|
RE_COLORS = {k: re.escape(v) for k, v in COLORS.items()}
|
||||||
|
|
||||||
|
|
||||||
class Option:
|
class Option:
|
||||||
|
@ -623,7 +629,7 @@ class TestTerminalFunctional:
|
||||||
if request.config.pluginmanager.list_plugin_distinfo():
|
if request.config.pluginmanager.list_plugin_distinfo():
|
||||||
result.stdout.fnmatch_lines(["plugins: *"])
|
result.stdout.fnmatch_lines(["plugins: *"])
|
||||||
|
|
||||||
def test_header(self, testdir, request):
|
def test_header(self, testdir):
|
||||||
testdir.tmpdir.join("tests").ensure_dir()
|
testdir.tmpdir.join("tests").ensure_dir()
|
||||||
testdir.tmpdir.join("gui").ensure_dir()
|
testdir.tmpdir.join("gui").ensure_dir()
|
||||||
|
|
||||||
|
@ -709,7 +715,7 @@ class TestTerminalFunctional:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_verbose_reporting(self, verbose_testfile, testdir, pytestconfig):
|
def test_verbose_reporting(self, verbose_testfile, testdir):
|
||||||
result = testdir.runpytest(
|
result = testdir.runpytest(
|
||||||
verbose_testfile, "-v", "-Walways::pytest.PytestWarning"
|
verbose_testfile, "-v", "-Walways::pytest.PytestWarning"
|
||||||
)
|
)
|
||||||
|
@ -879,10 +885,70 @@ def test_pass_output_reporting(testdir):
|
||||||
|
|
||||||
|
|
||||||
def test_color_yes(testdir):
|
def test_color_yes(testdir):
|
||||||
testdir.makepyfile("def test_this(): assert 1")
|
p1 = testdir.makepyfile(
|
||||||
result = testdir.runpytest("--color=yes")
|
"""
|
||||||
assert "test session starts" in result.stdout.str()
|
def fail():
|
||||||
assert "\x1b[1m" in result.stdout.str()
|
assert 0
|
||||||
|
|
||||||
|
def test_this():
|
||||||
|
fail()
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest("--color=yes", str(p1))
|
||||||
|
if sys.version_info < (3, 6):
|
||||||
|
# py36 required for ordered markup
|
||||||
|
output = result.stdout.str()
|
||||||
|
assert "test session starts" in output
|
||||||
|
assert "\x1b[1m" in output
|
||||||
|
return
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
line.format(**COLORS).replace("[", "[[]")
|
||||||
|
for line in [
|
||||||
|
"{bold}=*= test session starts =*={reset}",
|
||||||
|
"collected 1 item",
|
||||||
|
"",
|
||||||
|
"test_color_yes.py {red}F{reset}{red} * [100%]{reset}",
|
||||||
|
"",
|
||||||
|
"=*= FAILURES =*=",
|
||||||
|
"{red}{bold}_*_ test_this _*_{reset}",
|
||||||
|
"",
|
||||||
|
"{bold} def test_this():{reset}",
|
||||||
|
"{bold}> fail(){reset}",
|
||||||
|
"",
|
||||||
|
"{bold}{red}test_color_yes.py{reset}:5: ",
|
||||||
|
"_ _ * _ _*",
|
||||||
|
"",
|
||||||
|
"{bold} def fail():{reset}",
|
||||||
|
"{bold}> assert 0{reset}",
|
||||||
|
"{bold}{red}E assert 0{reset}",
|
||||||
|
"",
|
||||||
|
"{bold}{red}test_color_yes.py{reset}:2: AssertionError",
|
||||||
|
"{red}=*= {red}{bold}1 failed{reset}{red} in *s{reset}{red} =*={reset}",
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
result = testdir.runpytest("--color=yes", "--tb=short", str(p1))
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
line.format(**COLORS).replace("[", "[[]")
|
||||||
|
for line in [
|
||||||
|
"{bold}=*= test session starts =*={reset}",
|
||||||
|
"collected 1 item",
|
||||||
|
"",
|
||||||
|
"test_color_yes.py {red}F{reset}{red} * [100%]{reset}",
|
||||||
|
"",
|
||||||
|
"=*= FAILURES =*=",
|
||||||
|
"{red}{bold}_*_ test_this _*_{reset}",
|
||||||
|
"{bold}{red}test_color_yes.py{reset}:5: in test_this",
|
||||||
|
"{bold} fail(){reset}",
|
||||||
|
"{bold}{red}test_color_yes.py{reset}:2: in fail",
|
||||||
|
"{bold} assert 0{reset}",
|
||||||
|
"{bold}{red}E assert 0{reset}",
|
||||||
|
"{red}=*= {red}{bold}1 failed{reset}{red} in *s{reset}{red} =*={reset}",
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_color_no(testdir):
|
def test_color_no(testdir):
|
||||||
|
@ -994,7 +1060,7 @@ def test_tbstyle_short(testdir):
|
||||||
assert "assert x" in s
|
assert "assert x" in s
|
||||||
|
|
||||||
|
|
||||||
def test_traceconfig(testdir, monkeypatch):
|
def test_traceconfig(testdir):
|
||||||
result = testdir.runpytest("--traceconfig")
|
result = testdir.runpytest("--traceconfig")
|
||||||
result.stdout.fnmatch_lines(["*active plugins*"])
|
result.stdout.fnmatch_lines(["*active plugins*"])
|
||||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||||
|
@ -1599,18 +1665,15 @@ class TestProgressOutputStyle:
|
||||||
def test_foobar(i): raise ValueError()
|
def test_foobar(i): raise ValueError()
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
output = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
output.stdout.re_match_lines(
|
result.stdout.re_match_lines(
|
||||||
[
|
[
|
||||||
r"test_bar.py ({green}\.{reset}){{10}}{green} \s+ \[ 50%\]{reset}".format(
|
line.format(**RE_COLORS)
|
||||||
green=GREEN, reset=RESET
|
for line in [
|
||||||
),
|
r"test_bar.py ({green}\.{reset}){{10}}{green} \s+ \[ 50%\]{reset}",
|
||||||
r"test_foo.py ({green}\.{reset}){{5}}{yellow} \s+ \[ 75%\]{reset}".format(
|
r"test_foo.py ({green}\.{reset}){{5}}{yellow} \s+ \[ 75%\]{reset}",
|
||||||
green=GREEN, reset=RESET, yellow=YELLOW
|
r"test_foobar.py ({red}F{reset}){{5}}{red} \s+ \[100%\]{reset}",
|
||||||
),
|
]
|
||||||
r"test_foobar.py ({red}F{reset}){{5}}{red} \s+ \[100%\]{reset}".format(
|
|
||||||
reset=RESET, red=RED
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
25
tox.ini
25
tox.ini
|
@ -20,11 +20,12 @@ envlist =
|
||||||
[testenv]
|
[testenv]
|
||||||
commands =
|
commands =
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:{env:_PYTEST_TOX_DEFAULT_POSARGS:}}
|
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest {posargs:{env:_PYTEST_TOX_DEFAULT_POSARGS:}}
|
||||||
|
doctesting: {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules --pyargs _pytest
|
||||||
coverage: coverage combine
|
coverage: coverage combine
|
||||||
coverage: coverage report -m
|
coverage: coverage report -m
|
||||||
passenv = USER USERNAME COVERAGE_* TRAVIS PYTEST_ADDOPTS TERM
|
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_DOCTESTING:} {env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:}
|
||||||
|
|
||||||
# Configuration to run with coverage similar to CI, e.g.
|
# Configuration to run with coverage similar to CI, e.g.
|
||||||
# "tox -e py37-coverage".
|
# "tox -e py37-coverage".
|
||||||
|
@ -33,6 +34,8 @@ setenv =
|
||||||
coverage: COVERAGE_FILE={toxinidir}/.coverage
|
coverage: COVERAGE_FILE={toxinidir}/.coverage
|
||||||
coverage: COVERAGE_PROCESS_START={toxinidir}/.coveragerc
|
coverage: COVERAGE_PROCESS_START={toxinidir}/.coveragerc
|
||||||
|
|
||||||
|
doctesting: _PYTEST_TOX_POSARGS_DOCTESTING=doc/en
|
||||||
|
|
||||||
nobyte: PYTHONDONTWRITEBYTECODE=1
|
nobyte: PYTHONDONTWRITEBYTECODE=1
|
||||||
|
|
||||||
lsof: _PYTEST_TOX_POSARGS_LSOF=--lsof
|
lsof: _PYTEST_TOX_POSARGS_LSOF=--lsof
|
||||||
|
@ -40,6 +43,7 @@ setenv =
|
||||||
xdist: _PYTEST_TOX_POSARGS_XDIST=-n auto
|
xdist: _PYTEST_TOX_POSARGS_XDIST=-n auto
|
||||||
extras = testing
|
extras = testing
|
||||||
deps =
|
deps =
|
||||||
|
doctesting: PyYAML
|
||||||
oldattrs: attrs==17.4.0
|
oldattrs: attrs==17.4.0
|
||||||
oldattrs: hypothesis<=4.38.1
|
oldattrs: hypothesis<=4.38.1
|
||||||
numpy: numpy
|
numpy: numpy
|
||||||
|
@ -59,6 +63,15 @@ commands = pre-commit run --all-files --show-diff-on-failure {posargs:}
|
||||||
extras = checkqa-mypy, testing
|
extras = checkqa-mypy, testing
|
||||||
commands = mypy {posargs:src testing}
|
commands = mypy {posargs:src testing}
|
||||||
|
|
||||||
|
[testenv:mypy-diff]
|
||||||
|
extras = checkqa-mypy, testing
|
||||||
|
deps =
|
||||||
|
lxml
|
||||||
|
diff-cover
|
||||||
|
commands =
|
||||||
|
-mypy --cobertura-xml-report {envtmpdir} {posargs:src testing}
|
||||||
|
diff-cover --fail-under=100 --compare-branch={env:DIFF_BRANCH:origin/{env:GITHUB_BASE_REF:master}} {envtmpdir}/cobertura.xml
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
|
@ -81,16 +94,6 @@ deps = -r{toxinidir}/doc/en/requirements.txt
|
||||||
commands =
|
commands =
|
||||||
sphinx-build -W -q --keep-going -b linkcheck . _build
|
sphinx-build -W -q --keep-going -b linkcheck . _build
|
||||||
|
|
||||||
[testenv:doctesting]
|
|
||||||
basepython = python3
|
|
||||||
skipsdist = True
|
|
||||||
deps =
|
|
||||||
{[testenv]deps}
|
|
||||||
PyYAML
|
|
||||||
commands =
|
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest doc/en
|
|
||||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules --pyargs _pytest
|
|
||||||
|
|
||||||
[testenv:regen]
|
[testenv:regen]
|
||||||
changedir = doc/en
|
changedir = doc/en
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
|
|
Loading…
Reference in New Issue