Remove resultlog plugin

This commit is contained in:
Bruno Oliveira 2020-08-17 18:10:27 -03:00
parent b32c48ee05
commit ef946d557c
9 changed files with 10 additions and 441 deletions

View File

@ -11,6 +11,8 @@ removed:
* 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 ``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 For more information consult
`Deprecations and Removals <https://docs.pytest.org/en/stable/deprecations.html>`__ in the docs. `Deprecations and Removals <https://docs.pytest.org/en/stable/deprecations.html>`__ in the docs.

View File

@ -66,10 +66,17 @@ display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log``
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.
Result log (``--result-log``) Result log (``--result-log``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 4.0 .. deprecated:: 4.0
.. versionremoved:: 6.0
The ``--result-log`` option produces a stream of test reports which can be 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 analysed at runtime, but it uses a custom format which requires users to implement their own
@ -78,18 +85,9 @@ parser.
The `pytest-reportlog <https://github.com/pytest-dev/pytest-reportlog>`__ plugin provides a ``--report-log`` option, a more standard and extensible alternative, producing 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. 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 The ``pytest-reportlog`` plugin might even be merged into the core
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. at some point, depending on the plans for the plugins and number of users using it.
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_collect_directory`` hook ``pytest_collect_directory`` hook
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -239,7 +239,6 @@ default_plugins = essential_plugins + (
"nose", "nose",
"assertion", "assertion",
"junitxml", "junitxml",
"resultlog",
"doctest", "doctest",
"cacheprovider", "cacheprovider",
"freeze_support", "freeze_support",

View File

@ -25,12 +25,6 @@ FILLFUNCARGS = PytestDeprecationWarning(
"function._request._fillfixtures() instead if you cannot avoid reaching into internals." "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."
)
PYTEST_COLLECT_MODULE = UnformattedWarning( PYTEST_COLLECT_MODULE = UnformattedWarning(
PytestDeprecationWarning, PytestDeprecationWarning,
"pytest.collect.{name} was moved to pytest.{name}\n" "pytest.collect.{name} was moved to pytest.{name}\n"

View File

@ -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))

View File

@ -6,26 +6,6 @@ from _pytest import deprecated
from _pytest.pytester import Testdir 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.skip(reason="should be reintroduced in 6.1: #7361")
@pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore @pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore
# false positive due to dynamic attribute # false positive due to dynamic attribute

View File

@ -306,21 +306,6 @@ def test_no_conftest(testdir):
assert result.ret == ExitCode.USAGE_ERROR 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): def test_conftest_existing_junitxml(testdir):
x = testdir.mkdir("tests") x = testdir.mkdir("tests")
x.join("conftest.py").write( x.join("conftest.py").write(

View File

@ -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

View File

@ -737,31 +737,6 @@ class TestStackLevel:
assert "config{sep}__init__.py".format(sep=os.sep) in file assert "config{sep}__init__.py".format(sep=os.sep) in file
assert func == "import_plugin" 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): def test_issue4445_issue5928_mark_generator(self, testdir):
"""#4445 and #5928: Make sure the warning from an unknown mark points to """#4445 and #5928: Make sure the warning from an unknown mark points to
the test file where this mark is used. the test file where this mark is used.