From 3e1f57f35d50b5d5afd8ab7627a32e7dce0e9e0d Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:25:22 +0200 Subject: [PATCH 1/6] Only print 'and X failed' when non-zero --- Lib/doctest.py | 5 +++-- Lib/test/test_doctest/test_doctest.py | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Lib/doctest.py b/Lib/doctest.py index 6049423b5147a5..e2134459fb7086 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1193,7 +1193,7 @@ class DocTestRunner: 2 tests in _TestClass.get 1 tests in _TestClass.square 7 tests in 4 items. - 7 passed and 0 failed. + 7 passed. Test passed. TestResults(failed=0, attempted=7) @@ -1603,7 +1603,8 @@ def summarize(self, verbose=None): print(f" {failures:3d} of {tries:3d} in {name}") if verbose: print(f"{total_tries} tests in {len(self._stats)} items.") - print(f"{total_tries - total_failures} passed and {total_failures} failed.") + and_f = f" and {total_failures} failed" if total_failures else "" + print(f"{total_tries - total_failures} passed{and_f}.") if total_failures: msg = f"***Test Failed*** {total_failures} failures" if total_skips: diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index 43be200b983227..e855d34b67e39e 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -2692,7 +2692,7 @@ def test_testfile(): r""" 1 items passed all tests: 2 tests in test_doctest.txt 2 tests in 1 items. - 2 passed and 0 failed. + 2 passed. Test passed. TestResults(failed=0, attempted=2) >>> doctest.master = None # Reset master. @@ -2775,7 +2775,7 @@ def test_testfile(): r""" 1 items passed all tests: 2 tests in test_doctest4.txt 2 tests in 1 items. - 2 passed and 0 failed. + 2 passed. Test passed. TestResults(failed=0, attempted=2) >>> doctest.master = None # Reset master. @@ -3000,7 +3000,7 @@ def test_CLI(): r""" 1 items passed all tests: 2 tests in myfile.doc 2 tests in 1 items. - 2 passed and 0 failed. + 2 passed. Test passed. Now we'll write a couple files, one with three tests, the other a python module @@ -3129,7 +3129,7 @@ def test_CLI(): r""" 1 items passed all tests: 3 tests in myfile.doc 3 tests in 1 items. - 3 passed and 0 failed. + 3 passed. Test passed. Trying: 1 + 1 @@ -3146,7 +3146,7 @@ def test_CLI(): r""" 1 items passed all tests: 2 tests in myfile2.test_func 2 tests in 2 items. - 2 passed and 0 failed. + 2 passed. Test passed. We should also check some typical error cases. From 699b33d5d5ed618952ad54be78e373205b98f773 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:41:56 +0200 Subject: [PATCH 2/6] Do not pluralise 1 item --- Lib/doctest.py | 36 +++++++++++++++++++++------ Lib/test/test_doctest/test_doctest.py | 30 +++++++++++----------- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/Lib/doctest.py b/Lib/doctest.py index e2134459fb7086..2a5c86f58e3bf7 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1191,7 +1191,7 @@ class DocTestRunner: 2 tests in _TestClass 2 tests in _TestClass.__init__ 2 tests in _TestClass.get - 1 tests in _TestClass.square + 1 test in _TestClass.square 7 tests in 4 items. 7 passed. Test passed. @@ -1584,34 +1584,45 @@ def summarize(self, verbose=None): passed.append((name, tries)) else: failed.append(item) + if verbose: if notests: - print(f"{len(notests)} items had no tests:") + print(f"{_n_items(notests)} had no tests:") notests.sort() for name in notests: print(f" {name}") + if passed: - print(f"{len(passed)} items passed all tests:") + print(f"{_n_items(passed)} passed all tests:") passed.sort() for name, count in passed: - print(f" {count:3d} tests in {name}") + s = "" if count == 1 else "s" + print(f" {count:3d} test{s} in {name}") + if failed: print(self.DIVIDER) - print(f"{len(failed)} items had failures:") + print(f"{_n_items(failed)} had failures:") failed.sort() for name, (failures, tries, skips) in failed: print(f" {failures:3d} of {tries:3d} in {name}") + if verbose: - print(f"{total_tries} tests in {len(self._stats)} items.") + s = "" if total_tries == 1 else "s" + print(f"{total_tries} test{s} in {_n_items(self._stats)}.") + and_f = f" and {total_failures} failed" if total_failures else "" print(f"{total_tries - total_failures} passed{and_f}.") + if total_failures: - msg = f"***Test Failed*** {total_failures} failures" + s = "" if total_failures == 1 else "s" + msg = f"***Test Failed*** {total_failures} failure{s}" if total_skips: - msg = f"{msg} and {total_skips} skipped tests" + s = "" if total_skips == 1 else "s" + msg = f"{msg} and {total_skips} skipped test{s}" print(f"{msg}.") elif verbose: print("Test passed.") + return TestResults(total_failures, total_tries, skipped=total_skips) #///////////////////////////////////////////////////////////////// @@ -1628,6 +1639,15 @@ def merge(self, other): d[name] = (failures, tries, skips) +def _n_items(items: list) -> str: + """ + Helper to pluralise the number of items in a list. + """ + n = len(items) + s = "" if n == 1 else "s" + return f"{n} item{s}" + + class OutputChecker: """ A class used to check the whether the actual output from a doctest diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index e855d34b67e39e..477e9e435c3cb3 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -2628,7 +2628,7 @@ def test_testfile(): r""" ... NameError: name 'favorite_color' is not defined ********************************************************************** - 1 items had failures: + 1 item had failures: 1 of 2 in test_doctest.txt ***Test Failed*** 1 failures. TestResults(failed=1, attempted=2) @@ -2657,7 +2657,7 @@ def test_testfile(): r""" Got: 'red' ********************************************************************** - 1 items had failures: + 1 item had failures: 1 of 2 in test_doctest.txt ***Test Failed*** 1 failures. TestResults(failed=1, attempted=2) @@ -2689,9 +2689,9 @@ def test_testfile(): r""" b ok - 1 items passed all tests: + 1 item passed all tests: 2 tests in test_doctest.txt - 2 tests in 1 items. + 2 tests in 1 item. 2 passed. Test passed. TestResults(failed=0, attempted=2) @@ -2749,7 +2749,7 @@ def test_testfile(): r""" ********************************************************************** ... ********************************************************************** - 1 items had failures: + 1 item had failures: 2 of 2 in test_doctest4.txt ***Test Failed*** 2 failures. TestResults(failed=2, attempted=2) @@ -2772,9 +2772,9 @@ def test_testfile(): r""" Expecting: 'b\u0105r' ok - 1 items passed all tests: + 1 item passed all tests: 2 tests in test_doctest4.txt - 2 tests in 1 items. + 2 tests in 1 item. 2 passed. Test passed. TestResults(failed=0, attempted=2) @@ -2997,9 +2997,9 @@ def test_CLI(): r""" Expecting: 'a' ok - 1 items passed all tests: + 1 item passed all tests: 2 tests in myfile.doc - 2 tests in 1 items. + 2 tests in 1 item. 2 passed. Test passed. @@ -3074,7 +3074,7 @@ def test_CLI(): r""" Got: 'ajkml' ********************************************************************** - 1 items had failures: + 1 item had failures: 2 of 3 in myfile.doc ***Test Failed*** 2 failures. @@ -3101,7 +3101,7 @@ def test_CLI(): r""" Got: 'abcdef' ********************************************************************** - 1 items had failures: + 1 item had failures: 1 of 2 in myfile.doc ***Test Failed*** 1 failures. @@ -3126,9 +3126,9 @@ def test_CLI(): r""" Expecting: 'a...l' ok - 1 items passed all tests: + 1 item passed all tests: 3 tests in myfile.doc - 3 tests in 1 items. + 3 tests in 1 item. 3 passed. Test passed. Trying: @@ -3141,9 +3141,9 @@ def test_CLI(): r""" Expecting: 'abc def' ok - 1 items had no tests: + 1 item had no tests: myfile2 - 1 items passed all tests: + 1 item passed all tests: 2 tests in myfile2.test_func 2 tests in 2 items. 2 passed. From cb4d8d2fe6d45c1f08a41e403699651fc6bfda63 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:54:40 +0200 Subject: [PATCH 3/6] Refactor summarize() --- Lib/doctest.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Lib/doctest.py b/Lib/doctest.py index 2a5c86f58e3bf7..7a9f4e40d814d6 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1568,22 +1568,22 @@ def summarize(self, verbose=None): """ if verbose is None: verbose = self._verbose - notests = [] - passed = [] - failed = [] + + notests, passed, failed = [], [], [] total_tries = total_failures = total_skips = 0 - for item in self._stats.items(): - name, (failures, tries, skips) = item + + for name, (failures, tries, skips) in self._stats.items(): assert failures <= tries total_tries += tries total_failures += failures total_skips += skips + if tries == 0: notests.append(name) elif failures == 0: passed.append((name, tries)) else: - failed.append(item) + failed.append((name, (failures, tries, skips))) if verbose: if notests: @@ -1594,16 +1594,14 @@ def summarize(self, verbose=None): if passed: print(f"{_n_items(passed)} passed all tests:") - passed.sort() - for name, count in passed: + for name, count in sorted(passed): s = "" if count == 1 else "s" print(f" {count:3d} test{s} in {name}") if failed: print(self.DIVIDER) print(f"{_n_items(failed)} had failures:") - failed.sort() - for name, (failures, tries, skips) in failed: + for name, (failures, tries, skips) in sorted(failed): print(f" {failures:3d} of {tries:3d} in {name}") if verbose: From a684423d414873b592f7c687288753886b50578b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 24 Mar 2024 22:54:52 +0200 Subject: [PATCH 4/6] Update docs --- Doc/library/doctest.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index 835a3a76806148..135758187894ec 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -123,10 +123,10 @@ And so on, eventually ending with: OverflowError: n too large ok 2 items passed all tests: - 1 tests in __main__ - 8 tests in __main__.factorial - 9 tests in 2 items. - 9 passed and 0 failed. + 1 test in __main__ + 6 tests in __main__.factorial + 7 tests in 2 items. + 7 passed. Test passed. $ @@ -1933,7 +1933,7 @@ such a test runner:: optionflags=flags) else: fail, total = doctest.testmod(optionflags=flags) - print("{} failures out of {} tests".format(fail, total)) + print(f"{fail} failures out of {total} tests") .. rubric:: Footnotes From 266081e41b49ebd6cb1a92818a924e4fc57bcb24 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:16:07 +0200 Subject: [PATCH 5/6] Add blurb --- .../next/Library/2024-03-25-21-15-56.gh-issue-117225.oOaZXb.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-03-25-21-15-56.gh-issue-117225.oOaZXb.rst diff --git a/Misc/NEWS.d/next/Library/2024-03-25-21-15-56.gh-issue-117225.oOaZXb.rst b/Misc/NEWS.d/next/Library/2024-03-25-21-15-56.gh-issue-117225.oOaZXb.rst new file mode 100644 index 00000000000000..b6c4850f608c2a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-03-25-21-15-56.gh-issue-117225.oOaZXb.rst @@ -0,0 +1,2 @@ +doctest: only print "and X failed" when non-zero, don't pluralise "1 items". +Patch by Hugo van Kemenade. From 4c1efb601f4641b94ec2aca38671349431ef80c3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:31:11 +0200 Subject: [PATCH 6/6] Update test: '1 failures' -> '1 failure' --- Lib/test/test_doctest/test_doctest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_doctest/test_doctest.py b/Lib/test/test_doctest/test_doctest.py index 477e9e435c3cb3..3e883c56f6c766 100644 --- a/Lib/test/test_doctest/test_doctest.py +++ b/Lib/test/test_doctest/test_doctest.py @@ -2630,7 +2630,7 @@ def test_testfile(): r""" ********************************************************************** 1 item had failures: 1 of 2 in test_doctest.txt - ***Test Failed*** 1 failures. + ***Test Failed*** 1 failure. TestResults(failed=1, attempted=2) >>> doctest.master = None # Reset master. @@ -2659,7 +2659,7 @@ def test_testfile(): r""" ********************************************************************** 1 item had failures: 1 of 2 in test_doctest.txt - ***Test Failed*** 1 failures. + ***Test Failed*** 1 failure. TestResults(failed=1, attempted=2) >>> doctest.master = None # Reset master. @@ -3103,7 +3103,7 @@ def test_CLI(): r""" ********************************************************************** 1 item had failures: 1 of 2 in myfile.doc - ***Test Failed*** 1 failures. + ***Test Failed*** 1 failure. The fifth test uses verbose with the two options, so we should get verbose success output for the tests in both files: