Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display message from reprcrash in short test summary #5013

Merged
merged 14 commits into from May 8, 2019
Merged
1 change: 1 addition & 0 deletions changelog/5013.feature.rst
@@ -0,0 +1 @@
Messages from crash reports are displayed within test summaries now, truncated to the terminal width.
38 changes: 35 additions & 3 deletions src/_pytest/skipping.py
Expand Up @@ -204,14 +204,46 @@ def pytest_terminal_summary(terminalreporter):
tr._tw.line(line)


def _get_line_with_reprcrash_message(config, rep, termwidth):
"""Get summary line for a report, trying to add reprcrash message."""
verbose_word = _get_report_str(config, rep)
pos = _get_pos(config, rep)

line = "%s %s" % (verbose_word, pos)
len_line = len(line)
ellipsis, len_ellipsis = "...", 3
if len_line > termwidth - len_ellipsis:
# No space for an additional message.
return line

try:
msg = rep.longrepr.reprcrash.message
except AttributeError:
pass
else:
# Only use the first line.
i = msg.find("\n")
if i != -1:
msg = msg[:i]
len_msg = len(msg)

sep, len_sep = " - ", 3
max_len_msg = termwidth - len_line - len_sep
if max_len_msg >= len_ellipsis:
if len_msg > max_len_msg:
msg = msg[: (max_len_msg - len_ellipsis)] + ellipsis
line += sep + msg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh just realized this: is msg unicode-safe in Python 2? because we will be joining a str/bytes with a potential unicode message, so there's a good chance this might blow up

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would appreciate a failing example.. do you mean we should prefix sep etc also with u?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm actually no, this is safe: if msg is unicode, Python 2 will try to elevate sep to unicode using ascii (which is fine), not the other way around. Sorry for the brain fart. 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to play it safe and ensure we don't regress though, you can change the message to use a smiling face emoji instead (😄), but is up to you. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.. but it shows that we're not handling terminal cells there really - i.e. it would wrap due to this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Hmm not even sure what the solution would be, TBH.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a commit using wcwidth.. not sure if it is worth the extra dependency, but it worked well.. ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think adding wcwidth is fine: small, license is OK, seems to be maintained

return line


def show_simple(terminalreporter, lines, stat):
failed = terminalreporter.stats.get(stat)
if failed:
config = terminalreporter.config
termwidth = terminalreporter.writer.fullwidth
for rep in failed:
verbose_word = _get_report_str(config, rep)
pos = _get_pos(config, rep)
lines.append("%s %s" % (verbose_word, pos))
line = _get_line_with_reprcrash_message(config, rep, termwidth)
lines.append(line)


def show_xfailed(terminalreporter, lines):
Expand Down
4 changes: 3 additions & 1 deletion testing/acceptance_test.py
Expand Up @@ -865,7 +865,9 @@ def test_doctest_id(self, testdir):
_fail, _sep, testid = line.partition(" ")
break
result = testdir.runpytest(testid, "-rf")
result.stdout.fnmatch_lines([line, "*1 failed*"])
result.stdout.fnmatch_lines(
["FAILED test_doctest_id.txt::test_doctest_id.txt", "*1 failed*"]
)

def test_core_backward_compatibility(self):
"""Test backward compatibility for get_plugin_manager function. See #787."""
Expand Down
46 changes: 45 additions & 1 deletion testing/test_skipping.py
Expand Up @@ -1208,6 +1208,50 @@ def test_fail():
[
"=* FAILURES *=",
"*= short test summary info =*",
"FAILED test_summary_list_after_errors.py::test_fail",
"FAILED test_summary_list_after_errors.py::test_fail - assert 0",
]
)


def test_line_with_reprcrash(monkeypatch):
import _pytest.skipping
from _pytest.skipping import _get_line_with_reprcrash_message

def mock_get_report_str(*args):
return "FAILED"

def mock_get_pos(*args):
return "some::nodeid"

monkeypatch.setattr(_pytest.skipping, "_get_report_str", mock_get_report_str)
monkeypatch.setattr(_pytest.skipping, "_get_pos", mock_get_pos)

class config:
pass

class rep:
pass

f = _get_line_with_reprcrash_message
assert f(config, rep, 80) == "FAILED some::nodeid"

class rep:
class longrepr:
class reprcrash:
message = "msg"

assert f(config, rep, 80) == "FAILED some::nodeid - msg"
assert f(config, rep, 3) == "FAILED some::nodeid"

assert f(config, rep, 24) == "FAILED some::nodeid"
assert f(config, rep, 25) == "FAILED some::nodeid - msg"

rep.longrepr.reprcrash.message = "some longer message"
assert f(config, rep, 24) == "FAILED some::nodeid"
assert f(config, rep, 25) == "FAILED some::nodeid - ..."
assert f(config, rep, 26) == "FAILED some::nodeid - s..."

rep.longrepr.reprcrash.message = "some\nmessage"
assert f(config, rep, 25) == "FAILED some::nodeid - ..."
assert f(config, rep, 26) == "FAILED some::nodeid - some"
assert f(config, rep, 80) == "FAILED some::nodeid - some"
12 changes: 9 additions & 3 deletions testing/test_terminal.py
Expand Up @@ -726,12 +726,18 @@ def test(i):
result.stdout.fnmatch_lines(["collected 3 items", "hello from hook: 3 items"])


def test_fail_extra_reporting(testdir):
testdir.makepyfile("def test_this(): assert 0")
def test_fail_extra_reporting(testdir, monkeypatch):
monkeypatch.setenv("COLUMNS", "80")
testdir.makepyfile("def test_this(): assert 0, 'this_failed' * 100")
result = testdir.runpytest()
assert "short test summary" not in result.stdout.str()
result = testdir.runpytest("-rf")
result.stdout.fnmatch_lines(["*test summary*", "FAIL*test_fail_extra_reporting*"])
result.stdout.fnmatch_lines(
[
"*test summary*",
"FAILED test_fail_extra_reporting.py::test_this - AssertionError: this_failedt...",
]
)


def test_fail_reporting_on_pass(testdir):
Expand Down