cpython: 72129c767c92 (original) (raw)
Mercurial > cpython
changeset 98492:72129c767c92
Issue #18174: "python -m test --huntrleaks ..." now also checks for leak of file descriptors. Patch written by Richard Oudkerk. [#18174]
Victor Stinner victor.stinner@gmail.com | |
---|---|
date | Sat, 03 Oct 2015 00:20:56 +0200 |
parents | 4da7edbf78d4 |
children | 1005573e6a74 |
files | Lib/test/libregrtest/refleak.py Lib/test/test_regrtest.py Misc/NEWS |
diffstat | 3 files changed, 94 insertions(+), 19 deletions(-)[+] [-] Lib/test/libregrtest/refleak.py 47 Lib/test/test_regrtest.py 63 Misc/NEWS 3 |
line wrap: on
line diff
--- a/Lib/test/libregrtest/refleak.py +++ b/Lib/test/libregrtest/refleak.py @@ -1,3 +1,4 @@ +import errno import os import re import sys @@ -6,6 +7,36 @@ from inspect import isabstract from test import support +try:
- """Count the number of open file descriptors"""
- if sys.platform.startswith(('linux', 'freebsd')):
try:[](#l1.21)
names = os.listdir("/proc/self/fd")[](#l1.22)
return len(names)[](#l1.23)
except FileNotFoundError:[](#l1.24)
pass[](#l1.25)
- count = 0
- for fd in range(MAXFD):
try:[](#l1.29)
# Prefer dup() over fstat(). fstat() can require input/output[](#l1.30)
# whereas dup() doesn't.[](#l1.31)
fd2 = os.dup(fd)[](#l1.32)
except OSError as e:[](#l1.33)
if e.errno != errno.EBADF:[](#l1.34)
raise[](#l1.35)
else:[](#l1.36)
os.close(fd2)[](#l1.37)
count += 1[](#l1.38)
- return count
+ + def dash_R(the_module, test, indirect_test, huntrleaks): """Run a test multiple times, looking for reference leaks. @@ -42,20 +73,25 @@ def dash_R(the_module, test, indirect_te repcount = nwarmup + ntracked rc_deltas = [0] * repcount alloc_deltas = [0] * repcount
print("beginning", repcount, "repetitions", file=sys.stderr) print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr, flush=True) # initialize variables to make pyflakes quiet
alloc_after, rc_after = dash_R_cleanup(fs, ps, pic, zdc, abcs)[](#l1.59)
alloc_after, rc_after, fd_after = dash_R_cleanup(fs, ps, pic, zdc,[](#l1.60)
abcs)[](#l1.61) print('.', end='', flush=True)[](#l1.62) if i >= nwarmup:[](#l1.63) rc_deltas[i] = rc_after - rc_before[](#l1.64) alloc_deltas[i] = alloc_after - alloc_before[](#l1.65)
alloc_before, rc_before = alloc_after, rc_after[](#l1.66)
fd_deltas[i] = fd_after - fd_before[](#l1.67)
alloc_before = alloc_after[](#l1.68)
rc_before = rc_after[](#l1.69)
print(file=sys.stderr)fd_before = fd_after[](#l1.70)
def check_rc_deltas(deltas): These checkers return False on success, True on failure @@ -71,7 +107,8 @@ def dash_R(the_module, test, indirect_te failed = False for deltas, item_name, checker in [ (rc_deltas, 'references', check_rc_deltas),
(alloc_deltas, 'memory blocks', check_alloc_deltas)]:[](#l1.78)
(alloc_deltas, 'memory blocks', check_alloc_deltas),[](#l1.79)
(fd_deltas, 'file descriptors', check_rc_deltas)]:[](#l1.80) if checker(deltas):[](#l1.81) msg = '%s leaked %s %s, sum=%s' % ([](#l1.82) test, deltas[nwarmup:], item_name, sum(deltas))[](#l1.83)
@@ -151,7 +188,7 @@ def dash_R_cleanup(fs, ps, pic, zdc, abc func1 = sys.getallocatedblocks func2 = sys.gettotalrefcount gc.collect()
--- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -383,27 +383,32 @@ class BaseTestCase(unittest.TestCase): self.assertTrue(0 <= randseed <= 10000000, randseed) return randseed
- def run_command(self, args, input=None, exitcode=0, **kw): if not input: input = ''
if 'stderr' not in kw:[](#l2.11)
kw['stderr'] = subprocess.PIPE[](#l2.12) proc = subprocess.run(args,[](#l2.13) universal_newlines=True,[](#l2.14) input=input,[](#l2.15) stdout=subprocess.PIPE,[](#l2.16)
stderr=subprocess.PIPE)[](#l2.17)
**kw)[](#l2.18) if proc.returncode != exitcode:[](#l2.19)
self.fail("Command %s failed with exit code %s\n"[](#l2.20)
"\n"[](#l2.21)
"stdout:\n"[](#l2.22)
"---\n"[](#l2.23)
"%s\n"[](#l2.24)
"---\n"[](#l2.25)
"\n"[](#l2.26)
"stderr:\n"[](#l2.27)
"---\n"[](#l2.28)
"%s"[](#l2.29)
"---\n"[](#l2.30)
% (str(args), proc.returncode, proc.stdout, proc.stderr))[](#l2.31)
msg = ("Command %s failed with exit code %s\n"[](#l2.32)
"\n"[](#l2.33)
"stdout:\n"[](#l2.34)
"---\n"[](#l2.35)
"%s\n"[](#l2.36)
"---\n"[](#l2.37)
% (str(args), proc.returncode, proc.stdout))[](#l2.38)
if proc.stderr:[](#l2.39)
msg += ("\n"[](#l2.40)
"stderr:\n"[](#l2.41)
"---\n"[](#l2.42)
"%s"[](#l2.43)
"---\n"[](#l2.44)
% proc.stderr)[](#l2.45)
self.fail(msg)[](#l2.46) return proc[](#l2.47)
@@ -637,6 +642,36 @@ class ArgsTestCase(BaseTestCase): output = self.run_tests('--forever', test, exitcode=1) self.check_executed_tests(output, [test]*3, failed=test)
- def test_huntrleaks_fd_leak(self):
# test --huntrleaks for file descriptor leak[](#l2.55)
code = textwrap.dedent("""[](#l2.56)
import os[](#l2.57)
import unittest[](#l2.58)
class FDLeakTest(unittest.TestCase):[](#l2.60)
def test_leak(self):[](#l2.61)
fd = os.open(__file__, os.O_RDONLY)[](#l2.62)
# bug: never cloes the file descriptor[](#l2.63)
""")[](#l2.64)
test = self.create_test(code=code)[](#l2.65)
filename = 'reflog.txt'[](#l2.67)
self.addCleanup(support.unlink, filename)[](#l2.68)
output = self.run_tests('--huntrleaks', '3:3:', test,[](#l2.69)
exitcode=1,[](#l2.70)
stderr=subprocess.STDOUT)[](#l2.71)
self.check_executed_tests(output, [test], failed=test)[](#l2.72)
line = 'beginning 6 repetitions\n123456\n......\n'[](#l2.74)
self.check_line(output, re.escape(line))[](#l2.75)
line2 = '%s leaked [1, 1, 1] file descriptors, sum=3\n' % test[](#l2.77)
self.check_line(output, re.escape(line2))[](#l2.78)
with open(filename) as fp:[](#l2.80)
reflog = fp.read()[](#l2.81)
self.assertEqual(reflog, line2)[](#l2.82)
+ if name == 'main': unittest.main()
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -170,6 +170,9 @@ Documentation
Tests
-----
+- Issue #18174: python -m test --huntrleaks ...
now also checks for leak of