summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2017-06-02 14:06:28 +0200
committerGiampaolo Rodola <g.rodola@gmail.com>2017-06-02 14:06:28 +0200
commit65017b1a4e792b30dd8f16f999d6596fc295f66f (patch)
treed28466860c02c490f165aa92dd05fca9868baf2a
parent0058541a4e53ce5c32366349c6da317a10568dbb (diff)
downloadpsutil-65017b1a4e792b30dd8f16f999d6596fc295f66f.tar.gz
add create_zombie_proc utility function
-rw-r--r--psutil/tests/__init__.py40
-rwxr-xr-xpsutil/tests/test_misc.py7
-rwxr-xr-xpsutil/tests/test_process.py143
3 files changed, 99 insertions, 91 deletions
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 24718edd..ff452e55 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -18,6 +18,7 @@ import functools
import os
import random
import re
+import select
import shutil
import socket
import stat
@@ -35,6 +36,7 @@ from socket import SOCK_DGRAM
from socket import SOCK_STREAM
import psutil
+from psutil import OSX
from psutil import POSIX
from psutil import SUNOS
from psutil import WINDOWS
@@ -71,7 +73,7 @@ __all__ = [
"HAS_SENSORS_BATTERY", "HAS_BATTERY""HAS_SENSORS_FANS",
"HAS_SENSORS_TEMPERATURES", "HAS_MEMORY_FULL_INFO",
# subprocesses
- 'pyrun', 'reap_children', 'get_test_subprocess',
+ 'pyrun', 'reap_children', 'get_test_subprocess', 'create_zombie_proc',
'create_proc_children_pair',
# threads
'ThreadTask'
@@ -330,6 +332,42 @@ def create_proc_children_pair():
return (child1, child2)
+def create_zombie_proc():
+ """Create a zombie process and return its PID."""
+ unix_file = tempfile.mktemp(prefix=TESTFILE_PREFIX) if OSX else TESTFN
+ src = textwrap.dedent("""\
+ import os, sys, time, socket, contextlib
+ child_pid = os.fork()
+ if child_pid > 0:
+ time.sleep(3000)
+ else:
+ # this is the zombie process
+ s = socket.socket(socket.AF_UNIX)
+ with contextlib.closing(s):
+ s.connect('%s')
+ if sys.version_info < (3, ):
+ pid = str(os.getpid())
+ else:
+ pid = bytes(str(os.getpid()), 'ascii')
+ s.sendall(pid)
+ """ % unix_file)
+ with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock:
+ sock.settimeout(GLOBAL_TIMEOUT)
+ sock.bind(unix_file)
+ sock.listen(1)
+ pyrun(src)
+ conn, _ = sock.accept()
+ try:
+ select.select([conn.fileno()], [], [], GLOBAL_TIMEOUT)
+ zpid = int(conn.recv(1024))
+ _pids_started.add(zpid)
+ zproc = psutil.Process(zpid)
+ call_until(lambda: zproc.status(), "ret == psutil.STATUS_ZOMBIE")
+ return zpid
+ finally:
+ conn.close()
+
+
@_cleanup_on_err
def pyrun(src, **kwds):
"""Run python 'src' code string in a separate interpreter.
diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py
index f9459d30..c38dd9c4 100755
--- a/psutil/tests/test_misc.py
+++ b/psutil/tests/test_misc.py
@@ -35,6 +35,7 @@ from psutil.tests import call_until
from psutil.tests import chdir
from psutil.tests import create_proc_children_pair
from psutil.tests import create_sockets
+from psutil.tests import create_zombie_proc
from psutil.tests import DEVNULL
from psutil.tests import get_free_port
from psutil.tests import get_test_subprocess
@@ -944,6 +945,12 @@ class TestProcessUtils(unittest.TestCase):
assert not psutil.tests._pids_started
assert not psutil.tests._subprocesses_started
+ def test_create_zombie_proc(self):
+ zpid = create_zombie_proc()
+ self.addCleanup(reap_children, recursive=True)
+ p = psutil.Process(zpid)
+ self.assertEqual(p.status(), psutil.STATUS_ZOMBIE)
+
class TestNetUtils(unittest.TestCase):
diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py
index cab5a2fe..dd0c507e 100755
--- a/psutil/tests/test_process.py
+++ b/psutil/tests/test_process.py
@@ -7,11 +7,9 @@
"""Tests for psutil.Process class."""
import collections
-import contextlib
import errno
import getpass
import os
-import select
import signal
import socket
import subprocess
@@ -38,10 +36,10 @@ from psutil.tests import call_until
from psutil.tests import copyload_shared_lib
from psutil.tests import create_exe
from psutil.tests import create_proc_children_pair
+from psutil.tests import create_zombie_proc
from psutil.tests import enum
from psutil.tests import get_test_subprocess
from psutil.tests import get_winver
-from psutil.tests import GLOBAL_TIMEOUT
from psutil.tests import HAS_CPU_AFFINITY
from psutil.tests import HAS_ENVIRON
from psutil.tests import HAS_IONICE
@@ -51,7 +49,6 @@ from psutil.tests import HAS_PROC_IO_COUNTERS
from psutil.tests import HAS_RLIMIT
from psutil.tests import mock
from psutil.tests import PYPY
-from psutil.tests import pyrun
from psutil.tests import PYTHON
from psutil.tests import reap_children
from psutil.tests import retry_before_failing
@@ -1243,92 +1240,58 @@ class TestProcess(unittest.TestCase):
except (psutil.ZombieProcess, psutil.AccessDenied):
pass
- # Note: in this test we'll be creating two sub processes.
- # Both of them are supposed to be freed / killed by
- # reap_children() as they are attributable to 'us'
- # (os.getpid()) via children(recursive=True).
- unix_file = tempfile.mktemp(prefix=TESTFILE_PREFIX) if OSX else TESTFN
- src = textwrap.dedent("""\
- import os, sys, time, socket, contextlib
- child_pid = os.fork()
- if child_pid > 0:
- time.sleep(3000)
- else:
- # this is the zombie process
- s = socket.socket(socket.AF_UNIX)
- with contextlib.closing(s):
- s.connect('%s')
- if sys.version_info < (3, ):
- pid = str(os.getpid())
- else:
- pid = bytes(str(os.getpid()), 'ascii')
- s.sendall(pid)
- """ % unix_file)
- with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock:
- try:
- sock.settimeout(GLOBAL_TIMEOUT)
- sock.bind(unix_file)
- sock.listen(1)
- pyrun(src)
- conn, _ = sock.accept()
- self.addCleanup(conn.close)
- select.select([conn.fileno()], [], [], GLOBAL_TIMEOUT)
- zpid = int(conn.recv(1024))
- zproc = psutil.Process(zpid)
- call_until(lambda: zproc.status(),
- "ret == psutil.STATUS_ZOMBIE")
- # A zombie process should always be instantiable
- zproc = psutil.Process(zpid)
- # ...and at least its status always be querable
- self.assertEqual(zproc.status(), psutil.STATUS_ZOMBIE)
- # ...and it should be considered 'running'
- self.assertTrue(zproc.is_running())
- # ...and as_dict() shouldn't crash
- zproc.as_dict()
- # if cmdline succeeds it should be an empty list
- ret = succeed_or_zombie_p_exc(zproc.suspend)
- if ret is not None:
- self.assertEqual(ret, [])
-
- if hasattr(zproc, "rlimit"):
- succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE)
- succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE,
- (5, 5))
- # set methods
- succeed_or_zombie_p_exc(zproc.parent)
- if hasattr(zproc, 'cpu_affinity'):
- succeed_or_zombie_p_exc(zproc.cpu_affinity, [0])
- succeed_or_zombie_p_exc(zproc.nice, 0)
- if hasattr(zproc, 'ionice'):
- if LINUX:
- succeed_or_zombie_p_exc(zproc.ionice, 2, 0)
- else:
- succeed_or_zombie_p_exc(zproc.ionice, 0) # Windows
- if hasattr(zproc, 'rlimit'):
- succeed_or_zombie_p_exc(zproc.rlimit,
- psutil.RLIMIT_NOFILE, (5, 5))
- succeed_or_zombie_p_exc(zproc.suspend)
- succeed_or_zombie_p_exc(zproc.resume)
- succeed_or_zombie_p_exc(zproc.terminate)
- succeed_or_zombie_p_exc(zproc.kill)
-
- # ...its parent should 'see' it
- # edit: not true on BSD and OSX
- # descendants = [x.pid for x in psutil.Process().children(
- # recursive=True)]
- # self.assertIn(zpid, descendants)
- # XXX should we also assume ppid be usable? Note: this
- # would be an important use case as the only way to get
- # rid of a zombie is to kill its parent.
- # self.assertEqual(zpid.ppid(), os.getpid())
- # ...and all other APIs should be able to deal with it
- self.assertTrue(psutil.pid_exists(zpid))
- self.assertIn(zpid, psutil.pids())
- self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
- psutil._pmap = {}
- self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
- finally:
- reap_children(recursive=True)
+ zpid = create_zombie_proc()
+ self.addCleanup(reap_children, recursive=True)
+ # A zombie process should always be instantiable
+ zproc = psutil.Process(zpid)
+ # ...and at least its status always be querable
+ self.assertEqual(zproc.status(), psutil.STATUS_ZOMBIE)
+ # ...and it should be considered 'running'
+ self.assertTrue(zproc.is_running())
+ # ...and as_dict() shouldn't crash
+ zproc.as_dict()
+ # if cmdline succeeds it should be an empty list
+ ret = succeed_or_zombie_p_exc(zproc.suspend)
+ if ret is not None:
+ self.assertEqual(ret, [])
+
+ if hasattr(zproc, "rlimit"):
+ succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE)
+ succeed_or_zombie_p_exc(zproc.rlimit, psutil.RLIMIT_NOFILE,
+ (5, 5))
+ # set methods
+ succeed_or_zombie_p_exc(zproc.parent)
+ if hasattr(zproc, 'cpu_affinity'):
+ succeed_or_zombie_p_exc(zproc.cpu_affinity, [0])
+ succeed_or_zombie_p_exc(zproc.nice, 0)
+ if hasattr(zproc, 'ionice'):
+ if LINUX:
+ succeed_or_zombie_p_exc(zproc.ionice, 2, 0)
+ else:
+ succeed_or_zombie_p_exc(zproc.ionice, 0) # Windows
+ if hasattr(zproc, 'rlimit'):
+ succeed_or_zombie_p_exc(zproc.rlimit,
+ psutil.RLIMIT_NOFILE, (5, 5))
+ succeed_or_zombie_p_exc(zproc.suspend)
+ succeed_or_zombie_p_exc(zproc.resume)
+ succeed_or_zombie_p_exc(zproc.terminate)
+ succeed_or_zombie_p_exc(zproc.kill)
+
+ # ...its parent should 'see' it
+ # edit: not true on BSD and OSX
+ # descendants = [x.pid for x in psutil.Process().children(
+ # recursive=True)]
+ # self.assertIn(zpid, descendants)
+ # XXX should we also assume ppid be usable? Note: this
+ # would be an important use case as the only way to get
+ # rid of a zombie is to kill its parent.
+ # self.assertEqual(zpid.ppid(), os.getpid())
+ # ...and all other APIs should be able to deal with it
+ self.assertTrue(psutil.pid_exists(zpid))
+ self.assertIn(zpid, psutil.pids())
+ self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
+ psutil._pmap = {}
+ self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
@unittest.skipIf(not POSIX, 'POSIX only')
def test_zombie_process_is_running_w_exc(self):