Skip to content

Commit ebc4540

Browse files
authored
Merge pull request #12258 from bluetech/xdist-align
terminal: fix progress percentages not aligning correctly in xdist
2 parents 127a372 + 50d1e81 commit ebc4540

File tree

2 files changed

+39
-25
lines changed

2 files changed

+39
-25
lines changed

changelog/7166.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed progress percentages (the ``[ 87%]`` at the edge of the screen) sometimes not aligning correctly when running with pytest-xdist ``-n``.

src/_pytest/terminal.py

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ def hasopt(self, char: str) -> bool:
432432
char = {"xfailed": "x", "skipped": "s"}.get(char, char)
433433
return char in self.reportchars
434434

435-
def write_fspath_result(self, nodeid: str, res, **markup: bool) -> None:
435+
def write_fspath_result(self, nodeid: str, res: str, **markup: bool) -> None:
436436
fspath = self.config.rootpath / nodeid.split("::")[0]
437437
if self.currentfspath is None or fspath != self.currentfspath:
438438
if self.currentfspath is not None and self._show_progress_info:
@@ -565,10 +565,11 @@ def pytest_deselected(self, items: Sequence[Item]) -> None:
565565
def pytest_runtest_logstart(
566566
self, nodeid: str, location: Tuple[str, Optional[int], str]
567567
) -> None:
568+
fspath, lineno, domain = location
568569
# Ensure that the path is printed before the
569570
# 1st test of a module starts running.
570571
if self.showlongtestinfo:
571-
line = self._locationline(nodeid, *location)
572+
line = self._locationline(nodeid, fspath, lineno, domain)
572573
self.write_ensure_prefix(line, "")
573574
self.flush()
574575
elif self.showfspath:
@@ -591,7 +592,6 @@ def pytest_runtest_logreport(self, report: TestReport) -> None:
591592
if not letter and not word:
592593
# Probably passed setup/teardown.
593594
return
594-
running_xdist = hasattr(rep, "node")
595595
if markup is None:
596596
was_xfail = hasattr(report, "wasxfail")
597597
if rep.passed and not was_xfail:
@@ -604,11 +604,20 @@ def pytest_runtest_logreport(self, report: TestReport) -> None:
604604
markup = {"yellow": True}
605605
else:
606606
markup = {}
607+
self._progress_nodeids_reported.add(rep.nodeid)
607608
if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0:
608609
self._tw.write(letter, **markup)
610+
# When running in xdist, the logreport and logfinish of multiple
611+
# items are interspersed, e.g. `logreport`, `logreport`,
612+
# `logfinish`, `logfinish`. To avoid the "past edge" calculation
613+
# from getting confused and overflowing (#7166), do the past edge
614+
# printing here and not in logfinish, except for the 100% which
615+
# should only be printed after all teardowns are finished.
616+
if self._show_progress_info and not self._is_last_item:
617+
self._write_progress_information_if_past_edge()
609618
else:
610-
self._progress_nodeids_reported.add(rep.nodeid)
611619
line = self._locationline(rep.nodeid, *rep.location)
620+
running_xdist = hasattr(rep, "node")
612621
if not running_xdist:
613622
self.write_ensure_prefix(line, word, **markup)
614623
if rep.skipped or hasattr(report, "wasxfail"):
@@ -648,39 +657,29 @@ def _is_last_item(self) -> bool:
648657
assert self._session is not None
649658
return len(self._progress_nodeids_reported) == self._session.testscollected
650659

651-
def pytest_runtest_logfinish(self, nodeid: str) -> None:
652-
assert self._session
660+
@hookimpl(wrapper=True)
661+
def pytest_runtestloop(self) -> Generator[None, object, object]:
662+
result = yield
663+
664+
# Write the final/100% progress -- deferred until the loop is complete.
653665
if (
654666
self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0
655667
and self._show_progress_info
668+
and self._progress_nodeids_reported
656669
):
657-
if self._show_progress_info == "count":
658-
num_tests = self._session.testscollected
659-
progress_length = len(f" [{num_tests}/{num_tests}]")
660-
else:
661-
progress_length = len(" [100%]")
662-
663-
self._progress_nodeids_reported.add(nodeid)
670+
self._write_progress_information_filling_space()
664671

665-
if self._is_last_item:
666-
self._write_progress_information_filling_space()
667-
else:
668-
main_color, _ = self._get_main_color()
669-
w = self._width_of_current_line
670-
past_edge = w + progress_length + 1 >= self._screen_width
671-
if past_edge:
672-
msg = self._get_progress_information_message()
673-
self._tw.write(msg + "\n", **{main_color: True})
672+
return result
674673

675674
def _get_progress_information_message(self) -> str:
676675
assert self._session
677676
collected = self._session.testscollected
678677
if self._show_progress_info == "count":
679678
if collected:
680-
progress = self._progress_nodeids_reported
679+
progress = len(self._progress_nodeids_reported)
681680
counter_format = f"{{:{len(str(collected))}d}}"
682681
format_string = f" [{counter_format}/{{}}]"
683-
return format_string.format(len(progress), collected)
682+
return format_string.format(progress, collected)
684683
return f" [ {collected} / {collected} ]"
685684
else:
686685
if collected:
@@ -689,6 +688,20 @@ def _get_progress_information_message(self) -> str:
689688
)
690689
return " [100%]"
691690

691+
def _write_progress_information_if_past_edge(self) -> None:
692+
w = self._width_of_current_line
693+
if self._show_progress_info == "count":
694+
assert self._session
695+
num_tests = self._session.testscollected
696+
progress_length = len(f" [{num_tests}/{num_tests}]")
697+
else:
698+
progress_length = len(" [100%]")
699+
past_edge = w + progress_length + 1 >= self._screen_width
700+
if past_edge:
701+
main_color, _ = self._get_main_color()
702+
msg = self._get_progress_information_message()
703+
self._tw.write(msg + "\n", **{main_color: True})
704+
692705
def _write_progress_information_filling_space(self) -> None:
693706
color, _ = self._get_main_color()
694707
msg = self._get_progress_information_message()
@@ -937,7 +950,7 @@ def mkrel(nodeid: str) -> str:
937950
line += "[".join(values)
938951
return line
939952

940-
# collect_fspath comes from testid which has a "/"-normalized path.
953+
# fspath comes from testid which has a "/"-normalized path.
941954
if fspath:
942955
res = mkrel(nodeid)
943956
if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace(

0 commit comments

Comments
 (0)