|
11 | 11 | import platform
|
12 | 12 | import sys
|
13 | 13 | import time
|
| 14 | +from functools import partial |
14 | 15 |
|
15 | 16 | import attr
|
16 | 17 | import pluggy
|
@@ -681,6 +682,7 @@ def pytest_terminal_summary(self):
|
681 | 682 | self.summary_failures()
|
682 | 683 | self.summary_warnings()
|
683 | 684 | yield
|
| 685 | + self.short_test_summary() |
684 | 686 | self.summary_passes()
|
685 | 687 | # Display any extra warnings from teardown here (if any).
|
686 | 688 | self.summary_warnings()
|
@@ -876,6 +878,106 @@ def summary_stats(self):
|
876 | 878 | if self.verbosity == -1:
|
877 | 879 | self.write_line(msg, **markup)
|
878 | 880 |
|
| 881 | + def short_test_summary(self): |
| 882 | + if not self.reportchars: |
| 883 | + return |
| 884 | + |
| 885 | + def show_simple(stat, lines): |
| 886 | + failed = self.stats.get(stat, []) |
| 887 | + for rep in failed: |
| 888 | + verbose_word = _get_report_str(self.config, rep) |
| 889 | + pos = _get_pos(self.config, rep) |
| 890 | + lines.append("%s %s" % (verbose_word, pos)) |
| 891 | + |
| 892 | + def show_xfailed(lines): |
| 893 | + xfailed = self.stats.get("xfailed", []) |
| 894 | + for rep in xfailed: |
| 895 | + verbose_word = _get_report_str(self.config, rep) |
| 896 | + pos = _get_pos(self.config, rep) |
| 897 | + lines.append("%s %s" % (verbose_word, pos)) |
| 898 | + reason = rep.wasxfail |
| 899 | + if reason: |
| 900 | + lines.append(" " + str(reason)) |
| 901 | + |
| 902 | + def show_xpassed(lines): |
| 903 | + xpassed = self.stats.get("xpassed", []) |
| 904 | + for rep in xpassed: |
| 905 | + verbose_word = _get_report_str(self.config, rep) |
| 906 | + pos = _get_pos(self.config, rep) |
| 907 | + reason = rep.wasxfail |
| 908 | + lines.append("%s %s %s" % (verbose_word, pos, reason)) |
| 909 | + |
| 910 | + def show_skipped(lines): |
| 911 | + skipped = self.stats.get("skipped", []) |
| 912 | + fskips = _folded_skips(skipped) if skipped else [] |
| 913 | + if not fskips: |
| 914 | + return |
| 915 | + verbose_word = _get_report_str(self.config, report=skipped[0]) |
| 916 | + for num, fspath, lineno, reason in fskips: |
| 917 | + if reason.startswith("Skipped: "): |
| 918 | + reason = reason[9:] |
| 919 | + if lineno is not None: |
| 920 | + lines.append( |
| 921 | + "%s [%d] %s:%d: %s" |
| 922 | + % (verbose_word, num, fspath, lineno + 1, reason) |
| 923 | + ) |
| 924 | + else: |
| 925 | + lines.append("%s [%d] %s: %s" % (verbose_word, num, fspath, reason)) |
| 926 | + |
| 927 | + def _get_report_str(config, report): |
| 928 | + _category, _short, verbose = config.hook.pytest_report_teststatus( |
| 929 | + report=report, config=config |
| 930 | + ) |
| 931 | + return verbose |
| 932 | + |
| 933 | + def _get_pos(config, rep): |
| 934 | + nodeid = config.cwd_relative_nodeid(rep.nodeid) |
| 935 | + return nodeid |
| 936 | + |
| 937 | + REPORTCHAR_ACTIONS = { |
| 938 | + "x": show_xfailed, |
| 939 | + "X": show_xpassed, |
| 940 | + "f": partial(show_simple, "failed"), |
| 941 | + "F": partial(show_simple, "failed"), |
| 942 | + "s": show_skipped, |
| 943 | + "S": show_skipped, |
| 944 | + "p": partial(show_simple, "passed"), |
| 945 | + "E": partial(show_simple, "error"), |
| 946 | + } |
| 947 | + |
| 948 | + lines = [] |
| 949 | + for char in self.reportchars: |
| 950 | + action = REPORTCHAR_ACTIONS.get(char) |
| 951 | + if action: # skipping e.g. "P" (passed with output) here. |
| 952 | + action(lines) |
| 953 | + |
| 954 | + if lines: |
| 955 | + self.write_sep("=", "short test summary info") |
| 956 | + for line in lines: |
| 957 | + self.write_line(line) |
| 958 | + |
| 959 | + |
| 960 | +def _folded_skips(skipped): |
| 961 | + d = {} |
| 962 | + for event in skipped: |
| 963 | + key = event.longrepr |
| 964 | + assert len(key) == 3, (event, key) |
| 965 | + keywords = getattr(event, "keywords", {}) |
| 966 | + # folding reports with global pytestmark variable |
| 967 | + # this is workaround, because for now we cannot identify the scope of a skip marker |
| 968 | + # TODO: revisit after marks scope would be fixed |
| 969 | + if ( |
| 970 | + event.when == "setup" |
| 971 | + and "skip" in keywords |
| 972 | + and "pytestmark" not in keywords |
| 973 | + ): |
| 974 | + key = (key[0], None, key[2]) |
| 975 | + d.setdefault(key, []).append(event) |
| 976 | + values = [] |
| 977 | + for key, events in d.items(): |
| 978 | + values.append((len(events),) + key) |
| 979 | + return values |
| 980 | + |
879 | 981 |
|
880 | 982 | def build_summary_stats_line(stats):
|
881 | 983 | known_types = (
|
|
0 commit comments