mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-20 04:13:07 +02:00
Unittest - Stream blocking tests can now be safely performed
* Processes that blocked waiting for stdin data will now be aborted after a 1 second timeout. * As a side-effect any process that takes longer than 1 second to finish will also be aborted.
This commit is contained in:
parent
7f9148efb4
commit
e3d0d2ff34
1 changed files with 69 additions and 10 deletions
|
@ -1,11 +1,77 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import socket
|
import socket
|
||||||
|
import signal
|
||||||
from subprocess import Popen, PIPE, STDOUT
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
from threading import Thread
|
||||||
|
from Queue import Queue, Empty
|
||||||
|
from time import sleep
|
||||||
from .exceptions import CommandError
|
from .exceptions import CommandError
|
||||||
|
|
||||||
USED_PORTS = set()
|
USED_PORTS = set()
|
||||||
|
ON_POSIX = 'posix' in sys.builtin_module_names
|
||||||
|
|
||||||
|
|
||||||
|
def wait_process(proc, timeout=1):
|
||||||
|
"""Wait for process to finish
|
||||||
|
"""
|
||||||
|
sleeptime = .1
|
||||||
|
# Max number of attempts until giving up
|
||||||
|
tries = int(timeout / sleeptime)
|
||||||
|
|
||||||
|
# Wait for up to a second for the process to finish and avoid zombies
|
||||||
|
for i in range(tries):
|
||||||
|
exit = proc.poll()
|
||||||
|
|
||||||
|
if exit is not None:
|
||||||
|
break
|
||||||
|
|
||||||
|
sleep(sleeptime)
|
||||||
|
|
||||||
|
return exit
|
||||||
|
|
||||||
|
|
||||||
|
def _get_output(proc, input):
|
||||||
|
"""Collect output from the subprocess without blocking the main process if
|
||||||
|
subprocess hangs.
|
||||||
|
"""
|
||||||
|
def queue_output(proc, input, outq, errq):
|
||||||
|
"""Read/Write output/input of given process.
|
||||||
|
This function is meant to be executed in a thread as it may block
|
||||||
|
"""
|
||||||
|
# Send input and wait for finish
|
||||||
|
out, err = proc.communicate(input)
|
||||||
|
# Give the output back to the caller
|
||||||
|
outq.put(out)
|
||||||
|
errq.put(err)
|
||||||
|
|
||||||
|
outq = Queue()
|
||||||
|
errq = Queue()
|
||||||
|
|
||||||
|
t = Thread(target=queue_output, args=(proc, input, outq, errq))
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
# A task process shouldn't take longer than 1 second to finish
|
||||||
|
exit = wait_process(proc)
|
||||||
|
|
||||||
|
# If it does take longer than 1 second, abort it
|
||||||
|
if exit is None:
|
||||||
|
proc.send_signal(signal.SIGABRT)
|
||||||
|
exit = wait_process(proc)
|
||||||
|
|
||||||
|
try:
|
||||||
|
out = outq.get_nowait()
|
||||||
|
except Empty:
|
||||||
|
out = None
|
||||||
|
try:
|
||||||
|
err = errq.get_nowait()
|
||||||
|
except Empty:
|
||||||
|
err = None
|
||||||
|
|
||||||
|
return out, err
|
||||||
|
|
||||||
|
|
||||||
def run_cmd_wait(cmd, input=None, stdout=PIPE, stderr=PIPE,
|
def run_cmd_wait(cmd, input=None, stdout=PIPE, stderr=PIPE,
|
||||||
|
@ -22,16 +88,9 @@ def run_cmd_wait(cmd, input=None, stdout=PIPE, stderr=PIPE,
|
||||||
else:
|
else:
|
||||||
stderr = PIPE
|
stderr = PIPE
|
||||||
|
|
||||||
p = Popen(cmd, stdin=stdin, stdout=stdout, stderr=stderr, env=env)
|
p = Popen(cmd, stdin=stdin, stdout=stdout, stderr=stderr, bufsize=1,
|
||||||
out, err = p.communicate(input)
|
close_fds=ON_POSIX, env=env)
|
||||||
|
out, err = _get_output(p, input)
|
||||||
# In python3 we will be able use the following instead of the previous
|
|
||||||
# line to avoid locking if task is unexpectedly waiting for input
|
|
||||||
# try:
|
|
||||||
# out, err = p.communicate(input, timeout=15)
|
|
||||||
# except TimeoutExpired:
|
|
||||||
# p.kill()
|
|
||||||
# out, err = proc.communicate()
|
|
||||||
|
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
raise CommandError(cmd, p.returncode, out, err)
|
raise CommandError(cmd, p.returncode, out, err)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue