summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2020-05-13 14:17:36 +0000
committerGiampaolo Rodola <g.rodola@gmail.com>2020-05-13 14:17:36 +0000
commitf6383a4f81e36a5da3f4046e9cdeb8c5cc68aac2 (patch)
tree1f17bfa6db94807c8ca8a4d1fb125414a1925619
parent913d4b1d6dcce88dea6ef9382b93883a04a66cd7 (diff)
downloadpsutil-f6383a4f81e36a5da3f4046e9cdeb8c5cc68aac2.tar.gz
put fds test in mem leak class
-rw-r--r--psutil/tests/__init__.py119
-rwxr-xr-xpsutil/tests/test_memory_leaks.py44
-rwxr-xr-xpsutil/tests/test_testutils.py35
3 files changed, 70 insertions, 128 deletions
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index cda88b38..ccfcb60c 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -89,7 +89,7 @@ __all__ = [
'ThreadTask'
# test utils
'unittest', 'skip_on_access_denied', 'skip_on_not_implemented',
- 'retry_on_failure', 'TestMemoryLeak', 'TestFdsLeak', 'PsutilTestCase',
+ 'retry_on_failure', 'TestMemoryLeak', 'PsutilTestCase',
'process_namespace', 'system_namespace',
# install utils
'install_pip', 'install_test_deps',
@@ -931,13 +931,10 @@ class TestMemoryLeak(PsutilTestCase):
# Configurable class attrs.
times = 200
warmup_times = 10
- retries = 5
tolerance = 0 # memory
+ retries = 5
verbose = True
-
- @classmethod
- def setUpClass(cls):
- cls._thisproc = psutil.Process()
+ _thisproc = psutil.Process()
def _get_mem(self):
# USS is the closest thing we have to "real" memory usage and it
@@ -945,9 +942,38 @@ class TestMemoryLeak(PsutilTestCase):
mem = self._thisproc.memory_full_info()
return getattr(mem, "uss", mem.rss)
+ def _get_num_fds(self):
+ if POSIX:
+ return self._thisproc.num_fds()
+ else:
+ return self._thisproc.num_handles()
+
def _call(self, fun):
return fun()
+ def _log(self, msg):
+ if self.verbose:
+ print_color(msg, color="yellow", file=sys.stderr)
+
+ def _check_fds(self, fun):
+ """Makes sure num_fds() (POSIX) or num_handles() (Windows) does
+ not increase after calling a function. Used to discover forgotten
+ close(2) and CloseHandle syscalls.
+ """
+ before = self._get_num_fds()
+ self._call(fun)
+ after = self._get_num_fds()
+ diff = after - before
+ if diff < 0:
+ raise self.fail("negative diff %r (gc probably collected a "
+ "resource from a previous test)" % diff)
+ if diff > 0:
+ type_ = "fd" if POSIX else "handle"
+ if diff > 1:
+ type_ += "s"
+ msg = "%s unclosed %s after calling %r" % (diff, type_, fun)
+ raise self.fail(msg)
+
def _call_ntimes(self, fun, times):
"""Get 2 distinct memory samples, before and after having
called fun repeadetly, and return the memory difference.
@@ -963,28 +989,7 @@ class TestMemoryLeak(PsutilTestCase):
diff = mem2 - mem1 # can also be negative
return diff
- def _log(self, msg):
- if self.verbose:
- print_color(msg, color="yellow", file=sys.stderr)
-
- def execute(self, fun, times=None, warmup_times=None, retries=None,
- tolerance=None):
- """Test a callable."""
- times = times if times is not None else self.times
- warmup_times = warmup_times if warmup_times is not None \
- else self.warmup_times
- retries = retries if retries is not None else self.retries
- tolerance = tolerance if tolerance is not None else self.tolerance
- try:
- assert times >= 1, "times must be >= 1"
- assert warmup_times >= 0, "warmup_times must be >= 0"
- assert retries >= 0, "retries must be >= 0"
- assert tolerance >= 0, "tolerance must be >= 0"
- except AssertionError as err:
- raise ValueError(str(err))
-
- # warm up
- self._call_ntimes(fun, warmup_times)
+ def _check_mem(self, fun, times, warmup_times, retries, tolerance):
messages = []
prev_mem = 0
increase = times
@@ -1006,6 +1011,27 @@ class TestMemoryLeak(PsutilTestCase):
prev_mem = mem
raise self.fail(". ".join(messages))
+ def execute(self, fun, times=None, warmup_times=None, retries=None,
+ tolerance=None):
+ """Test a callable."""
+ times = times if times is not None else self.times
+ warmup_times = warmup_times if warmup_times is not None \
+ else self.warmup_times
+ retries = retries if retries is not None else self.retries
+ tolerance = tolerance if tolerance is not None else self.tolerance
+ try:
+ assert times >= 1, "times must be >= 1"
+ assert warmup_times >= 0, "warmup_times must be >= 0"
+ assert retries >= 0, "retries must be >= 0"
+ assert tolerance >= 0, "tolerance must be >= 0"
+ except AssertionError as err:
+ raise ValueError(str(err))
+
+ self._call_ntimes(fun, warmup_times) # warm up
+ self._check_fds(fun)
+ self._check_mem(fun, times=times, warmup_times=warmup_times,
+ retries=retries, tolerance=tolerance)
+
def execute_w_exc(self, exc, fun, **kwargs):
"""Convenience method to test a callable while making sure it
raises an exception on every call.
@@ -1016,43 +1042,6 @@ class TestMemoryLeak(PsutilTestCase):
self.execute(call, **kwargs)
-class TestFdsLeak(PsutilTestCase):
- """Test framework class which makes sure num_fds() (POSIX) or
- num_handles() (Windows) does not increase after calling a function.
- This can be used to discover forgotten close(2) and CloseHandle
- syscalls.
- """
-
- tolerance = 0
- _thisproc = psutil.Process()
-
- def _get_fds_or_handles(self):
- if POSIX:
- return self._thisproc.num_fds()
- else:
- return self._thisproc.num_handles()
-
- def _call(self, fun):
- return fun()
-
- def execute(self, fun, tolerance=tolerance):
- # This is supposed to close() any unclosed file object.
- gc.collect()
- before = self._get_fds_or_handles()
- self._call(fun)
- after = self._get_fds_or_handles()
- diff = after - before
- if diff < 0:
- raise self.fail("negative diff %r (gc probably collected a "
- "resource from a previous test)" % diff)
- if diff > 0:
- type_ = "fd" if POSIX else "handle"
- if diff > 1:
- type_ += "s"
- msg = "%s unclosed %s after calling %r" % (diff, type_, fun)
- raise self.fail(msg)
-
-
def _get_eligible_cpu():
p = psutil.Process()
if hasattr(p, "cpu_num"):
diff --git a/psutil/tests/test_memory_leaks.py b/psutil/tests/test_memory_leaks.py
index 0bf2ad8d..b722f4fa 100755
--- a/psutil/tests/test_memory_leaks.py
+++ b/psutil/tests/test_memory_leaks.py
@@ -48,7 +48,6 @@ from psutil.tests import skip_on_access_denied
from psutil.tests import spawn_testproc
from psutil.tests import system_namespace
from psutil.tests import terminate
-from psutil.tests import TestFdsLeak
from psutil.tests import TestMemoryLeak
from psutil.tests import TRAVIS
from psutil.tests import unittest
@@ -471,49 +470,6 @@ class TestModuleFunctionsLeaks(TestMemoryLeak):
self.execute(lambda: cext.winservice_query_descr(name))
-# =====================================================================
-# --- File descriptors and handlers
-# =====================================================================
-
-
-class TestUnclosedFdsOrHandles(TestFdsLeak):
- # Note: on Windows certain C functions increase the handles count
- # on first call (and never again).
-
- def test_process_apis(self):
- p = psutil.Process()
- ns = process_namespace(p)
- for fun, name in ns.iter(ns.getters + ns.setters):
- if WINDOWS:
- fun()
- self.execute(fun)
-
- def test_process_apis_nsp(self):
- def wrapper(fun):
- try:
- fun()
- except psutil.NoSuchProcess:
- pass
-
- p = psutil.Process(self.spawn_testproc().pid)
- p.terminate()
- p.wait()
- ns = process_namespace(p)
- for fun, name in ns.iter(ns.getters + ns.setters + ns.killers):
- if WINDOWS:
- wrapper(fun)
- self.execute(lambda: wrapper(fun))
-
- def test_system_apis(self):
- ns = system_namespace
- for fun, name in ns.iter(ns.all):
- if WINDOWS:
- fun()
- if MACOS and name == 'net_connections':
- continue # raise AD
- self.execute(fun)
-
-
if __name__ == '__main__':
from psutil.tests.runner import run_from_name
run_from_name(__file__)
diff --git a/psutil/tests/test_testutils.py b/psutil/tests/test_testutils.py
index 6c510094..1ceccbe2 100755
--- a/psutil/tests/test_testutils.py
+++ b/psutil/tests/test_testutils.py
@@ -43,7 +43,6 @@ from psutil.tests import serialrun
from psutil.tests import system_namespace
from psutil.tests import tcp_socketpair
from psutil.tests import terminate
-from psutil.tests import TestFdsLeak
from psutil.tests import TestMemoryLeak
from psutil.tests import unittest
from psutil.tests import unix_socketpair
@@ -354,7 +353,7 @@ class TestMemLeakClass(TestMemoryLeak):
cnt['cnt'] += 1
cnt = {'cnt': 0}
self.execute(fun, times=10, warmup_times=15)
- self.assertEqual(cnt['cnt'], 25)
+ self.assertEqual(cnt['cnt'], 26)
def test_param_err(self):
self.assertRaises(ValueError, self.execute, lambda: 0, times=0)
@@ -363,17 +362,29 @@ class TestMemLeakClass(TestMemoryLeak):
self.assertRaises(ValueError, self.execute, lambda: 0, tolerance=-1)
self.assertRaises(ValueError, self.execute, lambda: 0, retries=-1)
- def test_leak(self):
+ def test_leak_mem(self):
ls = []
def fun(ls=ls):
ls.append("x" * 24 * 1024)
try:
- self.assertRaises(AssertionError, self.execute, fun)
+ self.assertRaisesRegex(AssertionError, "extra-mem",
+ self.execute, fun)
finally:
del ls
+ def test_unclosed_files(self):
+ def fun():
+ f = open(__file__)
+ self.addCleanup(f.close)
+ box.append(f)
+
+ box = []
+ kind = "fd" if POSIX else "handle"
+ self.assertRaisesRegex(AssertionError, "unclosed " + kind,
+ self.execute, fun)
+
def test_tolerance(self):
def fun():
ls.append("x" * 24 * 1024)
@@ -381,7 +392,7 @@ class TestMemLeakClass(TestMemoryLeak):
times = 100
self.execute(fun, times=times, warmup_times=0,
tolerance=200 * 1024 * 1024)
- self.assertEqual(len(ls), times)
+ self.assertEqual(len(ls), times + 1)
def test_execute_w_exc(self):
def fun():
@@ -396,20 +407,6 @@ class TestMemLeakClass(TestMemoryLeak):
self.execute_w_exc(ZeroDivisionError, fun)
-@serialrun
-class TestFdsLeakClass(TestFdsLeak):
-
- def test_unclosed_files(self):
- def fun():
- f = open(__file__)
- self.addCleanup(f.close)
- box.append(f)
-
- box = []
- self.assertRaisesRegex(AssertionError, "unclosed", self.execute, fun)
- self.assertEqual(len(box), 1)
-
-
class TestTestingUtils(PsutilTestCase):
def test_process_namespace(self):