Skip to content

Commit db823cb

Browse files
committed
bpo-34279: Issue a warning if no tests have been executed
1 parent 48498dd commit db823cb

File tree

5 files changed

+101
-7
lines changed

5 files changed

+101
-7
lines changed

Lib/test/libregrtest/main.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from test.libregrtest.runtest import (
1515
findtests, runtest, get_abs_module,
1616
STDTESTS, NOTTESTS, PASSED, FAILED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED,
17-
INTERRUPTED, CHILD_ERROR,
17+
INTERRUPTED, CHILD_ERROR, TEST_DID_NOT_RUN,
1818
PROGRESS_MIN_TIME, format_test_result)
1919
from test.libregrtest.setup import setup_tests
2020
from test.libregrtest.utils import removepy, count, format_duration, printlist
@@ -118,6 +118,8 @@ def accumulate_result(self, test, result):
118118
elif ok == RESOURCE_DENIED:
119119
self.skipped.append(test)
120120
self.resource_denieds.append(test)
121+
elif ok == TEST_DID_NOT_RUN:
122+
pass
121123
elif ok != INTERRUPTED:
122124
raise ValueError("invalid test result: %r" % ok)
123125

@@ -319,6 +321,10 @@ def display_result(self):
319321
if self.ns.pgo:
320322
return
321323

324+
if not any((self.good, self.bad, self.skipped, self.interrupted,
325+
self.environment_changed)):
326+
return
327+
322328
print()
323329
print("== Tests result: %s ==" % self.get_tests_result())
324330

@@ -458,6 +464,9 @@ def get_tests_result(self):
458464
result.append("FAILURE")
459465
elif self.ns.fail_env_changed and self.environment_changed:
460466
result.append("ENV CHANGED")
467+
elif not any((self.good, self.bad, self.skipped, self.interrupted,
468+
self.environment_changed)):
469+
result.append("NO TEST RUN")
461470

462471
if self.interrupted:
463472
result.append("INTERRUPTED")

Lib/test/libregrtest/runtest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
RESOURCE_DENIED = -3
2020
INTERRUPTED = -4
2121
CHILD_ERROR = -5 # error in a child process
22+
TEST_DID_NOT_RUN = -6 # error in a child process
2223

2324
_FORMAT_TEST_RESULT = {
2425
PASSED: '%s passed',
@@ -28,6 +29,7 @@
2829
RESOURCE_DENIED: '%s skipped (resource denied)',
2930
INTERRUPTED: '%s interrupted',
3031
CHILD_ERROR: '%s crashed',
32+
TEST_DID_NOT_RUN: '%s run no tests',
3133
}
3234

3335
# Minimum duration of a test to display its duration or to mention that
@@ -94,6 +96,7 @@ def runtest(ns, test):
9496
ENV_CHANGED test failed because it changed the execution environment
9597
FAILED test failed
9698
PASSED test passed
99+
EMPTY_TEST_SUITE test ran no subtests.
97100
98101
If ns.xmlpath is not None, xml_data is a list containing each
99102
generated testsuite element.
@@ -197,6 +200,8 @@ def test_runner():
197200
else:
198201
print("test", test, "failed", file=sys.stderr, flush=True)
199202
return FAILED, test_time
203+
except support.TestDidNotRun:
204+
return TEST_DID_NOT_RUN, test_time
200205
except:
201206
msg = traceback.format_exc()
202207
if not ns.pgo:

Lib/test/support/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
# globals
7373
"PIPE_MAX_SIZE", "verbose", "max_memuse", "use_resources", "failfast",
7474
# exceptions
75-
"Error", "TestFailed", "ResourceDenied",
75+
"Error", "TestFailed", "TestDidNotRun", "ResourceDenied",
7676
# imports
7777
"import_module", "import_fresh_module", "CleanImport",
7878
# modules
@@ -120,6 +120,9 @@ class Error(Exception):
120120
class TestFailed(Error):
121121
"""Test failed."""
122122

123+
class TestDidNotRun(Error):
124+
"""Test did not run any subtests."""
125+
123126
class ResourceDenied(unittest.SkipTest):
124127
"""Test skipped because it requested a disallowed resource.
125128
@@ -1930,6 +1933,8 @@ def _run_suite(suite):
19301933
if junit_xml_list is not None:
19311934
junit_xml_list.append(result.get_xml_element())
19321935

1936+
if not result.testsRun:
1937+
raise TestDidNotRun
19331938
if not result.wasSuccessful():
19341939
if len(result.errors) == 1 and not result.failures:
19351940
err = result.errors[0][1]

Lib/test/test_regrtest.py

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,20 @@ def setUp(self):
351351
self.tmptestdir = tempfile.mkdtemp()
352352
self.addCleanup(support.rmtree, self.tmptestdir)
353353

354-
def create_test(self, name=None, code=''):
354+
def create_test(self, name=None, code=None):
355355
if not name:
356356
name = 'noop%s' % BaseTestCase.TEST_UNIQUE_ID
357357
BaseTestCase.TEST_UNIQUE_ID += 1
358358

359+
if code is None:
360+
code = textwrap.dedent("""
361+
import unittest
362+
363+
class Tests(unittest.TestCase):
364+
def test_empty_test(self):
365+
pass
366+
""")
367+
359368
# test_regrtest cannot be run twice in parallel because
360369
# of setUp() and create_test()
361370
name = self.TESTNAME_PREFIX + name
@@ -390,7 +399,7 @@ def parse_executed_tests(self, output):
390399

391400
def check_executed_tests(self, output, tests, skipped=(), failed=(),
392401
env_changed=(), omitted=(),
393-
rerun=(),
402+
rerun=(), no_test_ran=(),
394403
randomize=False, interrupted=False,
395404
fail_env_changed=False):
396405
if isinstance(tests, str):
@@ -405,6 +414,8 @@ def check_executed_tests(self, output, tests, skipped=(), failed=(),
405414
omitted = [omitted]
406415
if isinstance(rerun, str):
407416
rerun = [rerun]
417+
if isinstance(no_test_ran, str):
418+
no_test_ran = [no_test_ran]
408419

409420
executed = self.parse_executed_tests(output)
410421
if randomize:
@@ -448,7 +459,7 @@ def list_regex(line_format, tests):
448459
self.check_line(output, regex)
449460

450461
good = (len(tests) - len(skipped) - len(failed)
451-
- len(omitted) - len(env_changed))
462+
- len(omitted) - len(env_changed) - len(no_test_ran))
452463
if good:
453464
regex = r'%s test%s OK\.$' % (good, plural(good))
454465
if not skipped and not failed and good > 1:
@@ -465,12 +476,16 @@ def list_regex(line_format, tests):
465476
result.append('ENV CHANGED')
466477
if interrupted:
467478
result.append('INTERRUPTED')
468-
if not result:
479+
if not any((good, result, failed, interrupted, skipped,
480+
env_changed, fail_env_changed)):
481+
result.append("NO TEST RUN")
482+
elif not result:
469483
result.append('SUCCESS')
470484
result = ', '.join(result)
471485
if rerun:
472486
self.check_line(output, 'Tests result: %s' % result)
473487
result = 'FAILURE then %s' % result
488+
474489
self.check_line(output, 'Tests result: %s' % result)
475490

476491
def parse_random_seed(self, output):
@@ -649,7 +664,14 @@ def test_resources(self):
649664
# test -u command line option
650665
tests = {}
651666
for resource in ('audio', 'network'):
652-
code = 'from test import support\nsupport.requires(%r)' % resource
667+
code = textwrap.dedent("""
668+
from test import support; support.requires(%r)
669+
import unittest
670+
class PassingTest(unittest.TestCase):
671+
def test_pass(self):
672+
pass
673+
""" % resource)
674+
653675
tests[resource] = self.create_test(resource, code)
654676
test_names = sorted(tests.values())
655677

@@ -978,6 +1000,56 @@ def test_bug(self):
9781000
output = self.run_tests("-w", testname, exitcode=2)
9791001
self.check_executed_tests(output, [testname],
9801002
failed=testname, rerun=testname)
1003+
def test_no_tests_ran(self):
1004+
code = textwrap.dedent("""
1005+
import unittest
1006+
1007+
class Tests(unittest.TestCase):
1008+
def test_bug(self):
1009+
pass
1010+
""")
1011+
testname = self.create_test(code=code)
1012+
1013+
output = self.run_tests(testname, "-m", "nosuchtest", exitcode=0)
1014+
self.check_executed_tests(output, [testname], no_test_ran=testname)
1015+
1016+
def test_no_tests_ran_multiple_tests_nonexistent(self):
1017+
code = textwrap.dedent("""
1018+
import unittest
1019+
1020+
class Tests(unittest.TestCase):
1021+
def test_bug(self):
1022+
pass
1023+
""")
1024+
testname = self.create_test(code=code)
1025+
testname2 = self.create_test(code=code)
1026+
1027+
output = self.run_tests(testname, testname2, "-m", "nosuchtest", exitcode=0)
1028+
self.check_executed_tests(output, [testname, testname2],
1029+
no_test_ran=[testname, testname2])
1030+
1031+
def test_no_test_ran_some_test_exist_some_not(self):
1032+
code = textwrap.dedent("""
1033+
import unittest
1034+
1035+
class Tests(unittest.TestCase):
1036+
def test_bug(self):
1037+
pass
1038+
""")
1039+
testname = self.create_test(code=code)
1040+
other_code = textwrap.dedent("""
1041+
import unittest
1042+
1043+
class Tests(unittest.TestCase):
1044+
def test_other_bug(self):
1045+
pass
1046+
""")
1047+
testname2 = self.create_test(code=other_code)
1048+
1049+
output = self.run_tests(testname, testname2, "-m", "nosuchtest",
1050+
"-m", "test_other_bug", exitcode=0)
1051+
self.check_executed_tests(output, [testname, testname2],
1052+
no_test_ran=[testname])
9811053

9821054

9831055
class TestUtils(unittest.TestCase):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
regrtest issue a warning when no tests have been executed in a particular
2+
test file. Also, a new final result state is issued if no test have been
3+
executed across all test files. Patch by Pablo Galindo.

0 commit comments

Comments
 (0)