Skip to content

Commit 2cc75b6

Browse files
committed
pythongh-98903: Test suite fails with exit code 4 if no tests ran (python#98904)
The Python test suite now fails wit exit code 4 if no tests ran. It should help detecting typos in test names and test methods. * Add "EXITCODE_" constants to Lib/test/libregrtest/main.py. * Fix a typo: "NO TEST RUN" becomes "NO TESTS RAN" (cherry picked from commit c76db37)
1 parent 73334eb commit 2cc75b6

File tree

3 files changed

+52
-26
lines changed

3 files changed

+52
-26
lines changed

Lib/test/libregrtest/main.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
# Must be smaller than buildbot "1200 seconds without output" limit.
2929
EXIT_TIMEOUT = 120.0
3030

31+
EXITCODE_BAD_TEST = 2
32+
EXITCODE_INTERRUPTED = 130
33+
EXITCODE_ENV_CHANGED = 3
34+
EXITCODE_NO_TESTS_RAN = 4
35+
3136

3237
class Regrtest:
3338
"""Execute a test suite.
@@ -493,15 +498,18 @@ def display_header(self):
493498
print("== encodings: locale=%s, FS=%s"
494499
% (locale.getencoding(), sys.getfilesystemencoding()))
495500

501+
def no_tests_run(self):
502+
return not any((self.good, self.bad, self.skipped, self.interrupted,
503+
self.environment_changed))
504+
496505
def get_tests_result(self):
497506
result = []
498507
if self.bad:
499508
result.append("FAILURE")
500509
elif self.ns.fail_env_changed and self.environment_changed:
501510
result.append("ENV CHANGED")
502-
elif not any((self.good, self.bad, self.skipped, self.interrupted,
503-
self.environment_changed)):
504-
result.append("NO TEST RUN")
511+
elif self.no_tests_run():
512+
result.append("NO TESTS RAN")
505513

506514
if self.interrupted:
507515
result.append("INTERRUPTED")
@@ -750,11 +758,13 @@ def _main(self, tests, kwargs):
750758
self.save_xml_result()
751759

752760
if self.bad:
753-
sys.exit(2)
761+
sys.exit(EXITCODE_BAD_TEST)
754762
if self.interrupted:
755-
sys.exit(130)
763+
sys.exit(EXITCODE_INTERRUPTED)
756764
if self.ns.fail_env_changed and self.environment_changed:
757-
sys.exit(3)
765+
sys.exit(EXITCODE_ENV_CHANGED)
766+
if self.no_tests_run():
767+
sys.exit(EXITCODE_NO_TESTS_RAN)
758768
sys.exit(0)
759769

760770

Lib/test/test_regrtest.py

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
ROOT_DIR = os.path.abspath(os.path.normpath(ROOT_DIR))
3131
LOG_PREFIX = r'[0-9]+:[0-9]+:[0-9]+ (?:load avg: [0-9]+\.[0-9]{2} )?'
3232

33+
EXITCODE_BAD_TEST = 2
34+
EXITCODE_ENV_CHANGED = 3
35+
EXITCODE_NO_TESTS_RAN = 4
36+
EXITCODE_INTERRUPTED = 130
37+
3338
TEST_INTERRUPTED = textwrap.dedent("""
3439
from signal import SIGINT, raise_signal
3540
try:
@@ -499,7 +504,7 @@ def list_regex(line_format, tests):
499504
result.append('INTERRUPTED')
500505
if not any((good, result, failed, interrupted, skipped,
501506
env_changed, fail_env_changed)):
502-
result.append("NO TEST RUN")
507+
result.append("NO TESTS RAN")
503508
elif not result:
504509
result.append('SUCCESS')
505510
result = ', '.join(result)
@@ -709,7 +714,7 @@ def test_failing(self):
709714
test_failing = self.create_test('failing', code=code)
710715
tests = [test_ok, test_failing]
711716

712-
output = self.run_tests(*tests, exitcode=2)
717+
output = self.run_tests(*tests, exitcode=EXITCODE_BAD_TEST)
713718
self.check_executed_tests(output, tests, failed=test_failing)
714719

715720
def test_resources(self):
@@ -750,13 +755,14 @@ def test_random(self):
750755
test = self.create_test('random', code)
751756

752757
# first run to get the output with the random seed
753-
output = self.run_tests('-r', test)
758+
output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN)
754759
randseed = self.parse_random_seed(output)
755760
match = self.regex_search(r'TESTRANDOM: ([0-9]+)', output)
756761
test_random = int(match.group(1))
757762

758763
# try to reproduce with the random seed
759-
output = self.run_tests('-r', '--randseed=%s' % randseed, test)
764+
output = self.run_tests('-r', '--randseed=%s' % randseed, test,
765+
exitcode=EXITCODE_NO_TESTS_RAN)
760766
randseed2 = self.parse_random_seed(output)
761767
self.assertEqual(randseed2, randseed)
762768

@@ -815,7 +821,7 @@ def test_fromfile(self):
815821
def test_interrupted(self):
816822
code = TEST_INTERRUPTED
817823
test = self.create_test('sigint', code=code)
818-
output = self.run_tests(test, exitcode=130)
824+
output = self.run_tests(test, exitcode=EXITCODE_INTERRUPTED)
819825
self.check_executed_tests(output, test, omitted=test,
820826
interrupted=True)
821827

@@ -840,7 +846,7 @@ def test_slowest_interrupted(self):
840846
args = ("--slowest", "-j2", test)
841847
else:
842848
args = ("--slowest", test)
843-
output = self.run_tests(*args, exitcode=130)
849+
output = self.run_tests(*args, exitcode=EXITCODE_INTERRUPTED)
844850
self.check_executed_tests(output, test,
845851
omitted=test, interrupted=True)
846852

@@ -880,7 +886,7 @@ def test_run(self):
880886
builtins.__dict__['RUN'] = 1
881887
""")
882888
test = self.create_test('forever', code=code)
883-
output = self.run_tests('--forever', test, exitcode=2)
889+
output = self.run_tests('--forever', test, exitcode=EXITCODE_BAD_TEST)
884890
self.check_executed_tests(output, [test]*3, failed=test)
885891

886892
def check_leak(self, code, what):
@@ -889,7 +895,7 @@ def check_leak(self, code, what):
889895
filename = 'reflog.txt'
890896
self.addCleanup(os_helper.unlink, filename)
891897
output = self.run_tests('--huntrleaks', '3:3:', test,
892-
exitcode=2,
898+
exitcode=EXITCODE_BAD_TEST,
893899
stderr=subprocess.STDOUT)
894900
self.check_executed_tests(output, [test], failed=test)
895901

@@ -971,7 +977,7 @@ def test_crashed(self):
971977
crash_test = self.create_test(name="crash", code=code)
972978

973979
tests = [crash_test]
974-
output = self.run_tests("-j2", *tests, exitcode=2)
980+
output = self.run_tests("-j2", *tests, exitcode=EXITCODE_BAD_TEST)
975981
self.check_executed_tests(output, tests, failed=crash_test,
976982
randomize=True)
977983

@@ -1071,7 +1077,8 @@ def test_env_changed(self):
10711077
self.check_executed_tests(output, [testname], env_changed=testname)
10721078

10731079
# fail with --fail-env-changed
1074-
output = self.run_tests("--fail-env-changed", testname, exitcode=3)
1080+
output = self.run_tests("--fail-env-changed", testname,
1081+
exitcode=EXITCODE_ENV_CHANGED)
10751082
self.check_executed_tests(output, [testname], env_changed=testname,
10761083
fail_env_changed=True)
10771084

@@ -1090,7 +1097,7 @@ def test_fail_always(self):
10901097
""")
10911098
testname = self.create_test(code=code)
10921099

1093-
output = self.run_tests("-w", testname, exitcode=2)
1100+
output = self.run_tests("-w", testname, exitcode=EXITCODE_BAD_TEST)
10941101
self.check_executed_tests(output, [testname],
10951102
failed=testname, rerun={testname: "test_fail_always"})
10961103

@@ -1125,7 +1132,8 @@ def test_bug(self):
11251132
""")
11261133
testname = self.create_test(code=code)
11271134

1128-
output = self.run_tests(testname, "-m", "nosuchtest", exitcode=0)
1135+
output = self.run_tests(testname, "-m", "nosuchtest",
1136+
exitcode=EXITCODE_NO_TESTS_RAN)
11291137
self.check_executed_tests(output, [testname], no_test_ran=testname)
11301138

11311139
def test_no_tests_ran_skip(self):
@@ -1138,7 +1146,7 @@ def test_skipped(self):
11381146
""")
11391147
testname = self.create_test(code=code)
11401148

1141-
output = self.run_tests(testname, exitcode=0)
1149+
output = self.run_tests(testname)
11421150
self.check_executed_tests(output, [testname])
11431151

11441152
def test_no_tests_ran_multiple_tests_nonexistent(self):
@@ -1152,7 +1160,8 @@ def test_bug(self):
11521160
testname = self.create_test(code=code)
11531161
testname2 = self.create_test(code=code)
11541162

1155-
output = self.run_tests(testname, testname2, "-m", "nosuchtest", exitcode=0)
1163+
output = self.run_tests(testname, testname2, "-m", "nosuchtest",
1164+
exitcode=EXITCODE_NO_TESTS_RAN)
11561165
self.check_executed_tests(output, [testname, testname2],
11571166
no_test_ran=[testname, testname2])
11581167

@@ -1200,7 +1209,8 @@ def test_garbage(self):
12001209
""")
12011210
testname = self.create_test(code=code)
12021211

1203-
output = self.run_tests("--fail-env-changed", testname, exitcode=3)
1212+
output = self.run_tests("--fail-env-changed", testname,
1213+
exitcode=EXITCODE_ENV_CHANGED)
12041214
self.check_executed_tests(output, [testname],
12051215
env_changed=[testname],
12061216
fail_env_changed=True)
@@ -1226,7 +1236,8 @@ def test_sleep(self):
12261236
""")
12271237
testname = self.create_test(code=code)
12281238

1229-
output = self.run_tests("-j2", "--timeout=1.0", testname, exitcode=2)
1239+
output = self.run_tests("-j2", "--timeout=1.0", testname,
1240+
exitcode=EXITCODE_BAD_TEST)
12301241
self.check_executed_tests(output, [testname],
12311242
failed=testname)
12321243
self.assertRegex(output,
@@ -1258,7 +1269,8 @@ def test_unraisable_exc(self):
12581269
""")
12591270
testname = self.create_test(code=code)
12601271

1261-
output = self.run_tests("--fail-env-changed", "-v", testname, exitcode=3)
1272+
output = self.run_tests("--fail-env-changed", "-v", testname,
1273+
exitcode=EXITCODE_ENV_CHANGED)
12621274
self.check_executed_tests(output, [testname],
12631275
env_changed=[testname],
12641276
fail_env_changed=True)
@@ -1289,7 +1301,8 @@ def test_threading_excepthook(self):
12891301
""")
12901302
testname = self.create_test(code=code)
12911303

1292-
output = self.run_tests("--fail-env-changed", "-v", testname, exitcode=3)
1304+
output = self.run_tests("--fail-env-changed", "-v", testname,
1305+
exitcode=EXITCODE_ENV_CHANGED)
12931306
self.check_executed_tests(output, [testname],
12941307
env_changed=[testname],
12951308
fail_env_changed=True)
@@ -1330,7 +1343,7 @@ def test_print_warning(self):
13301343
for option in ("-v", "-W"):
13311344
with self.subTest(option=option):
13321345
cmd = ["--fail-env-changed", option, testname]
1333-
output = self.run_tests(*cmd, exitcode=3)
1346+
output = self.run_tests(*cmd, exitcode=EXITCODE_ENV_CHANGED)
13341347
self.check_executed_tests(output, [testname],
13351348
env_changed=[testname],
13361349
fail_env_changed=True)
@@ -1375,7 +1388,8 @@ def test_leak_tmp_file(self):
13751388
""")
13761389
testnames = [self.create_test(code=code) for _ in range(3)]
13771390

1378-
output = self.run_tests("--fail-env-changed", "-v", "-j2", *testnames, exitcode=3)
1391+
output = self.run_tests("--fail-env-changed", "-v", "-j2", *testnames,
1392+
exitcode=EXITCODE_ENV_CHANGED)
13791393
self.check_executed_tests(output, testnames,
13801394
env_changed=testnames,
13811395
fail_env_changed=True,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The Python test suite now fails wit exit code 4 if no tests ran. It should
2+
help detecting typos in test names and test methods.

0 commit comments

Comments
 (0)