diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2020-05-13 14:17:36 +0000 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2020-05-13 14:17:36 +0000 |
commit | f6383a4f81e36a5da3f4046e9cdeb8c5cc68aac2 (patch) | |
tree | 1f17bfa6db94807c8ca8a4d1fb125414a1925619 | |
parent | 913d4b1d6dcce88dea6ef9382b93883a04a66cd7 (diff) | |
download | psutil-f6383a4f81e36a5da3f4046e9cdeb8c5cc68aac2.tar.gz |
put fds test in mem leak class
-rw-r--r-- | psutil/tests/__init__.py | 119 | ||||
-rwxr-xr-x | psutil/tests/test_memory_leaks.py | 44 | ||||
-rwxr-xr-x | psutil/tests/test_testutils.py | 35 |
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): |