issue #406: test for FD leak after every TestCase

issue260
David Wilson 6 years ago
parent e9a6e4c3d2
commit 8a0b343760

@ -1,4 +1,5 @@
-r docs/docs-requirements.txt -r docs/docs-requirements.txt
psutil==5.4.8
coverage==4.5.1 coverage==4.5.1
Django==1.6.11 # Last version supporting 2.6. Django==1.6.11 # Last version supporting 2.6.
mock==2.0.0 mock==2.0.0

@ -10,7 +10,7 @@ import testlib
import plain_old_module import plain_old_module
class ConstructorTest(unittest2.TestCase): class ConstructorTest(testlib.TestCase):
klass = mitogen.core.CallError klass = mitogen.core.CallError
def test_string_noargs(self): def test_string_noargs(self):
@ -44,7 +44,7 @@ class ConstructorTest(unittest2.TestCase):
self.assertTrue('test_from_exc_tb' in e.args[0]) self.assertTrue('test_from_exc_tb' in e.args[0])
class PickleTest(unittest2.TestCase): class PickleTest(testlib.TestCase):
klass = mitogen.core.CallError klass = mitogen.core.CallError
def test_string_noargs(self): def test_string_noargs(self):

@ -7,7 +7,7 @@ import unittest2
import testlib import testlib
class ConstructorTest(testlib.RouterMixin, unittest2.TestCase): class ConstructorTest(testlib.RouterMixin, testlib.TestCase):
def test_okay(self): def test_okay(self):
docker_path = testlib.data_path('stubs/stub-docker.py') docker_path = testlib.data_path('stubs/stub-docker.py')
context = self.router.docker( context = self.router.docker(

@ -10,7 +10,7 @@ import mitogen.fakessh
import testlib import testlib
class RsyncTest(testlib.DockerMixin, unittest2.TestCase): class RsyncTest(testlib.DockerMixin, testlib.TestCase):
@timeoutcontext.timeout(5) @timeoutcontext.timeout(5)
@unittest2.skip('broken') @unittest2.skip('broken')
def test_rsync_from_master(self): def test_rsync_from_master(self):

@ -55,7 +55,7 @@ def exercise_importer(n):
return simple_pkg.a.subtract_one_add_two(n) return simple_pkg.a.subtract_one_add_two(n)
class ForkTest(testlib.RouterMixin, unittest2.TestCase): class ForkTest(testlib.RouterMixin, testlib.TestCase):
def test_okay(self): def test_okay(self):
context = self.router.fork() context = self.router.fork()
self.assertNotEqual(context.call(os.getpid), os.getpid()) self.assertNotEqual(context.call(os.getpid), os.getpid())
@ -84,7 +84,8 @@ class ForkTest(testlib.RouterMixin, unittest2.TestCase):
context = self.router.fork(on_start=on_start) context = self.router.fork(on_start=on_start)
self.assertEquals(123, recv.get().unpickle()) self.assertEquals(123, recv.get().unpickle())
class DoubleChildTest(testlib.RouterMixin, unittest2.TestCase):
class DoubleChildTest(testlib.RouterMixin, testlib.TestCase):
def test_okay(self): def test_okay(self):
# When forking from the master process, Mitogen had nothing to do with # When forking from the master process, Mitogen had nothing to do with
# setting up stdio -- that was inherited wherever the Master is running # setting up stdio -- that was inherited wherever the Master is running

@ -20,7 +20,7 @@ def get_os_environ():
return dict(os.environ) return dict(os.environ)
class LocalTest(testlib.RouterMixin, unittest2.TestCase): class LocalTest(testlib.RouterMixin, testlib.TestCase):
stream_class = mitogen.ssh.Stream stream_class = mitogen.ssh.Stream
def test_stream_name(self): def test_stream_name(self):
@ -29,7 +29,7 @@ class LocalTest(testlib.RouterMixin, unittest2.TestCase):
self.assertEquals('local.%d' % (pid,), context.name) self.assertEquals('local.%d' % (pid,), context.name)
class PythonPathTest(testlib.RouterMixin, unittest2.TestCase): class PythonPathTest(testlib.RouterMixin, testlib.TestCase):
stream_class = mitogen.ssh.Stream stream_class = mitogen.ssh.Stream
def test_inherited(self): def test_inherited(self):

@ -6,7 +6,7 @@ import testlib
import mitogen.master import mitogen.master
class ScanCodeImportsTest(unittest2.TestCase): class ScanCodeImportsTest(testlib.TestCase):
func = staticmethod(mitogen.master.scan_code_imports) func = staticmethod(mitogen.master.scan_code_imports)
if mitogen.core.PY3: if mitogen.core.PY3:

@ -16,7 +16,7 @@ def read_sample(fname):
return sample return sample
class MinimizeSourceTest(unittest2.TestCase): class MinimizeSourceTest(testlib.TestCase):
func = staticmethod(mitogen.minify.minimize_source) func = staticmethod(mitogen.minify.minimize_source)
def test_class(self): def test_class(self):
@ -55,7 +55,7 @@ class MinimizeSourceTest(unittest2.TestCase):
self.assertEqual(expected, self.func(original)) self.assertEqual(expected, self.func(original))
class MitogenCoreTest(unittest2.TestCase): class MitogenCoreTest(testlib.TestCase):
# Verify minimize_source() succeeds for all built-in modules. # Verify minimize_source() succeeds for all built-in modules.
func = staticmethod(mitogen.minify.minimize_source) func = staticmethod(mitogen.minify.minimize_source)

@ -153,7 +153,7 @@ class StreamErrorTest(testlib.RouterMixin, testlib.TestCase):
self.assertTrue(s in e.args[0]) self.assertTrue(s in e.args[0])
class ContextTest(testlib.RouterMixin, unittest2.TestCase): class ContextTest(testlib.RouterMixin, testlib.TestCase):
def test_context_shutdown(self): def test_context_shutdown(self):
local = self.router.local() local = self.router.local()
pid = local.call(os.getpid) pid = local.call(os.getpid)
@ -181,7 +181,7 @@ class OpenPtyTest(testlib.TestCase):
self.assertEquals(e.args[0], msg) self.assertEquals(e.args[0], msg)
class TtyCreateChildTest(unittest2.TestCase): class TtyCreateChildTest(testlib.TestCase):
func = staticmethod(mitogen.parent.tty_create_child) func = staticmethod(mitogen.parent.tty_create_child)
def test_dev_tty_open_succeeds(self): def test_dev_tty_open_succeeds(self):
@ -211,7 +211,7 @@ class TtyCreateChildTest(unittest2.TestCase):
tf.close() tf.close()
class IterReadTest(unittest2.TestCase): class IterReadTest(testlib.TestCase):
func = staticmethod(mitogen.parent.iter_read) func = staticmethod(mitogen.parent.iter_read)
def make_proc(self): def make_proc(self):
@ -263,7 +263,7 @@ class IterReadTest(unittest2.TestCase):
proc.terminate() proc.terminate()
class WriteAllTest(unittest2.TestCase): class WriteAllTest(testlib.TestCase):
func = staticmethod(mitogen.parent.write_all) func = staticmethod(mitogen.parent.write_all)
def make_proc(self): def make_proc(self):

@ -13,7 +13,7 @@ import plain_old_module
import simple_pkg.a import simple_pkg.a
class NeutralizeMainTest(testlib.RouterMixin, unittest2.TestCase): class NeutralizeMainTest(testlib.RouterMixin, testlib.TestCase):
klass = mitogen.master.ModuleResponder klass = mitogen.master.ModuleResponder
def call(self, *args, **kwargs): def call(self, *args, **kwargs):
@ -67,7 +67,7 @@ class NeutralizeMainTest(testlib.RouterMixin, unittest2.TestCase):
class GoodModulesTest(testlib.RouterMixin, unittest2.TestCase): class GoodModulesTest(testlib.RouterMixin, testlib.TestCase):
def test_plain_old_module(self): def test_plain_old_module(self):
# The simplest case: a top-level module with no interesting imports or # The simplest case: a top-level module with no interesting imports or
# package machinery damage. # package machinery damage.
@ -89,7 +89,7 @@ class GoodModulesTest(testlib.RouterMixin, unittest2.TestCase):
self.assertEquals(output, "['__main__', 50]\n") self.assertEquals(output, "['__main__', 50]\n")
class BrokenModulesTest(unittest2.TestCase): class BrokenModulesTest(testlib.TestCase):
def test_obviously_missing(self): def test_obviously_missing(self):
# Ensure we don't crash in the case of a module legitimately being # Ensure we don't crash in the case of a module legitimately being
# unavailable. Should never happen in the real world. # unavailable. Should never happen in the real world.
@ -144,7 +144,7 @@ class BrokenModulesTest(unittest2.TestCase):
self.assertIsInstance(msg.unpickle(), tuple) self.assertIsInstance(msg.unpickle(), tuple)
class BlacklistTest(unittest2.TestCase): class BlacklistTest(testlib.TestCase):
@unittest2.skip('implement me') @unittest2.skip('implement me')
def test_whitelist_no_blacklist(self): def test_whitelist_no_blacklist(self):
assert 0 assert 0

@ -36,7 +36,7 @@ def send_n_sized_reply(sender, n):
return 123 return 123
class SourceVerifyTest(testlib.RouterMixin, unittest2.TestCase): class SourceVerifyTest(testlib.RouterMixin, testlib.TestCase):
def setUp(self): def setUp(self):
super(SourceVerifyTest, self).setUp() super(SourceVerifyTest, self).setUp()
# Create some children, ping them, and store what their messages look # Create some children, ping them, and store what their messages look
@ -149,7 +149,7 @@ class PolicyTest(testlib.RouterMixin, testlib.TestCase):
self.assertEquals(e.args[0], self.router.refused_msg) self.assertEquals(e.args[0], self.router.refused_msg)
class CrashTest(testlib.BrokerMixin, unittest2.TestCase): class CrashTest(testlib.BrokerMixin, testlib.TestCase):
# This is testing both Broker's ability to crash nicely, and Router's # This is testing both Broker's ability to crash nicely, and Router's
# ability to respond to the crash event. # ability to respond to the crash event.
klass = mitogen.master.Router klass = mitogen.master.Router
@ -178,8 +178,7 @@ class CrashTest(testlib.BrokerMixin, unittest2.TestCase):
self.assertTrue(expect in log.stop()) self.assertTrue(expect in log.stop())
class AddHandlerTest(testlib.TestCase):
class AddHandlerTest(unittest2.TestCase):
klass = mitogen.master.Router klass = mitogen.master.Router
def test_invoked_at_shutdown(self): def test_invoked_at_shutdown(self):

@ -20,7 +20,7 @@ def roundtrip(v):
return mitogen.core.Message(data=msg.data).unpickle() return mitogen.core.Message(data=msg.data).unpickle()
class BlobTest(unittest2.TestCase): class BlobTest(testlib.TestCase):
klass = mitogen.core.Blob klass = mitogen.core.Blob
# Python 3 pickle protocol 2 does weird stuff depending on whether an empty # Python 3 pickle protocol 2 does weird stuff depending on whether an empty
@ -36,7 +36,7 @@ class BlobTest(unittest2.TestCase):
self.assertEquals(b(''), roundtrip(v)) self.assertEquals(b(''), roundtrip(v))
class ContextTest(testlib.RouterMixin, unittest2.TestCase): class ContextTest(testlib.RouterMixin, testlib.TestCase):
klass = mitogen.core.Context klass = mitogen.core.Context
# Ensure Context can be round-tripped by regular pickle in addition to # Ensure Context can be round-tripped by regular pickle in addition to

@ -29,7 +29,7 @@ class StubSshMixin(testlib.RouterMixin):
del os.environ['STUBSSH_MODE'] del os.environ['STUBSSH_MODE']
class ConstructorTest(testlib.RouterMixin, unittest2.TestCase): class ConstructorTest(testlib.RouterMixin, testlib.TestCase):
def test_okay(self): def test_okay(self):
context = self.router.ssh( context = self.router.ssh(
hostname='hostname', hostname='hostname',
@ -165,7 +165,7 @@ class SshTest(testlib.DockerMixin, testlib.TestCase):
fp.close() fp.close()
class BannerTest(testlib.DockerMixin, unittest2.TestCase): class BannerTest(testlib.DockerMixin, testlib.TestCase):
# Verify the ability to disambiguate random spam appearing in the SSHd's # Verify the ability to disambiguate random spam appearing in the SSHd's
# login banner from a legitimate password prompt. # login banner from a legitimate password prompt.
stream_class = mitogen.ssh.Stream stream_class = mitogen.ssh.Stream

@ -8,9 +8,11 @@ import subprocess
import sys import sys
import time import time
import psutil
import unittest2 import unittest2
import mitogen.core import mitogen.core
import mitogen.fork
import mitogen.master import mitogen.master
import mitogen.utils import mitogen.utils
@ -41,6 +43,13 @@ if faulthandler is not None:
faulthandler.enable() faulthandler.enable()
def get_fd_count():
"""
Return the number of FDs open by this process.
"""
return psutil.Process().num_fds()
def data_path(suffix): def data_path(suffix):
path = os.path.join(DATA_DIR, suffix) path = os.path.join(DATA_DIR, suffix)
if path.endswith('.key'): if path.endswith('.key'):
@ -211,6 +220,23 @@ class LogCapturer(object):
class TestCase(unittest2.TestCase): class TestCase(unittest2.TestCase):
@classmethod
def setUpClass(cls):
# This is done in setUpClass() so we have a chance to run before any
# Broker() instantiations in setUp() etc.
mitogen.fork.on_fork()
cls._fd_count_before = get_fd_count()
super(TestCase, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(TestCase, cls).tearDownClass()
mitogen.fork.on_fork()
assert get_fd_count() == cls._fd_count_before, \
"%s leaked FDs. Count before: %s, after: %s" % (
cls, cls._fd_count_before, get_fd_count(),
)
def assertRaises(self, exc, func, *args, **kwargs): def assertRaises(self, exc, func, *args, **kwargs):
"""Like regular assertRaises, except return the exception that was """Like regular assertRaises, except return the exception that was
raised. Can't use context manager because tests must run on Python2.4""" raised. Can't use context manager because tests must run on Python2.4"""

@ -11,8 +11,10 @@ import unittest2
import mitogen.core import mitogen.core
from mitogen.core import b from mitogen.core import b
import testlib
class BlobTest(unittest2.TestCase):
class BlobTest(testlib.TestCase):
klass = mitogen.core.Blob klass = mitogen.core.Blob
def make(self): def make(self):
@ -43,7 +45,7 @@ class BlobTest(unittest2.TestCase):
mitogen.core.BytesType(blob2)) mitogen.core.BytesType(blob2))
class SecretTest(unittest2.TestCase): class SecretTest(testlib.TestCase):
klass = mitogen.core.Secret klass = mitogen.core.Secret
def make(self): def make(self):

@ -30,7 +30,7 @@ class MyService(mitogen.service.Service):
} }
class IsPathDeadTest(unittest2.TestCase): class IsPathDeadTest(testlib.TestCase):
func = staticmethod(mitogen.unix.is_path_dead) func = staticmethod(mitogen.unix.is_path_dead)
path = '/tmp/stale-socket' path = '/tmp/stale-socket'
@ -57,7 +57,7 @@ class IsPathDeadTest(unittest2.TestCase):
os.unlink(self.path) os.unlink(self.path)
class ListenerTest(testlib.RouterMixin, unittest2.TestCase): class ListenerTest(testlib.RouterMixin, testlib.TestCase):
klass = mitogen.unix.Listener klass = mitogen.unix.Listener
def test_constructor_basic(self): def test_constructor_basic(self):
@ -66,7 +66,7 @@ class ListenerTest(testlib.RouterMixin, unittest2.TestCase):
os.unlink(listener.path) os.unlink(listener.path)
class ClientTest(unittest2.TestCase): class ClientTest(testlib.TestCase):
klass = mitogen.unix.Listener klass = mitogen.unix.Listener
def _try_connect(self, path): def _try_connect(self, path):

@ -6,6 +6,8 @@ import mitogen.core
import mitogen.master import mitogen.master
import mitogen.utils import mitogen.utils
import testlib
def func0(router): def func0(router):
return router return router
@ -16,7 +18,7 @@ def func(router):
return router return router
class RunWithRouterTest(unittest2.TestCase): class RunWithRouterTest(testlib.TestCase):
# test_shutdown_on_exception # test_shutdown_on_exception
# test_shutdown_on_success # test_shutdown_on_success
@ -26,7 +28,7 @@ class RunWithRouterTest(unittest2.TestCase):
self.assertFalse(router.broker._thread.isAlive()) self.assertFalse(router.broker._thread.isAlive())
class WithRouterTest(unittest2.TestCase): class WithRouterTest(testlib.TestCase):
def test_with_broker(self): def test_with_broker(self):
router = func() router = func()
self.assertIsInstance(router, mitogen.master.Router) self.assertIsInstance(router, mitogen.master.Router)
@ -40,7 +42,7 @@ class Unicode(mitogen.core.UnicodeType): pass
class Bytes(mitogen.core.BytesType): pass class Bytes(mitogen.core.BytesType): pass
class CastTest(unittest2.TestCase): class CastTest(testlib.TestCase):
def test_dict(self): def test_dict(self):
self.assertEqual(type(mitogen.utils.cast({})), dict) self.assertEqual(type(mitogen.utils.cast({})), dict)
self.assertEqual(type(mitogen.utils.cast(Dict())), dict) self.assertEqual(type(mitogen.utils.cast(Dict())), dict)

Loading…
Cancel
Save