Merge pull request #7660 from nicoddemus/deprecated-features
This commit is contained in:
commit
d69abff2c7
|
@ -0,0 +1,18 @@
|
|||
As per our policy, the following features have been deprecated in the 5.X series and are now
|
||||
removed:
|
||||
|
||||
* The ``funcargnames`` read-only property of ``FixtureRequest``, ``Metafunc``, and ``Function`` classes. Use ``fixturenames`` attribute.
|
||||
|
||||
* ``@pytest.fixture`` no longer supports positional arguments, pass all arguments by keyword instead.
|
||||
|
||||
* Direct construction of ``Node`` subclasses now raise an error, use ``from_parent`` instead.
|
||||
|
||||
* The default value for ``junit_family`` has changed to ``xunit2``. If you require the old format, add ``junit_family=xunit1`` to your configuration file.
|
||||
|
||||
* The ``TerminalReporter`` no longer has a ``writer`` attribute. Plugin authors may use the public functions of the ``TerminalReporter`` instead of accessing the ``TerminalWriter`` object directly.
|
||||
|
||||
* The ``--result-log`` option has been removed. Users are recommended to use the `pytest-reportlog <https://github.com/pytest-dev/pytest-reportlog>`__ plugin instead.
|
||||
|
||||
|
||||
For more information consult
|
||||
`Deprecations and Removals <https://docs.pytest.org/en/stable/deprecations.html>`__ in the docs.
|
|
@ -30,11 +30,19 @@ This hook has an `item` parameter which cannot be serialized by ``pytest-xdist``
|
|||
Use the ``pytest_warning_recored`` hook instead, which replaces the ``item`` parameter
|
||||
by a ``nodeid`` parameter.
|
||||
|
||||
The ``pytest.collect`` module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 6.0
|
||||
|
||||
The ``pytest.collect`` module is no longer part of the public API, all its names
|
||||
should now be imported from ``pytest`` directly instead.
|
||||
|
||||
|
||||
The ``pytest._fillfuncargs`` function
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.5
|
||||
.. deprecated:: 6.0
|
||||
|
||||
This function was kept for backward compatibility with an older plugin.
|
||||
|
||||
|
@ -43,6 +51,11 @@ it, use `function._request._fillfixtures()` instead, though note this is not
|
|||
a public API and may break in the future.
|
||||
|
||||
|
||||
Removed Features
|
||||
----------------
|
||||
|
||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||
an appropriate period of deprecation has passed.
|
||||
|
||||
``--no-print-logs`` command-line option
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -59,38 +72,46 @@ display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log``
|
|||
|
||||
|
||||
|
||||
Node Construction changed to ``Node.from_parent``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Result log (``--result-log``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.4
|
||||
.. deprecated:: 4.0
|
||||
.. versionremoved:: 6.0
|
||||
|
||||
The construction of nodes now should use the named constructor ``from_parent``.
|
||||
This limitation in api surface intends to enable better/simpler refactoring of the collection tree.
|
||||
The ``--result-log`` option produces a stream of test reports which can be
|
||||
analysed at runtime, but it uses a custom format which requires users to implement their own
|
||||
parser.
|
||||
|
||||
This means that instead of :code:`MyItem(name="foo", parent=collector, obj=42)`
|
||||
one now has to invoke :code:`MyItem.from_parent(collector, name="foo")`.
|
||||
The `pytest-reportlog <https://github.com/pytest-dev/pytest-reportlog>`__ plugin provides a ``--report-log`` option, a more standard and extensible alternative, producing
|
||||
one JSON object per-line, and should cover the same use cases. Please try it out and provide feedback.
|
||||
|
||||
Plugins that wish to support older versions of pytest and suppress the warning can use
|
||||
`hasattr` to check if `from_parent` exists in that version:
|
||||
The ``pytest-reportlog`` plugin might even be merged into the core
|
||||
at some point, depending on the plans for the plugins and number of users using it.
|
||||
|
||||
.. code-block:: python
|
||||
``pytest_collect_directory`` hook
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if hasattr(MyItem, "from_parent"):
|
||||
item = MyItem.from_parent(collector, name="foo")
|
||||
item.obj = 42
|
||||
return item
|
||||
else:
|
||||
return MyItem(name="foo", parent=collector, obj=42)
|
||||
.. versionremoved:: 6.0
|
||||
|
||||
Note that ``from_parent`` should only be called with keyword arguments for the parameters.
|
||||
The ``pytest_collect_directory`` has not worked properly for years (it was called
|
||||
but the results were ignored). Users may consider using :func:`pytest_collection_modifyitems <_pytest.hookspec.pytest_collection_modifyitems>` instead.
|
||||
|
||||
TerminalReporter.writer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 6.0
|
||||
|
||||
The ``TerminalReporter.writer`` attribute has been deprecated and should no longer be used. This
|
||||
was inadvertently exposed as part of the public API of that plugin and ties it too much
|
||||
with ``py.io.TerminalWriter``.
|
||||
|
||||
Plugins that used ``TerminalReporter.writer`` directly should instead use ``TerminalReporter``
|
||||
methods that provide the same functionality.
|
||||
|
||||
``junit_family`` default value change to "xunit2"
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.2
|
||||
.. versionchanged:: 6.0
|
||||
|
||||
The default value of ``junit_family`` option will change to ``xunit2`` in pytest 6.0, which
|
||||
is an update of the old ``xunit1`` format and is supported by default in modern tools
|
||||
|
@ -126,11 +147,44 @@ Services known to support the ``xunit2`` format:
|
|||
* `Jenkins <https://www.jenkins.io/>`__ with the `JUnit <https://plugins.jenkins.io/junit>`__ plugin.
|
||||
* `Azure Pipelines <https://azure.microsoft.com/en-us/services/devops/pipelines>`__.
|
||||
|
||||
Node Construction changed to ``Node.from_parent``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionchanged:: 6.0
|
||||
|
||||
The construction of nodes now should use the named constructor ``from_parent``.
|
||||
This limitation in api surface intends to enable better/simpler refactoring of the collection tree.
|
||||
|
||||
This means that instead of :code:`MyItem(name="foo", parent=collector, obj=42)`
|
||||
one now has to invoke :code:`MyItem.from_parent(collector, name="foo")`.
|
||||
|
||||
Plugins that wish to support older versions of pytest and suppress the warning can use
|
||||
`hasattr` to check if `from_parent` exists in that version:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if hasattr(MyItem, "from_parent"):
|
||||
item = MyItem.from_parent(collector, name="foo")
|
||||
item.obj = 42
|
||||
return item
|
||||
else:
|
||||
return MyItem(name="foo", parent=collector, obj=42)
|
||||
|
||||
Note that ``from_parent`` should only be called with keyword arguments for the parameters.
|
||||
|
||||
|
||||
``pytest.fixture`` arguments are keyword only
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionremoved:: 6.0
|
||||
|
||||
Passing arguments to pytest.fixture() as positional arguments has been removed - pass them by keyword instead.
|
||||
|
||||
``funcargnames`` alias for ``fixturenames``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.0
|
||||
.. versionremoved:: 6.0
|
||||
|
||||
The ``FixtureRequest``, ``Metafunc``, and ``Function`` classes track the names of
|
||||
their associated fixtures, with the aptly-named ``fixturenames`` attribute.
|
||||
|
@ -141,42 +195,6 @@ in places where we or plugin authors must distinguish between fixture names and
|
|||
names supplied by non-fixture things such as ``pytest.mark.parametrize``.
|
||||
|
||||
|
||||
Result log (``--result-log``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 4.0
|
||||
|
||||
The ``--result-log`` option produces a stream of test reports which can be
|
||||
analysed at runtime, but it uses a custom format which requires users to implement their own
|
||||
parser.
|
||||
|
||||
The `pytest-reportlog <https://github.com/pytest-dev/pytest-reportlog>`__ plugin provides a ``--report-log`` option, a more standard and extensible alternative, producing
|
||||
one JSON object per-line, and should cover the same use cases. Please try it out and provide feedback.
|
||||
|
||||
The plan is remove the ``--result-log`` option in pytest 6.0 if ``pytest-reportlog`` proves satisfactory
|
||||
to all users and is deemed stable. The ``pytest-reportlog`` plugin might even be merged into the core
|
||||
at some point, depending on the plans for the plugins and number of users using it.
|
||||
|
||||
TerminalReporter.writer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.4
|
||||
|
||||
The ``TerminalReporter.writer`` attribute has been deprecated and should no longer be used. This
|
||||
was inadvertently exposed as part of the public API of that plugin and ties it too much
|
||||
with ``py.io.TerminalWriter``.
|
||||
|
||||
Plugins that used ``TerminalReporter.writer`` directly should instead use ``TerminalReporter``
|
||||
methods that provide the same functionality.
|
||||
|
||||
|
||||
Removed Features
|
||||
----------------
|
||||
|
||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||
an appropriate period of deprecation has passed.
|
||||
|
||||
|
||||
``pytest.config`` global
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -656,7 +656,6 @@ Collection hooks
|
|||
|
||||
.. autofunction:: pytest_collection
|
||||
.. autofunction:: pytest_ignore_collect
|
||||
.. autofunction:: pytest_collect_directory
|
||||
.. autofunction:: pytest_collect_file
|
||||
.. autofunction:: pytest_pycollect_makemodule
|
||||
|
||||
|
|
|
@ -239,7 +239,6 @@ default_plugins = essential_plugins + (
|
|||
"nose",
|
||||
"assertion",
|
||||
"junitxml",
|
||||
"resultlog",
|
||||
"doctest",
|
||||
"cacheprovider",
|
||||
"freeze_support",
|
||||
|
|
|
@ -19,45 +19,12 @@ DEPRECATED_EXTERNAL_PLUGINS = {
|
|||
"pytest_faulthandler",
|
||||
}
|
||||
|
||||
FUNCARGNAMES = PytestDeprecationWarning(
|
||||
"The `funcargnames` attribute was an alias for `fixturenames`, "
|
||||
"since pytest 2.3 - use the newer attribute instead."
|
||||
)
|
||||
|
||||
FILLFUNCARGS = PytestDeprecationWarning(
|
||||
"The `_fillfuncargs` function is deprecated, use "
|
||||
"function._request._fillfixtures() instead if you cannot avoid reaching into internals."
|
||||
)
|
||||
|
||||
RESULT_LOG = PytestDeprecationWarning(
|
||||
"--result-log is deprecated, please try the new pytest-reportlog plugin.\n"
|
||||
"See https://docs.pytest.org/en/stable/deprecations.html#result-log-result-log for more information."
|
||||
)
|
||||
|
||||
FIXTURE_POSITIONAL_ARGUMENTS = PytestDeprecationWarning(
|
||||
"Passing arguments to pytest.fixture() as positional arguments is deprecated - pass them "
|
||||
"as a keyword argument instead."
|
||||
)
|
||||
|
||||
NODE_USE_FROM_PARENT = UnformattedWarning(
|
||||
PytestDeprecationWarning,
|
||||
"Direct construction of {name} has been deprecated, please use {name}.from_parent.\n"
|
||||
"See "
|
||||
"https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent"
|
||||
" for more details.",
|
||||
)
|
||||
|
||||
JUNIT_XML_DEFAULT_FAMILY = PytestDeprecationWarning(
|
||||
"The 'junit_family' default value will change to 'xunit2' in pytest 6.0. See:\n"
|
||||
" https://docs.pytest.org/en/stable/deprecations.html#junit-family-default-value-change-to-xunit2\n"
|
||||
"for more information."
|
||||
)
|
||||
|
||||
COLLECT_DIRECTORY_HOOK = PytestDeprecationWarning(
|
||||
"The pytest_collect_directory hook is not working.\n"
|
||||
"Please use collect_ignore in conftests or pytest_collection_modifyitems."
|
||||
)
|
||||
|
||||
PYTEST_COLLECT_MODULE = UnformattedWarning(
|
||||
PytestDeprecationWarning,
|
||||
"pytest.collect.{name} was moved to pytest.{name}\n"
|
||||
|
@ -65,12 +32,6 @@ PYTEST_COLLECT_MODULE = UnformattedWarning(
|
|||
)
|
||||
|
||||
|
||||
TERMINALWRITER_WRITER = PytestDeprecationWarning(
|
||||
"The TerminalReporter.writer attribute is deprecated, use TerminalReporter._tw instead at your own risk.\n"
|
||||
"See https://docs.pytest.org/en/stable/deprecations.html#terminalreporter-writer for more information."
|
||||
)
|
||||
|
||||
|
||||
MINUS_K_DASH = PytestDeprecationWarning(
|
||||
"The `-k '-expr'` syntax to -k is deprecated.\nUse `-k 'not expr'` instead."
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@ import functools
|
|||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
from collections import deque
|
||||
from types import TracebackType
|
||||
|
@ -46,8 +45,6 @@ from _pytest.compat import TYPE_CHECKING
|
|||
from _pytest.config import _PluggyPlugin
|
||||
from _pytest.config import Config
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
||||
from _pytest.deprecated import FUNCARGNAMES
|
||||
from _pytest.mark import ParameterSet
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.outcomes import TEST_OUTCOME
|
||||
|
@ -457,12 +454,6 @@ class FixtureRequest:
|
|||
result.extend(set(self._fixture_defs).difference(result))
|
||||
return result
|
||||
|
||||
@property
|
||||
def funcargnames(self) -> List[str]:
|
||||
"""Alias attribute for ``fixturenames`` for pre-2.3 compatibility."""
|
||||
warnings.warn(FUNCARGNAMES, stacklevel=2)
|
||||
return self.fixturenames
|
||||
|
||||
@property
|
||||
def node(self):
|
||||
"""Underlying collection node (depends on current request scope)."""
|
||||
|
@ -1253,7 +1244,7 @@ def fixture( # noqa: F811
|
|||
|
||||
def fixture( # noqa: F811
|
||||
fixture_function: Optional[_FixtureFunction] = None,
|
||||
*args: Any,
|
||||
*,
|
||||
scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
|
||||
params: Optional[Iterable[object]] = None,
|
||||
autouse: bool = False,
|
||||
|
@ -1315,53 +1306,6 @@ def fixture( # noqa: F811
|
|||
name the decorated function ``fixture_<fixturename>`` and then use
|
||||
``@pytest.fixture(name='<fixturename>')``.
|
||||
"""
|
||||
# Positional arguments backward compatibility.
|
||||
# If a kwarg is equal to its default, assume it was not explicitly
|
||||
# passed, i.e. not duplicated. The more correct way is to use a
|
||||
# **kwargs and check `in`, but that obfuscates the function signature.
|
||||
if isinstance(fixture_function, str):
|
||||
# It's actually the first positional argument, scope.
|
||||
args = (fixture_function, *args) # type: ignore[unreachable]
|
||||
fixture_function = None
|
||||
duplicated_args = []
|
||||
if len(args) > 0:
|
||||
if scope == "function":
|
||||
scope = args[0]
|
||||
else:
|
||||
duplicated_args.append("scope")
|
||||
if len(args) > 1:
|
||||
if params is None:
|
||||
params = args[1]
|
||||
else:
|
||||
duplicated_args.append("params")
|
||||
if len(args) > 2:
|
||||
if autouse is False:
|
||||
autouse = args[2]
|
||||
else:
|
||||
duplicated_args.append("autouse")
|
||||
if len(args) > 3:
|
||||
if ids is None:
|
||||
ids = args[3]
|
||||
else:
|
||||
duplicated_args.append("ids")
|
||||
if len(args) > 4:
|
||||
if name is None:
|
||||
name = args[4]
|
||||
else:
|
||||
duplicated_args.append("name")
|
||||
if len(args) > 5:
|
||||
raise TypeError(
|
||||
"fixture() takes 5 positional arguments but {} were given".format(len(args))
|
||||
)
|
||||
if duplicated_args:
|
||||
raise TypeError(
|
||||
"The fixture arguments are defined as positional and keyword: {}. "
|
||||
"Use only keyword arguments.".format(", ".join(duplicated_args))
|
||||
)
|
||||
if args:
|
||||
warnings.warn(FIXTURE_POSITIONAL_ARGUMENTS, stacklevel=2)
|
||||
# End backward compatiblity.
|
||||
|
||||
fixture_marker = FixtureFunctionMarker(
|
||||
scope=scope, params=params, autouse=autouse, ids=ids, name=name,
|
||||
)
|
||||
|
|
|
@ -12,7 +12,6 @@ from typing import Union
|
|||
import py.path
|
||||
from pluggy import HookspecMarker
|
||||
|
||||
from .deprecated import COLLECT_DIRECTORY_HOOK
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -262,16 +261,6 @@ def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[boo
|
|||
"""
|
||||
|
||||
|
||||
@hookspec(firstresult=True, warn_on_impl=COLLECT_DIRECTORY_HOOK)
|
||||
def pytest_collect_directory(path: py.path.local, parent) -> Optional[object]:
|
||||
"""Called before traversing a directory for collection files.
|
||||
|
||||
Stops at first non-None result, see :ref:`firstresult`.
|
||||
|
||||
:param py.path.local path: The path to analyze.
|
||||
"""
|
||||
|
||||
|
||||
def pytest_collect_file(path: py.path.local, parent) -> "Optional[Collector]":
|
||||
"""Return collection Node or None for the given path.
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ from typing import Tuple
|
|||
from typing import Union
|
||||
|
||||
import pytest
|
||||
from _pytest import deprecated
|
||||
from _pytest import nodes
|
||||
from _pytest import timing
|
||||
from _pytest._code.code import ExceptionRepr
|
||||
|
@ -33,7 +32,6 @@ from _pytest.fixtures import FixtureRequest
|
|||
from _pytest.reports import TestReport
|
||||
from _pytest.store import StoreKey
|
||||
from _pytest.terminal import TerminalReporter
|
||||
from _pytest.warnings import _issue_warning_captured
|
||||
|
||||
|
||||
xml_key = StoreKey["LogXML"]()
|
||||
|
@ -413,7 +411,9 @@ def pytest_addoption(parser: Parser) -> None:
|
|||
default="total",
|
||||
) # choices=['total', 'call'])
|
||||
parser.addini(
|
||||
"junit_family", "Emit XML for schema: one of legacy|xunit1|xunit2", default=None
|
||||
"junit_family",
|
||||
"Emit XML for schema: one of legacy|xunit1|xunit2",
|
||||
default="xunit2",
|
||||
)
|
||||
|
||||
|
||||
|
@ -422,9 +422,6 @@ def pytest_configure(config: Config) -> None:
|
|||
# Prevent opening xmllog on worker nodes (xdist).
|
||||
if xmlpath and not hasattr(config, "workerinput"):
|
||||
junit_family = config.getini("junit_family")
|
||||
if not junit_family:
|
||||
_issue_warning_captured(deprecated.JUNIT_XML_DEFAULT_FAMILY, config.hook, 2)
|
||||
junit_family = "xunit1"
|
||||
config._store[xml_key] = LogXML(
|
||||
xmlpath,
|
||||
config.option.junitprefix,
|
||||
|
|
|
@ -26,7 +26,6 @@ from _pytest.compat import TYPE_CHECKING
|
|||
from _pytest.config import Config
|
||||
from _pytest.config import ConftestImportFailure
|
||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||
from _pytest.deprecated import NODE_USE_FROM_PARENT
|
||||
from _pytest.fixtures import FixtureDef
|
||||
from _pytest.fixtures import FixtureLookupError
|
||||
from _pytest.mark.structures import Mark
|
||||
|
@ -97,8 +96,13 @@ _NodeType = TypeVar("_NodeType", bound="Node")
|
|||
|
||||
class NodeMeta(type):
|
||||
def __call__(self, *k, **kw):
|
||||
warnings.warn(NODE_USE_FROM_PARENT.format(name=self.__name__), stacklevel=2)
|
||||
return super().__call__(*k, **kw)
|
||||
msg = (
|
||||
"Direct construction of {name} has been deprecated, please use {name}.from_parent.\n"
|
||||
"See "
|
||||
"https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent"
|
||||
" for more details."
|
||||
).format(name=self.__name__)
|
||||
fail(msg, pytrace=False)
|
||||
|
||||
def _create(self, *k, **kw):
|
||||
return super().__call__(*k, **kw)
|
||||
|
@ -549,8 +553,6 @@ class FSCollector(Collector):
|
|||
for pat in self._norecursepatterns:
|
||||
if path.check(fnmatch=pat):
|
||||
return False
|
||||
ihook = self.session.gethookproxy(path)
|
||||
ihook.pytest_collect_directory(path=path, parent=self)
|
||||
return True
|
||||
|
||||
def _collectfile(
|
||||
|
|
|
@ -53,7 +53,6 @@ from _pytest.config import ExitCode
|
|||
from _pytest.config import hookimpl
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||
from _pytest.deprecated import FUNCARGNAMES
|
||||
from _pytest.fixtures import FuncFixtureInfo
|
||||
from _pytest.main import Session
|
||||
from _pytest.mark import MARK_GEN
|
||||
|
@ -906,12 +905,6 @@ class Metafunc:
|
|||
self._calls = [] # type: List[CallSpec2]
|
||||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs
|
||||
|
||||
@property
|
||||
def funcargnames(self) -> List[str]:
|
||||
"""Alias attribute for ``fixturenames`` for pre-2.3 compatibility."""
|
||||
warnings.warn(FUNCARGNAMES, stacklevel=2)
|
||||
return self.fixturenames
|
||||
|
||||
def parametrize(
|
||||
self,
|
||||
argnames: Union[str, List[str], Tuple[str, ...]],
|
||||
|
@ -1568,12 +1561,6 @@ class Function(PyobjMixin, nodes.Item):
|
|||
"""(compatonly) for code expecting pytest-2.2 style request objects."""
|
||||
return self
|
||||
|
||||
@property
|
||||
def funcargnames(self) -> List[str]:
|
||||
"""Alias attribute for ``fixturenames`` for pre-2.3 compatibility."""
|
||||
warnings.warn(FUNCARGNAMES, stacklevel=2)
|
||||
return self.fixturenames
|
||||
|
||||
def runtest(self) -> None:
|
||||
"""Execute the underlying test function."""
|
||||
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
"""log machine-parseable test session result information to a plain text file."""
|
||||
import os
|
||||
from typing import IO
|
||||
from typing import Union
|
||||
|
||||
from _pytest._code.code import ExceptionRepr
|
||||
from _pytest.config import Config
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.reports import CollectReport
|
||||
from _pytest.reports import TestReport
|
||||
from _pytest.store import StoreKey
|
||||
|
||||
|
||||
resultlog_key = StoreKey["ResultLog"]()
|
||||
|
||||
|
||||
def pytest_addoption(parser: Parser) -> None:
|
||||
group = parser.getgroup("terminal reporting", "resultlog plugin options")
|
||||
group.addoption(
|
||||
"--resultlog",
|
||||
"--result-log",
|
||||
action="store",
|
||||
metavar="path",
|
||||
default=None,
|
||||
help="DEPRECATED path for machine-readable result log.",
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config: Config) -> None:
|
||||
resultlog = config.option.resultlog
|
||||
# Prevent opening resultlog on worker nodes (xdist).
|
||||
if resultlog and not hasattr(config, "workerinput"):
|
||||
dirname = os.path.dirname(os.path.abspath(resultlog))
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
logfile = open(resultlog, "w", 1) # line buffered
|
||||
config._store[resultlog_key] = ResultLog(config, logfile)
|
||||
config.pluginmanager.register(config._store[resultlog_key])
|
||||
|
||||
from _pytest.deprecated import RESULT_LOG
|
||||
from _pytest.warnings import _issue_warning_captured
|
||||
|
||||
_issue_warning_captured(RESULT_LOG, config.hook, stacklevel=2)
|
||||
|
||||
|
||||
def pytest_unconfigure(config: Config) -> None:
|
||||
resultlog = config._store.get(resultlog_key, None)
|
||||
if resultlog:
|
||||
resultlog.logfile.close()
|
||||
del config._store[resultlog_key]
|
||||
config.pluginmanager.unregister(resultlog)
|
||||
|
||||
|
||||
class ResultLog:
|
||||
def __init__(self, config: Config, logfile: IO[str]) -> None:
|
||||
self.config = config
|
||||
self.logfile = logfile # preferably line buffered
|
||||
|
||||
def write_log_entry(self, testpath: str, lettercode: str, longrepr: str) -> None:
|
||||
print("{} {}".format(lettercode, testpath), file=self.logfile)
|
||||
for line in longrepr.splitlines():
|
||||
print(" %s" % line, file=self.logfile)
|
||||
|
||||
def log_outcome(
|
||||
self, report: Union[TestReport, CollectReport], lettercode: str, longrepr: str
|
||||
) -> None:
|
||||
testpath = getattr(report, "nodeid", None)
|
||||
if testpath is None:
|
||||
testpath = report.fspath
|
||||
self.write_log_entry(testpath, lettercode, longrepr)
|
||||
|
||||
def pytest_runtest_logreport(self, report: TestReport) -> None:
|
||||
if report.when != "call" and report.passed:
|
||||
return
|
||||
res = self.config.hook.pytest_report_teststatus(
|
||||
report=report, config=self.config
|
||||
)
|
||||
code = res[1] # type: str
|
||||
if code == "x":
|
||||
longrepr = str(report.longrepr)
|
||||
elif code == "X":
|
||||
longrepr = ""
|
||||
elif report.passed:
|
||||
longrepr = ""
|
||||
elif report.skipped:
|
||||
assert isinstance(report.longrepr, tuple)
|
||||
longrepr = str(report.longrepr[2])
|
||||
else:
|
||||
longrepr = str(report.longrepr)
|
||||
self.log_outcome(report, code, longrepr)
|
||||
|
||||
def pytest_collectreport(self, report: CollectReport) -> None:
|
||||
if not report.passed:
|
||||
if report.failed:
|
||||
code = "F"
|
||||
longrepr = str(report.longrepr)
|
||||
else:
|
||||
assert report.skipped
|
||||
code = "S"
|
||||
longrepr = "%s:%d: %s" % report.longrepr # type: ignore
|
||||
self.log_outcome(report, code, longrepr)
|
||||
|
||||
def pytest_internalerror(self, excrepr: ExceptionRepr) -> None:
|
||||
if excrepr.reprcrash is not None:
|
||||
path = excrepr.reprcrash.path
|
||||
else:
|
||||
path = "cwd:%s" % os.getcwd()
|
||||
self.write_log_entry(path, "!", str(excrepr))
|
|
@ -31,7 +31,6 @@ from _pytest import nodes
|
|||
from _pytest import timing
|
||||
from _pytest._code import ExceptionInfo
|
||||
from _pytest._code.code import ExceptionRepr
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest._io.wcwidth import wcswidth
|
||||
from _pytest.compat import order_preserving_dict
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
|
@ -39,7 +38,6 @@ from _pytest.config import _PluggyPlugin
|
|||
from _pytest.config import Config
|
||||
from _pytest.config import ExitCode
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.deprecated import TERMINALWRITER_WRITER
|
||||
from _pytest.nodes import Item
|
||||
from _pytest.nodes import Node
|
||||
from _pytest.reports import BaseReport
|
||||
|
@ -335,16 +333,6 @@ class TerminalReporter:
|
|||
self._already_displayed_warnings = None # type: Optional[int]
|
||||
self._keyboardinterrupt_memo = None # type: Optional[ExceptionRepr]
|
||||
|
||||
@property
|
||||
def writer(self) -> TerminalWriter:
|
||||
warnings.warn(TERMINALWRITER_WRITER, stacklevel=2)
|
||||
return self._tw
|
||||
|
||||
@writer.setter
|
||||
def writer(self, value: TerminalWriter) -> None:
|
||||
warnings.warn(TERMINALWRITER_WRITER, stacklevel=2)
|
||||
self._tw = value
|
||||
|
||||
def _determine_show_progress_info(self) -> "Literal['progress', 'count', False]":
|
||||
"""Return whether we should display progress information based on the current config."""
|
||||
# do not show progress if we are not capturing output (#3038)
|
||||
|
|
|
@ -104,8 +104,6 @@ def catch_warnings_for_item(
|
|||
warnings.filterwarnings("always", category=DeprecationWarning)
|
||||
warnings.filterwarnings("always", category=PendingDeprecationWarning)
|
||||
|
||||
warnings.filterwarnings("error", category=pytest.PytestDeprecationWarning)
|
||||
|
||||
# Filters should have this precedence: mark, cmdline options, ini.
|
||||
# Filters should be applied in the inverse order of precedence.
|
||||
for arg in inifilters:
|
||||
|
|
|
@ -223,13 +223,12 @@ class TestGeneralUsage:
|
|||
"E {}: No module named 'qwerty'".format(exc_name),
|
||||
]
|
||||
|
||||
@pytest.mark.filterwarnings("ignore::pytest.PytestDeprecationWarning")
|
||||
def test_early_skip(self, testdir):
|
||||
testdir.mkdir("xyz")
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
import pytest
|
||||
def pytest_collect_directory():
|
||||
def pytest_collect_file():
|
||||
pytest.skip("early")
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -1,35 +1,11 @@
|
|||
import copy
|
||||
import inspect
|
||||
import warnings
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from _pytest import deprecated
|
||||
from _pytest import nodes
|
||||
from _pytest.config import Config
|
||||
from _pytest.pytester import Testdir
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("default")
|
||||
def test_resultlog_is_deprecated(testdir):
|
||||
result = testdir.runpytest("--help")
|
||||
result.stdout.fnmatch_lines(["*DEPRECATED path for machine-readable result log*"])
|
||||
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--result-log=%s" % testdir.tmpdir.join("result.log"))
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*--result-log is deprecated, please try the new pytest-reportlog plugin.",
|
||||
"*See https://docs.pytest.org/en/stable/deprecations.html#result-log-result-log for more information*",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="should be reintroduced in 6.1: #7361")
|
||||
@pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore
|
||||
# false positive due to dynamic attribute
|
||||
|
@ -38,35 +14,6 @@ def test_pytest_collect_module_deprecated(attribute):
|
|||
getattr(pytest.collect, attribute)
|
||||
|
||||
|
||||
def test_terminal_reporter_writer_attr(pytestconfig: Config) -> None:
|
||||
"""Check that TerminalReporter._tw is also available as 'writer' (#2984)
|
||||
This attribute has been deprecated in 5.4.
|
||||
"""
|
||||
try:
|
||||
import xdist # noqa
|
||||
|
||||
pytest.skip("xdist workers disable the terminal reporter plugin")
|
||||
except ImportError:
|
||||
pass
|
||||
terminal_reporter = pytestconfig.pluginmanager.get_plugin("terminalreporter")
|
||||
original_tw = terminal_reporter._tw
|
||||
|
||||
with pytest.warns(pytest.PytestDeprecationWarning) as cw:
|
||||
assert terminal_reporter.writer is original_tw
|
||||
assert len(cw) == 1
|
||||
assert cw[0].filename == __file__
|
||||
|
||||
new_tw = copy.copy(original_tw)
|
||||
with pytest.warns(pytest.PytestDeprecationWarning) as cw:
|
||||
terminal_reporter.writer = new_tw
|
||||
try:
|
||||
assert terminal_reporter._tw is new_tw
|
||||
finally:
|
||||
terminal_reporter.writer = original_tw
|
||||
assert len(cw) == 2
|
||||
assert cw[0].filename == cw[1].filename == __file__
|
||||
|
||||
|
||||
@pytest.mark.parametrize("plugin", sorted(deprecated.DEPRECATED_EXTERNAL_PLUGINS))
|
||||
@pytest.mark.filterwarnings("default")
|
||||
def test_external_plugins_integrated(testdir, plugin):
|
||||
|
@ -77,49 +24,6 @@ def test_external_plugins_integrated(testdir, plugin):
|
|||
testdir.parseconfig("-p", plugin)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("junit_family", [None, "legacy", "xunit2"])
|
||||
def test_warn_about_imminent_junit_family_default_change(testdir, junit_family):
|
||||
"""Show a warning if junit_family is not defined and --junitxml is used (#6179)"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_foo():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
if junit_family:
|
||||
testdir.makeini(
|
||||
"""
|
||||
[pytest]
|
||||
junit_family={junit_family}
|
||||
""".format(
|
||||
junit_family=junit_family
|
||||
)
|
||||
)
|
||||
|
||||
result = testdir.runpytest("--junit-xml=foo.xml")
|
||||
warning_msg = (
|
||||
"*PytestDeprecationWarning: The 'junit_family' default value will change*"
|
||||
)
|
||||
if junit_family:
|
||||
result.stdout.no_fnmatch_line(warning_msg)
|
||||
else:
|
||||
result.stdout.fnmatch_lines([warning_msg])
|
||||
|
||||
|
||||
def test_node_direct_ctor_warning() -> None:
|
||||
class MockConfig:
|
||||
pass
|
||||
|
||||
ms = MockConfig()
|
||||
with pytest.warns(
|
||||
DeprecationWarning,
|
||||
match="Direct construction of .* has been deprecated, please use .*.from_parent.*",
|
||||
) as w:
|
||||
nodes.Node(name="test", config=ms, session=ms, nodeid="None") # type: ignore
|
||||
assert w[0].lineno == inspect.currentframe().f_lineno - 1 # type: ignore
|
||||
assert w[0].filename == __file__
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="should be reintroduced in 6.1: #7361")
|
||||
def test_fillfuncargs_is_deprecated() -> None:
|
||||
with pytest.warns(
|
||||
|
|
|
@ -815,28 +815,6 @@ class TestRequestBasic:
|
|||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||
|
||||
def test_funcargnames_compatattr(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
def pytest_generate_tests(metafunc):
|
||||
with pytest.warns(pytest.PytestDeprecationWarning):
|
||||
assert metafunc.funcargnames == metafunc.fixturenames
|
||||
@pytest.fixture
|
||||
def fn(request):
|
||||
with pytest.warns(pytest.PytestDeprecationWarning):
|
||||
assert request._pyfuncitem.funcargnames == \
|
||||
request._pyfuncitem.fixturenames
|
||||
with pytest.warns(pytest.PytestDeprecationWarning):
|
||||
return request.funcargnames, request.fixturenames
|
||||
|
||||
def test_hello(fn):
|
||||
assert fn[0] == fn[1]
|
||||
"""
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
def test_setupdecorator_and_xunit(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
|
@ -4152,73 +4130,6 @@ def test_fixture_named_request(testdir):
|
|||
)
|
||||
|
||||
|
||||
def test_fixture_duplicated_arguments() -> None:
|
||||
"""Raise error if there are positional and keyword arguments for the same parameter (#1682)."""
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
|
||||
@pytest.fixture("session", scope="session") # type: ignore[call-overload]
|
||||
def arg(arg):
|
||||
pass
|
||||
|
||||
assert (
|
||||
str(excinfo.value)
|
||||
== "The fixture arguments are defined as positional and keyword: scope. "
|
||||
"Use only keyword arguments."
|
||||
)
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
|
||||
@pytest.fixture( # type: ignore[call-overload]
|
||||
"function",
|
||||
["p1"],
|
||||
True,
|
||||
["id1"],
|
||||
"name",
|
||||
scope="session",
|
||||
params=["p1"],
|
||||
autouse=True,
|
||||
ids=["id1"],
|
||||
name="name",
|
||||
)
|
||||
def arg2(request):
|
||||
pass
|
||||
|
||||
assert (
|
||||
str(excinfo.value)
|
||||
== "The fixture arguments are defined as positional and keyword: scope, params, autouse, ids, name. "
|
||||
"Use only keyword arguments."
|
||||
)
|
||||
|
||||
|
||||
def test_fixture_with_positionals() -> None:
|
||||
"""Raise warning, but the positionals should still works (#1682)."""
|
||||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
||||
|
||||
with pytest.warns(pytest.PytestDeprecationWarning) as warnings:
|
||||
|
||||
@pytest.fixture("function", [0], True) # type: ignore[call-overload]
|
||||
def fixture_with_positionals():
|
||||
pass
|
||||
|
||||
assert str(warnings[0].message) == str(FIXTURE_POSITIONAL_ARGUMENTS)
|
||||
|
||||
assert fixture_with_positionals._pytestfixturefunction.scope == "function"
|
||||
assert fixture_with_positionals._pytestfixturefunction.params == (0,)
|
||||
assert fixture_with_positionals._pytestfixturefunction.autouse
|
||||
|
||||
|
||||
def test_fixture_with_too_many_positionals() -> None:
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
|
||||
@pytest.fixture("function", [0], True, ["id"], "name", "extra") # type: ignore[call-overload]
|
||||
def fixture_with_positionals():
|
||||
pass
|
||||
|
||||
assert (
|
||||
str(excinfo.value) == "fixture() takes 5 positional arguments but 6 were given"
|
||||
)
|
||||
|
||||
|
||||
def test_indirect_fixture_does_not_break_scope(testdir):
|
||||
"""Ensure that fixture scope is respected when using indirect fixtures (#570)"""
|
||||
testdir.makepyfile(
|
||||
|
|
|
@ -257,20 +257,6 @@ class TestCollectPluginHookRelay:
|
|||
assert len(wascalled) == 1
|
||||
assert wascalled[0].ext == ".abc"
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:.*pytest_collect_directory.*")
|
||||
def test_pytest_collect_directory(self, testdir):
|
||||
wascalled = []
|
||||
|
||||
class Plugin:
|
||||
def pytest_collect_directory(self, path):
|
||||
wascalled.append(path.basename)
|
||||
|
||||
testdir.mkdir("hello")
|
||||
testdir.mkdir("world")
|
||||
pytest.main(testdir.tmpdir, plugins=[Plugin()])
|
||||
assert "hello" in wascalled
|
||||
assert "world" in wascalled
|
||||
|
||||
|
||||
class TestPrunetraceback:
|
||||
def test_custom_repr_failure(self, testdir):
|
||||
|
|
|
@ -306,21 +306,6 @@ def test_no_conftest(testdir):
|
|||
assert result.ret == ExitCode.USAGE_ERROR
|
||||
|
||||
|
||||
def test_conftest_existing_resultlog(testdir):
|
||||
x = testdir.mkdir("tests")
|
||||
x.join("conftest.py").write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true")
|
||||
"""
|
||||
)
|
||||
)
|
||||
testdir.makefile(ext=".log", result="") # Writes result.log
|
||||
result = testdir.runpytest("-h", "--resultlog", "result.log")
|
||||
result.stdout.fnmatch_lines(["*--xyz*"])
|
||||
|
||||
|
||||
def test_conftest_existing_junitxml(testdir):
|
||||
x = testdir.mkdir("tests")
|
||||
x.join("conftest.py").write(
|
||||
|
|
|
@ -1,256 +0,0 @@
|
|||
import os
|
||||
from io import StringIO
|
||||
from typing import List
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
from _pytest.pytester import Testdir
|
||||
from _pytest.resultlog import pytest_configure
|
||||
from _pytest.resultlog import pytest_unconfigure
|
||||
from _pytest.resultlog import ResultLog
|
||||
from _pytest.resultlog import resultlog_key
|
||||
|
||||
|
||||
pytestmark = pytest.mark.filterwarnings("ignore:--result-log is deprecated")
|
||||
|
||||
|
||||
def test_write_log_entry() -> None:
|
||||
reslog = ResultLog(None, None) # type: ignore[arg-type]
|
||||
reslog.logfile = StringIO()
|
||||
reslog.write_log_entry("name", ".", "")
|
||||
entry = reslog.logfile.getvalue()
|
||||
assert entry[-1] == "\n"
|
||||
entry_lines = entry.splitlines()
|
||||
assert len(entry_lines) == 1
|
||||
assert entry_lines[0] == ". name"
|
||||
|
||||
reslog.logfile = StringIO()
|
||||
reslog.write_log_entry("name", "s", "Skipped")
|
||||
entry = reslog.logfile.getvalue()
|
||||
assert entry[-1] == "\n"
|
||||
entry_lines = entry.splitlines()
|
||||
assert len(entry_lines) == 2
|
||||
assert entry_lines[0] == "s name"
|
||||
assert entry_lines[1] == " Skipped"
|
||||
|
||||
reslog.logfile = StringIO()
|
||||
reslog.write_log_entry("name", "s", "Skipped\n")
|
||||
entry = reslog.logfile.getvalue()
|
||||
assert entry[-1] == "\n"
|
||||
entry_lines = entry.splitlines()
|
||||
assert len(entry_lines) == 2
|
||||
assert entry_lines[0] == "s name"
|
||||
assert entry_lines[1] == " Skipped"
|
||||
|
||||
reslog.logfile = StringIO()
|
||||
longrepr = " tb1\n tb 2\nE tb3\nSome Error"
|
||||
reslog.write_log_entry("name", "F", longrepr)
|
||||
entry = reslog.logfile.getvalue()
|
||||
assert entry[-1] == "\n"
|
||||
entry_lines = entry.splitlines()
|
||||
assert len(entry_lines) == 5
|
||||
assert entry_lines[0] == "F name"
|
||||
assert entry_lines[1:] == [" " + line for line in longrepr.splitlines()]
|
||||
|
||||
|
||||
class TestWithFunctionIntegration:
|
||||
# XXX (hpk) i think that the resultlog plugin should
|
||||
# provide a Parser object so that one can remain
|
||||
# ignorant regarding formatting details.
|
||||
def getresultlog(self, testdir: Testdir, arg: str) -> List[str]:
|
||||
resultlog = testdir.tmpdir.join("resultlog")
|
||||
testdir.plugins.append("resultlog")
|
||||
args = ["--resultlog=%s" % resultlog] + [arg]
|
||||
testdir.runpytest(*args)
|
||||
return [x for x in resultlog.readlines(cr=0) if x]
|
||||
|
||||
def test_collection_report(self, testdir: Testdir) -> None:
|
||||
ok = testdir.makepyfile(test_collection_ok="")
|
||||
fail = testdir.makepyfile(test_collection_fail="XXX")
|
||||
lines = self.getresultlog(testdir, ok)
|
||||
assert not lines
|
||||
|
||||
lines = self.getresultlog(testdir, fail)
|
||||
assert lines
|
||||
assert lines[0].startswith("F ")
|
||||
assert lines[0].endswith("test_collection_fail.py"), lines[0]
|
||||
for x in lines[1:]:
|
||||
assert x.startswith(" ")
|
||||
assert "XXX" in "".join(lines[1:])
|
||||
|
||||
def test_log_test_outcomes(self, testdir: Testdir) -> None:
|
||||
mod = testdir.makepyfile(
|
||||
test_mod="""
|
||||
import pytest
|
||||
def test_pass(): pass
|
||||
def test_skip(): pytest.skip("hello")
|
||||
def test_fail(): raise ValueError("FAIL")
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_xfail(): raise ValueError("XFAIL")
|
||||
@pytest.mark.xfail
|
||||
def test_xpass(): pass
|
||||
|
||||
"""
|
||||
)
|
||||
lines = self.getresultlog(testdir, mod)
|
||||
assert len(lines) >= 3
|
||||
assert lines[0].startswith(". ")
|
||||
assert lines[0].endswith("test_pass")
|
||||
assert lines[1].startswith("s "), lines[1]
|
||||
assert lines[1].endswith("test_skip")
|
||||
assert lines[2].find("hello") != -1
|
||||
|
||||
assert lines[3].startswith("F ")
|
||||
assert lines[3].endswith("test_fail")
|
||||
tb = "".join(lines[4:8])
|
||||
assert tb.find('raise ValueError("FAIL")') != -1
|
||||
|
||||
assert lines[8].startswith("x ")
|
||||
tb = "".join(lines[8:14])
|
||||
assert tb.find('raise ValueError("XFAIL")') != -1
|
||||
|
||||
assert lines[14].startswith("X ")
|
||||
assert len(lines) == 15
|
||||
|
||||
@pytest.mark.parametrize("style", ("native", "long", "short"))
|
||||
def test_internal_exception(self, style) -> None:
|
||||
# they are produced for example by a teardown failing
|
||||
# at the end of the run or a failing hook invocation
|
||||
try:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
||||
file = StringIO()
|
||||
reslog = ResultLog(None, file) # type: ignore[arg-type]
|
||||
reslog.pytest_internalerror(excinfo.getrepr(style=style))
|
||||
entry = file.getvalue()
|
||||
entry_lines = entry.splitlines()
|
||||
|
||||
assert entry_lines[0].startswith("! ")
|
||||
if style != "native":
|
||||
assert os.path.basename(__file__)[:-9] in entry_lines[0] # .pyc/class
|
||||
assert entry_lines[-1][0] == " "
|
||||
assert "ValueError" in entry
|
||||
|
||||
|
||||
def test_generic(testdir: Testdir, LineMatcher) -> None:
|
||||
testdir.plugins.append("resultlog")
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
def test_pass():
|
||||
pass
|
||||
def test_fail():
|
||||
assert 0
|
||||
def test_skip():
|
||||
pytest.skip("")
|
||||
@pytest.mark.xfail
|
||||
def test_xfail():
|
||||
assert 0
|
||||
@pytest.mark.xfail(run=False)
|
||||
def test_xfail_norun():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
testdir.runpytest("--resultlog=result.log")
|
||||
lines = testdir.tmpdir.join("result.log").readlines(cr=0)
|
||||
LineMatcher(lines).fnmatch_lines(
|
||||
[
|
||||
". *:test_pass",
|
||||
"F *:test_fail",
|
||||
"s *:test_skip",
|
||||
"x *:test_xfail",
|
||||
"x *:test_xfail_norun",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_makedir_for_resultlog(testdir: Testdir, LineMatcher) -> None:
|
||||
"""--resultlog should automatically create directories for the log file"""
|
||||
testdir.plugins.append("resultlog")
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
def test_pass():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
testdir.runpytest("--resultlog=path/to/result.log")
|
||||
lines = testdir.tmpdir.join("path/to/result.log").readlines(cr=0)
|
||||
LineMatcher(lines).fnmatch_lines([". *:test_pass"])
|
||||
|
||||
|
||||
def test_no_resultlog_on_workers(testdir: Testdir) -> None:
|
||||
config = testdir.parseconfig("-p", "resultlog", "--resultlog=resultlog")
|
||||
|
||||
assert resultlog_key not in config._store
|
||||
pytest_configure(config)
|
||||
assert resultlog_key in config._store
|
||||
pytest_unconfigure(config)
|
||||
assert resultlog_key not in config._store
|
||||
|
||||
config.workerinput = {} # type: ignore[attr-defined]
|
||||
pytest_configure(config)
|
||||
assert resultlog_key not in config._store
|
||||
pytest_unconfigure(config)
|
||||
assert resultlog_key not in config._store
|
||||
|
||||
|
||||
def test_unknown_teststatus(testdir: Testdir) -> None:
|
||||
"""Ensure resultlog correctly handles unknown status from pytest_report_teststatus
|
||||
|
||||
Inspired on pytest-rerunfailures.
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def pytest_report_teststatus(report):
|
||||
if report.outcome == 'rerun':
|
||||
return "rerun", "r", "RERUN"
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_makereport():
|
||||
res = yield
|
||||
report = res.get_result()
|
||||
if report.when == "call":
|
||||
report.outcome = 'rerun'
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--resultlog=result.log")
|
||||
result.stdout.fnmatch_lines(
|
||||
["test_unknown_teststatus.py r *[[]100%[]]", "* 1 rerun *"]
|
||||
)
|
||||
|
||||
lines = testdir.tmpdir.join("result.log").readlines(cr=0)
|
||||
assert lines[0] == "r test_unknown_teststatus.py::test"
|
||||
|
||||
|
||||
def test_failure_issue380(testdir: Testdir) -> None:
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
import pytest
|
||||
class MyCollector(pytest.File):
|
||||
def collect(self):
|
||||
raise ValueError()
|
||||
def repr_failure(self, excinfo):
|
||||
return "somestring"
|
||||
def pytest_collect_file(path, parent):
|
||||
return MyCollector(parent=parent, fspath=path)
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_func():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--resultlog=log")
|
||||
assert result.ret == 2
|
|
@ -513,6 +513,9 @@ class TestDeprecationWarningsByDefault:
|
|||
|
||||
|
||||
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
|
||||
@pytest.mark.skip(
|
||||
reason="This test should be enabled again before pytest 7.0 is released"
|
||||
)
|
||||
def test_deprecation_warning_as_error(testdir, change_default):
|
||||
"""This ensures that PytestDeprecationWarnings raised by pytest are turned into errors.
|
||||
|
||||
|
@ -737,31 +740,6 @@ class TestStackLevel:
|
|||
assert "config{sep}__init__.py".format(sep=os.sep) in file
|
||||
assert func == "import_plugin"
|
||||
|
||||
def test_issue4445_resultlog(self, testdir, capwarn):
|
||||
"""#4445: Make sure the warning points to a reasonable location
|
||||
See origin of _issue_warning_captured at: _pytest.resultlog.py:35
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_dummy():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
# Use parseconfigure() because the warning in resultlog.py is triggered in
|
||||
# the pytest_configure hook
|
||||
testdir.parseconfigure(
|
||||
"--result-log={dir}".format(dir=testdir.tmpdir.join("result.log"))
|
||||
)
|
||||
|
||||
# with stacklevel=2 the warning originates from resultlog.pytest_configure
|
||||
# and is thrown when --result-log is used
|
||||
warning, location = capwarn.captured.pop()
|
||||
file, _, func = location
|
||||
|
||||
assert "--result-log is deprecated" in str(warning.message)
|
||||
assert "resultlog.py" in file
|
||||
assert func == "pytest_configure"
|
||||
|
||||
def test_issue4445_issue5928_mark_generator(self, testdir):
|
||||
"""#4445 and #5928: Make sure the warning from an unknown mark points to
|
||||
the test file where this mark is used.
|
||||
|
|
Loading…
Reference in New Issue