Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/_pytest/junitxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ def __init__(
self.log_passing_tests = log_passing_tests
self.report_duration = report_duration
self.family = family
self.stats = dict.fromkeys(["error", "passed", "failure", "skipped"], 0)
self.stats = dict.fromkeys(["errors", "passed", "failure", "skipped"], 0)
self.node_reporters = {} # nodeid -> _NodeReporter
self.node_reporters_ordered = []
self.global_properties = []
Expand Down Expand Up @@ -641,7 +641,7 @@ def pytest_sessionfinish(self):
self.stats["passed"]
+ self.stats["failure"]
+ self.stats["skipped"]
+ self.stats["error"]
+ self.stats["errors"]
- self.cnt_double_fail_tests
)
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
Expand All @@ -650,7 +650,7 @@ def pytest_sessionfinish(self):
self._get_global_properties_node(),
[x.to_xml() for x in self.node_reporters_ordered],
name=self.suite_name,
errors=self.stats["error"],
errors=self.stats["errors"],
failures=self.stats["failure"],
skipped=self.stats["skipped"],
tests=numtests,
Expand Down
9 changes: 4 additions & 5 deletions src/_pytest/pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,8 @@ def parseoutcomes(self) -> Dict[str, int]:
break
else:
raise ValueError("Pytest terminal summary report not found")
if "errors" in ret:
assert "error" not in ret
ret["error"] = ret.pop("errors")
if "error" in ret:
ret["errors"] = ret.pop("error")
return ret

def assert_outcomes(
Expand All @@ -468,15 +467,15 @@ def assert_outcomes(
"passed": d.get("passed", 0),
"skipped": d.get("skipped", 0),
"failed": d.get("failed", 0),
"error": d.get("error", 0),
"errors": d.get("errors", 0),
"xpassed": d.get("xpassed", 0),
"xfailed": d.get("xfailed", 0),
}
expected = {
"passed": passed,
"skipped": skipped,
"failed": failed,
"error": error,
"errors": error,
"xpassed": xpassed,
"xfailed": xfailed,
}
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def pytest_report_teststatus(report):
if report.when in ("setup", "teardown"):
if report.failed:
# category, shortletter, verbose-word
return "error", "E", "ERROR"
return "errors", "E", "ERROR"
elif report.skipped:
return "skipped", "s", "SKIPPED"
else:
Expand Down
23 changes: 12 additions & 11 deletions src/_pytest/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"xfailed",
"xpassed",
"warnings",
"error",
"errors",
)

_REPORTCHARS_DEFAULT = "fE"
Expand Down Expand Up @@ -216,7 +216,7 @@ def pytest_report_teststatus(report: TestReport) -> Tuple[str, str, str]:

outcome = report.outcome
if report.when in ("collect", "setup", "teardown") and outcome == "failed":
outcome = "error"
outcome = "errors"
letter = "E"

return outcome, letter, outcome.upper()
Expand Down Expand Up @@ -559,7 +559,7 @@ def pytest_collection(self) -> None:

def pytest_collectreport(self, report: CollectReport) -> None:
if report.failed:
self._add_stats("error", [report])
self._add_stats("errors", [report])
elif report.skipped:
self._add_stats("skipped", [report])
items = [x for x in report.result if isinstance(x, pytest.Item)]
Expand All @@ -581,7 +581,7 @@ def report_collect(self, final=False):
return
self._collect_report_last_write = t

errors = len(self.stats.get("error", []))
errors = len(self.stats.get("errors", []))
skipped = len(self.stats.get("skipped", []))
deselected = len(self.stats.get("deselected", []))
selected = self._numcollected - errors - skipped - deselected
Expand Down Expand Up @@ -929,11 +929,11 @@ def summary_failures(self):

def summary_errors(self):
if self.config.option.tbstyle != "no":
reports = self.getreports("error")
reports = self.getreports("errors")
if not reports:
return
self.write_sep("=", "ERRORS")
for rep in self.stats["error"]:
for rep in self.stats["errors"]:
msg = self._getfailureheadline(rep)
if rep.when == "collect":
msg = "ERROR collecting " + msg
Expand Down Expand Up @@ -1047,7 +1047,7 @@ def show_skipped(lines: List[str]) -> None:
"f": partial(show_simple, "failed"),
"s": show_skipped,
"p": partial(show_simple, "passed"),
"E": partial(show_simple, "error"),
"E": partial(show_simple, "errors"),
} # type: Mapping[str, Callable[[List[str]], None]]

lines = [] # type: List[str]
Expand All @@ -1070,7 +1070,7 @@ def _get_main_color(self) -> Tuple[str, List[str]]:

def _determine_main_color(self, unknown_type_seen: bool) -> str:
stats = self.stats
if "failed" in stats or "error" in stats:
if "failed" in stats or "errors" in stats:
main_color = "red"
elif "warnings" in stats or "xpassed" in stats or unknown_type_seen:
main_color = "yellow"
Expand Down Expand Up @@ -1176,7 +1176,7 @@ def _folded_skips(skipped):

_color_for_type = {
"failed": "red",
"error": "red",
"errors": "red",
"warnings": "yellow",
"passed": "green",
}
Expand All @@ -1185,13 +1185,14 @@ def _folded_skips(skipped):

def _make_plural(count, noun):
# No need to pluralize words such as `failed` or `passed`.
if noun not in ["error", "warnings"]:
if noun not in ["errors", "warnings"]:
return count, noun

# The `warnings` key is plural. To avoid API breakage, we keep it that way but
# set it to singular here so we can determine plurality in the same way as we do
# for `error`.
# for `errors`.
noun = noun.replace("warnings", "warning")
noun = noun.replace("errors", "error")

return count, noun + "s" if count != 1 else noun

Expand Down
4 changes: 2 additions & 2 deletions testing/example_scripts/junit-10.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ THE SOFTWARE.
</xs:complexType>
</xs:element>

<xs:element name="error">
<xs:element name="errors">
<xs:complexType mixed="true">
<xs:attribute name="type" type="xs:string"/>
<xs:attribute name="message" type="xs:string"/>
Expand Down Expand Up @@ -87,7 +87,7 @@ THE SOFTWARE.
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="skipped"/>
<xs:element ref="error"/>
<xs:element ref="errors"/>
<xs:element ref="failure"/>
<xs:element ref="rerunFailure" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="rerunError" minOccurs="0" maxOccurs="unbounded"/>
Expand Down
10 changes: 5 additions & 5 deletions testing/test_junitxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def test_function(arg):
node.assert_attr(errors=1, tests=1)
tnode = node.find_first_by_tag("testcase")
tnode.assert_attr(classname="test_setup_error", name="test_function")
fnode = tnode.find_first_by_tag("error")
fnode = tnode.find_first_by_tag("errors")
fnode.assert_attr(message="test setup failure")
assert "ValueError" in fnode.toxml()

Expand All @@ -293,7 +293,7 @@ def test_function(arg):
node = dom.find_first_by_tag("testsuite")
tnode = node.find_first_by_tag("testcase")
tnode.assert_attr(classname="test_teardown_error", name="test_function")
fnode = tnode.find_first_by_tag("error")
fnode = tnode.find_first_by_tag("errors")
fnode.assert_attr(message="test teardown failure")
assert "ValueError" in fnode.toxml()

Expand All @@ -320,7 +320,7 @@ def test_function(arg):
assert 0
fnode = first.find_first_by_tag("failure")
fnode.assert_attr(message="Exception: Call Exception")
snode = second.find_first_by_tag("error")
snode = second.find_first_by_tag("errors")
snode.assert_attr(message="test teardown failure")

@parametrize_families
Expand Down Expand Up @@ -442,7 +442,7 @@ def test_internal_error(self, testdir, run_and_parse, xunit_family):
node.assert_attr(errors=1, tests=1)
tnode = node.find_first_by_tag("testcase")
tnode.assert_attr(classname="pytest", name="internal")
fnode = tnode.find_first_by_tag("error")
fnode = tnode.find_first_by_tag("errors")
fnode.assert_attr(message="internal error")
assert "Division" in fnode.toxml()

Expand Down Expand Up @@ -688,7 +688,7 @@ def test_collect_error(self, testdir, run_and_parse, xunit_family):
node = dom.find_first_by_tag("testsuite")
node.assert_attr(errors=1, tests=1)
tnode = node.find_first_by_tag("testcase")
fnode = tnode.find_first_by_tag("error")
fnode = tnode.find_first_by_tag("errors")
fnode.assert_attr(message="collection failure")
assert "SyntaxError" in fnode.toxml()

Expand Down
2 changes: 1 addition & 1 deletion testing/test_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_no_warnings(module):
# fmt: off
subprocess.check_call((
sys.executable,
"-W", "error",
"-W", "errors",
# https://github.com/pytest-dev/pytest/issues/5901
"-W", "ignore:The usage of `cmp` is deprecated and will be removed on or after 2021-06-01. Please use `eq` and `order` instead.:DeprecationWarning", # noqa: E501
"-c", "import {}".format(module),
Expand Down
21 changes: 20 additions & 1 deletion testing/test_pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,25 @@ def test_run_result_repr() -> None:
)


def test_testdir_outcomes_with_single_error(testdir):
p1 = testdir.makepyfile(
"""
import pytest

@pytest.fixture
def bad_fixture():
raise Exception("bad")

def test_error1(bad_fixture):
pass
"""
)
result = testdir.runpytest(str(p1))
result.assert_outcomes(error=1)

assert result.parseoutcomes() == {"errors": 1}


def test_testdir_outcomes_with_multiple_errors(testdir):
p1 = testdir.makepyfile(
"""
Expand All @@ -731,7 +750,7 @@ def test_error2(bad_fixture):
result = testdir.runpytest(str(p1))
result.assert_outcomes(error=2)

assert result.parseoutcomes() == {"error": 2}
assert result.parseoutcomes() == {"errors": 2}


def test_makefile_joins_absolute_path(testdir: Testdir) -> None:
Expand Down
8 changes: 4 additions & 4 deletions testing/test_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1461,15 +1461,15 @@ def tr() -> TerminalReporter:
],
{"failed": (1,), "passed": (1,)},
),
("red", [("1 error", {"bold": True, "red": True})], {"error": (1,)}),
("red", [("2 errors", {"bold": True, "red": True})], {"error": (1, 2)}),
("red", [("1 error", {"bold": True, "red": True})], {"errors": (1,)}),
("red", [("2 errors", {"bold": True, "red": True})], {"errors": (1, 2)}),
(
"red",
[
("1 passed", {"bold": False, "green": True}),
("1 error", {"bold": True, "red": True}),
],
{"error": (1,), "passed": (1,)},
{"errors": (1,), "passed": (1,)},
),
# (a status that's not known to the code)
("yellow", [("1 weird", {"bold": True, "yellow": True})], {"weird": (1,)}),
Expand Down Expand Up @@ -2082,7 +2082,7 @@ def test_collecterror(testdir):
"*_ ERROR collecting test_collecterror.py _*",
"E SyntaxError: *",
"*= short test summary info =*",
"ERROR test_collecterror.py",
"ERRORS test_collecterror.py",
"*! Interrupted: 1 error during collection !*",
"*= 1 error in *",
]
Expand Down