From 0f546c4670146fbb89407cad85518e3a7dcfa833 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Tue, 22 Jan 2019 23:26:30 -0600 Subject: [PATCH 1/3] pytest_terminal_summary uses result from pytest_report_teststatus hook, rather than hardcoded strings Less hacky way to make XPASS yellow markup. Make sure collect reports still have a "when" attribute. xfail changed to XFAIL in the test report, for consistency with other outcomes which are all CAPS --- changelog/4667.bugfix.rst | 1 + src/_pytest/junitxml.py | 2 +- src/_pytest/reports.py | 4 +++ src/_pytest/skipping.py | 51 +++++++++++++++++++------------------- src/_pytest/terminal.py | 8 +++--- testing/acceptance_test.py | 4 +-- testing/test_skipping.py | 2 +- testing/test_terminal.py | 2 +- 8 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 changelog/4667.bugfix.rst diff --git a/changelog/4667.bugfix.rst b/changelog/4667.bugfix.rst new file mode 100644 index 000000000..ac2d8567c --- /dev/null +++ b/changelog/4667.bugfix.rst @@ -0,0 +1 @@ +``pytest_terminal_summary`` uses result from ``pytest_report_teststatus`` hook, rather than hardcoded strings. diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 1a06ea6d1..8b9af2a88 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -213,7 +213,7 @@ class _NodeReporter(object): self._add_simple(Junit.skipped, "collection skipped", report.longrepr) def append_error(self, report): - if getattr(report, "when", None) == "teardown": + if report.when == "teardown": msg = "test teardown failure" else: msg = "test setup failure" diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index b2010cc2e..3bdbce791 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -19,6 +19,8 @@ def getslaveinfoline(node): class BaseReport(object): + when = None + def __init__(self, **kw): self.__dict__.update(kw) @@ -169,6 +171,8 @@ class TeardownErrorReport(BaseReport): class CollectReport(BaseReport): + when = "collect" + def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra): self.nodeid = nodeid self.outcome = outcome diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index f755fc4eb..9f7f525ce 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -180,9 +180,9 @@ def pytest_runtest_makereport(item, call): def pytest_report_teststatus(report): if hasattr(report, "wasxfail"): if report.skipped: - return "xfailed", "x", "xfail" + return "xfailed", "x", "XFAIL" elif report.passed: - return "xpassed", "X", ("XPASS", {"yellow": True}) + return "xpassed", "X", "XPASS" # called by the terminalreporter instance/plugin @@ -191,11 +191,6 @@ def pytest_report_teststatus(report): def pytest_terminal_summary(terminalreporter): tr = terminalreporter if not tr.reportchars: - # for name in "xfailed skipped failed xpassed": - # if not tr.stats.get(name, 0): - # tr.write_line("HINT: use '-r' option to see extra " - # "summary info about tests") - # break return lines = [] @@ -209,21 +204,23 @@ def pytest_terminal_summary(terminalreporter): tr._tw.line(line) -def show_simple(terminalreporter, lines, stat, format): +def show_simple(terminalreporter, lines, stat): failed = terminalreporter.stats.get(stat) if failed: for rep in failed: + verbose_word = _get_report_str(terminalreporter, rep) pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid) - lines.append(format % (pos,)) + lines.append("%s %s" % (verbose_word, pos)) def show_xfailed(terminalreporter, lines): xfailed = terminalreporter.stats.get("xfailed") if xfailed: for rep in xfailed: + verbose_word = _get_report_str(terminalreporter, rep) pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid) reason = rep.wasxfail - lines.append("XFAIL %s" % (pos,)) + lines.append("%s %s" % (verbose_word, pos)) if reason: lines.append(" " + str(reason)) @@ -232,9 +229,10 @@ def show_xpassed(terminalreporter, lines): xpassed = terminalreporter.stats.get("xpassed") if xpassed: for rep in xpassed: + verbose_word = _get_report_str(terminalreporter, rep) pos = terminalreporter.config.cwd_relative_nodeid(rep.nodeid) reason = rep.wasxfail - lines.append("XPASS %s %s" % (pos, reason)) + lines.append("%s %s %s" % (verbose_word, pos, reason)) def folded_skips(skipped): @@ -260,39 +258,42 @@ def show_skipped(terminalreporter, lines): tr = terminalreporter skipped = tr.stats.get("skipped", []) if skipped: - # if not tr.hasopt('skipped'): - # tr.write_line( - # "%d skipped tests, specify -rs for more info" % - # len(skipped)) - # return + verbose_word = _get_report_str(terminalreporter, report=skipped[0]) fskips = folded_skips(skipped) if fskips: - # tr.write_sep("_", "skipped test summary") for num, fspath, lineno, reason in fskips: if reason.startswith("Skipped: "): reason = reason[9:] if lineno is not None: lines.append( - "SKIP [%d] %s:%d: %s" % (num, fspath, lineno + 1, reason) + "%s [%d] %s:%d: %s" + % (verbose_word, num, fspath, lineno + 1, reason) ) else: - lines.append("SKIP [%d] %s: %s" % (num, fspath, reason)) + lines.append("%s [%d] %s: %s" % (verbose_word, num, fspath, reason)) -def shower(stat, format): +def shower(stat): def show_(terminalreporter, lines): - return show_simple(terminalreporter, lines, stat, format) + return show_simple(terminalreporter, lines, stat) return show_ +def _get_report_str(terminalreporter, report): + _category, _short, verbose = terminalreporter.config.hook.pytest_report_teststatus( + report=report + ) + return verbose + + REPORTCHAR_ACTIONS = { "x": show_xfailed, "X": show_xpassed, - "f": shower("failed", "FAIL %s"), - "F": shower("failed", "FAIL %s"), + "f": shower("failed"), + "F": shower("failed"), "s": show_skipped, "S": show_skipped, - "p": shower("passed", "PASSED %s"), - "E": shower("error", "ERROR %s"), + "p": shower("passed"), + "E": shower("error"), } diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index bea02306b..9e1d1cb62 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -376,8 +376,11 @@ class TerminalReporter(object): return running_xdist = hasattr(rep, "node") if markup is None: - if rep.passed: + was_xfail = hasattr(report, "wasxfail") + if rep.passed and not was_xfail: markup = {"green": True} + elif rep.passed and was_xfail: + markup = {"yellow": True} elif rep.failed: markup = {"red": True} elif rep.skipped: @@ -806,8 +809,7 @@ class TerminalReporter(object): self.write_sep("=", "ERRORS") for rep in self.stats["error"]: msg = self._getfailureheadline(rep) - if not hasattr(rep, "when"): - # collect + if rep.when == "collect": msg = "ERROR collecting " + msg elif rep.when == "setup": msg = "ERROR at setup of " + msg diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index b7f914335..af30f2123 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -804,8 +804,8 @@ class TestInvocationVariants(object): result = testdir.runpytest("-rf") lines = result.stdout.str().splitlines() for line in lines: - if line.startswith("FAIL "): - testid = line[5:].strip() + if line.startswith(("FAIL ", "FAILED ")): + _fail, _sep, testid = line.partition(" ") break result = testdir.runpytest(testid, "-rf") result.stdout.fnmatch_lines([line, "*1 failed*"]) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 6b18011b6..be3f74760 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1202,6 +1202,6 @@ def test_summary_list_after_errors(testdir): [ "=* FAILURES *=", "*= short test summary info =*", - "FAIL test_summary_list_after_errors.py::test_fail", + "FAILED test_summary_list_after_errors.py::test_fail", ] ) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 06345f88d..6837da5e0 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -625,7 +625,7 @@ class TestTerminalFunctional(object): "*test_verbose_reporting.py::test_fail *FAIL*", "*test_verbose_reporting.py::test_pass *PASS*", "*test_verbose_reporting.py::TestClass::test_skip *SKIP*", - "*test_verbose_reporting.py::test_gen *xfail*", + "*test_verbose_reporting.py::test_gen *XFAIL*", ] ) assert result.ret == 1 From 2d18546870837a12ebf2bcf93c2d5aa74cbaf0df Mon Sep 17 00:00:00 2001 From: wim glenn Date: Thu, 24 Jan 2019 11:12:59 -0600 Subject: [PATCH 2/3] resolving report.when attribute should be reliable now --- src/_pytest/pytester.py | 13 +++++-------- src/_pytest/skipping.py | 7 +++++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 4b1564791..9cadd2f9d 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -305,13 +305,10 @@ class HookRecorder(object): """return a testreport whose dotted import path matches""" values = [] for rep in self.getreports(names=names): - try: - if not when and rep.when != "call" and rep.passed: - # setup/teardown passing reports - let's ignore those - continue - except AttributeError: - pass - if when and getattr(rep, "when", None) != when: + if not when and rep.when != "call" and rep.passed: + # setup/teardown passing reports - let's ignore those + continue + if when and rep.when != when: continue if not inamepart or inamepart in rep.nodeid.split("::"): values.append(rep) @@ -338,7 +335,7 @@ class HookRecorder(object): failed = [] for rep in self.getreports("pytest_collectreport pytest_runtest_logreport"): if rep.passed: - if getattr(rep, "when", None) == "call": + if rep.when == "call": passed.append(rep) elif rep.skipped: skipped.append(rep) diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 9f7f525ce..49676aa80 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -244,8 +244,11 @@ def folded_skips(skipped): # folding reports with global pytestmark variable # this is workaround, because for now we cannot identify the scope of a skip marker # TODO: revisit after marks scope would be fixed - when = getattr(event, "when", None) - if when == "setup" and "skip" in keywords and "pytestmark" not in keywords: + if ( + event.when == "setup" + and "skip" in keywords + and "pytestmark" not in keywords + ): key = (key[0], None, key[2]) d.setdefault(key, []).append(event) values = [] From 8cf097635e7cfdf9837f89227d3f0f080b57b684 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Thu, 24 Jan 2019 12:59:36 -0600 Subject: [PATCH 3/3] =?UTF-8?q?Fixed=20one=20weird=20test=20that=20creates?= =?UTF-8?q?=20a=20class=20instead=20of=20using=20mocks..=20=C2=AF\=5F(?= =?UTF-8?q?=E3=83=84)=5F/=C2=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- testing/test_skipping.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index be3f74760..b2a515f11 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -770,6 +770,7 @@ def test_skip_reasons_folding(): # ev3 might be a collection report ev3 = X() + ev3.when = "collect" ev3.longrepr = longrepr ev3.skipped = True