def check_process_termination(self, method): """Helper method for process termination tests.""" timer = Timer() # We use Executor to launch an external process. with ExternalCommand('sleep', '60', check=False) as cmd: # Verify that proc.unix.UnixProcess.is_running (which is normally # overridden by proc.core.Process.is_running) works as expected, # even though this property isn't actively used in the `proc' # package because we want to deal with not-yet-reclaimed # processes and zombie processes which is very much a Linux # specific thing (hence the override). unix_process = UnixProcess(pid=cmd.pid) assert unix_process.is_running, "UnixProcess.is_running is broken!" # We don't use Executor to control the process, instead we take the # process ID and use it to create a Process object that doesn't # know about Python's subprocess module. linux_process = Process.from_pid(cmd.pid) # We terminate the process using a positive but very low timeout so # that all of the code involved gets a chance to run, but without # significantly slowing down the test suite. getattr(linux_process, method)(timeout=0.1) # Now we can verify our assertions. assert not linux_process.is_running, "Child still running despite graceful termination request!" assert timer.elapsed_time < 10, "It look too long to terminate the child!" # Now comes a hairy bit of Linux implementation details that most # people can thankfully ignore (blissful ignorance :-). Parent # processes are responsible for reclaiming child processes and # until this happens the /proc/[pid] entry remains, which means # the `kill -0' trick used by UnixProcess to detect running # processes doesn't work as expected. Basically this means we # _must_ make sure that waitpid() is called before we can expect # UnixProcess.is_running to behave as expected. cmd.wait() # Now that we've called waitpid() things should work as expected. assert not unix_process.is_running, "UnixProcess.is_running is broken!"
def test_environ(self): """Test that parsing of process environments works as expected.""" unique_value = str(random.random()) with ExternalCommand( 'sleep', '30', environment=dict(unique_value=unique_value)) as sleep_cmd: sleep_proc = Process.from_pid(sleep_cmd.pid) assert sleep_proc.environ['unique_value'] == unique_value
def process(self): """ The :class:`proc.core.Process` object for this worker process (or :data:`None`). If :attr:`pid` is set then the value of :attr:`process` defaults to the result of :func:`proc.core.Process.from_pid()`. If the worker process disappears before the process information is requested :attr:`process` will be :data:`None`. """ return Process.from_pid(self.pid) if self.pid else None
def test_wait_for_processes(self): """Test the :func:`proc.cron.wait_for_processes()` function.""" children = [ subprocess.Popen(['sleep', str(int(5 + random.random() * 5))]) for i in range(5) ] wait_for_processes([Process.from_pid(c.pid) for c in children]) assert sum(c.poll() is None for c in children) == 0, \ "wait_for_processes() returned before all processes ended!"
def test_is_alive(self): """Test the :func:`proc.core.Process.is_alive` property.""" # Spawn a child that will live for a minute. with ExternalCommand('sleep', '60', check=False) as child: # Construct a process object for the child. process = Process.from_pid(child.pid) # Make sure the process object agrees the child is alive. assert process.is_alive, "Child died before Process.is_alive was called?!" # Kill the child process and give it a moment to terminate # (significantly less time then the process is normally expected to # run, otherwise there's no point in the test below). child.terminate(timeout=10) # Make sure the process object agrees the child is dead. assert not process.is_alive, "Child is still alive even though we killed it?!"
def verify_app(self, s): creds = s.getsockopt( SOL_SOCKET, self.SO_PEERCRED, struct.calcsize('3i')) pid, uid, gid = struct.unpack('3i', creds) proc = Process.from_pid(pid) file_hash = filehash.file_sha1(proc.exe) connected_app = SingleApp(proc.exe, file_hash) if (self.client_apps and connected_app in self.client_apps) \ or self.extra_approver(connected_app): return True, connected_app return False, None
def test_process_from_path(self): """Test the :func:`proc.core.Process.from_path()` constructor.""" process = Process.from_path('/proc/self') # The following tests verify properties based on information available # from the Python standard library. assert process.pid == os.getpid(), "Unexpected process ID!" assert process.ppid == os.getppid(), "Unexpected parent process ID!" assert process.pgrp == os.getpgrp(), "Unexpected process group ID!" assert process.user_ids.real == os.getuid(), "Unexpected real user ID!" assert process.group_ids.real == os.getgid( ), "Unexpected real group ID!" assert process.cwd == os.getcwd(), "Unexpected working directory!" # The following assertion may fail at some point, but I chose it above # using uid_to_name(os.getuid()) because the latter isn't testing # anything useful at all ... assert process.user == getpass.getuser( ), "Unexpected username based on real user ID!" # We really can't make much assumptions about the name of the real # group ID so we'll just check whether it's available. assert process.group, "Expected process to have real group ID with group name!" # The following tests are based on common sense, here's hoping they # don't bite me in the ass later on :-). assert process.state == 'R', "Unexpected process state!" assert process.runtime < 600, "Test process running for >= 10 minutes?!" assert process.rss > parse_size( '10 MB'), "Resident set size (RSS) less than 10 MB?!" assert process.vsize > process.rss, "Virtual memory usage less than its resident set size (RSS)?!" assert executable(process.cmdline[0]) or which(process.cmdline[0]), \ "First token in process command line isn't executable?!" assert executable( process.exe ), "Process executable pathname (based on /proc/[pid]/stat) invalid?!" assert executable( process.exe_path ), "Process executable pathname (fall back option) invalid?!" if not (os.environ.get('TRAVIS') and process.exe_name == 'pypy'): # https://travis-ci.org/xolox/python-proc/jobs/395176218 assert which( process.exe_name ), "Process executable base name (fall back option) not available on $PATH?!" assert process.is_alive, "The current process is not running?! :-P" # Python's standard library doesn't seem to expose process session IDs # so all I can test reliably is that the session ID is an integer... assert isinstance(process.session, int), "Process session ID not available!"
def test_suspend_and_resume_signals(self): """Test the sending of ``SIGSTOP``, ``SIGCONT`` and ``SIGTERM`` signals.""" # Spawn a child that will live for a minute. with ExternalCommand('sleep', '60', check=False) as cmd: process = Process.from_pid(cmd.pid) # Suspend the execution of the child process using SIGSTOP. process.suspend() # Test that the child process doesn't respond to SIGTERM once suspended. process.terminate(wait=False) assert process.is_running, "Child responded to signal even though it was suspended?!" # Resume the execution of the child process using SIGCONT. process.resume() # Test that the child process responds to signals again after # having been resumed, but give it a moment to terminate # (significantly less time then the process is normally expected # to run, otherwise there's no point in the test below). process.kill(wait=True, timeout=5) assert not process.is_running, "Child didn't respond to signal even though it was resumed?!"
def process(self): return Process.from_pid(self.pid) if self.pid else None