Commit da2705aa authored by Martín Ferrari's avatar Martín Ferrari

Some more fixes and tests. Almost complete coverage! (more cannot be done)

parent 1f950e84
...@@ -35,6 +35,7 @@ coverage: all ...@@ -35,6 +35,7 @@ coverage: all
set -e; \ set -e; \
PYTHONPATH="$(BUILDDIR):$$PYTHONPATH" $(COVERAGE) -x $$i; \ PYTHONPATH="$(BUILDDIR):$$PYTHONPATH" $(COVERAGE) -x $$i; \
done done
$(COVERAGE) -c
$(COVERAGE) -r -m `find "$(BUILDDIR)" -name \\*.py -type f` $(COVERAGE) -r -m `find "$(BUILDDIR)" -name \\*.py -type f`
rm -f .coverage rm -f .coverage
......
...@@ -494,7 +494,7 @@ class Client(object): ...@@ -494,7 +494,7 @@ class Client(object):
exitcode = int(text.split()[0]) exitcode = int(text.split()[0])
return exitcode return exitcode
if code / 100 == 4: if code / 100 == 4:
return Null return None
else: else:
raise "Error on command: %d %s" % (code, text) raise "Error on command: %d %s" % (code, text)
......
...@@ -66,22 +66,21 @@ class Subprocess(object): ...@@ -66,22 +66,21 @@ class Subprocess(object):
def poll(self): def poll(self):
"""Checks status of program, returns exitcode or None if still running. """Checks status of program, returns exitcode or None if still running.
See Popen.poll.""" See Popen.poll."""
r = self._slave.poll(self._pid) if self._returncode == None:
if r != None: self._returncode = self._slave.poll(self._pid)
del self._pid
self._returncode = r
return self.returncode return self.returncode
def wait(self): def wait(self):
"""Waits for program to complete and returns the exitcode. """Waits for program to complete and returns the exitcode.
See Popen.wait""" See Popen.wait"""
self._returncode = self._slave.wait(self._pid) if self._returncode == None:
del self._pid self._returncode = self._slave.wait(self._pid)
return self.returncode return self.returncode
def signal(self, sig = signal.SIGTERM): def signal(self, sig = signal.SIGTERM):
"""Sends a signal to the process.""" """Sends a signal to the process."""
return self._slave.signal(self._pid, sig) if self._returncode == None:
self._slave.signal(self._pid, sig)
@property @property
def returncode(self): def returncode(self):
...@@ -95,7 +94,7 @@ class Subprocess(object): ...@@ -95,7 +94,7 @@ class Subprocess(object):
return -os.WTERMSIG(self._returncode) return -os.WTERMSIG(self._returncode)
if os.WIFEXITED(self._returncode): if os.WIFEXITED(self._returncode):
return os.WEXITSTATUS(self._returncode) return os.WEXITSTATUS(self._returncode)
raise RuntimeError("Invalid return code") raise RuntimeError("Invalid return code") # pragma: no cover
# FIXME: do we have any other way to deal with this than having explicit # FIXME: do we have any other way to deal with this than having explicit
# destroy? # destroy?
...@@ -228,11 +227,11 @@ def backticks_raise(node, args): ...@@ -228,11 +227,11 @@ def backticks_raise(node, args):
args = [ '/bin/sh', '/bin/sh', '-c', args ] args = [ '/bin/sh', '/bin/sh', '-c', args ]
p = Popen(node, args[0], args[1:], stdout = PIPE) p = Popen(node, args[0], args[1:], stdout = PIPE)
out = p.communicate()[0] out = p.communicate()[0]
if p.returncode > 0: ret = p.returncode
raise RuntimeError("Command failed with return code %d." % if ret > 0:
p.returncode) raise RuntimeError("Command failed with return code %d." % ret)
if p.returncode < 0: if ret < 0:
raise RuntimeError("Command killed by signal %d." % -p.returncode) raise RuntimeError("Command killed by signal %d." % -ret)
return out return out
# ======================================================================= # =======================================================================
...@@ -261,7 +260,7 @@ def spawn(executable, argv = None, cwd = None, env = None, stdin = None, ...@@ -261,7 +260,7 @@ def spawn(executable, argv = None, cwd = None, env = None, stdin = None,
filtered_userfd = filter(lambda x: x != None and x >= 0, userfd) filtered_userfd = filter(lambda x: x != None and x >= 0, userfd)
for i in range(3): for i in range(3):
if userfd[i] != None and not isinstance(userfd[i], int): if userfd[i] != None and not isinstance(userfd[i], int):
userfd[i] = userfd[i].fileno() userfd[i] = userfd[i].fileno() # pragma: no cover
# Verify there is no clash # Verify there is no clash
assert not (set([0, 1, 2]) & set(filtered_userfd)) assert not (set([0, 1, 2]) & set(filtered_userfd))
...@@ -284,7 +283,8 @@ def spawn(executable, argv = None, cwd = None, env = None, stdin = None, ...@@ -284,7 +283,8 @@ def spawn(executable, argv = None, cwd = None, env = None, stdin = None,
(r, w) = os.pipe() (r, w) = os.pipe()
pid = os.fork() pid = os.fork()
if pid == 0: if pid == 0: # pragma: no cover
# coverage doesn't seem to understand fork
try: try:
# Set up stdio piping # Set up stdio piping
for i in range(3): for i in range(3):
...@@ -298,16 +298,14 @@ def spawn(executable, argv = None, cwd = None, env = None, stdin = None, ...@@ -298,16 +298,14 @@ def spawn(executable, argv = None, cwd = None, env = None, stdin = None,
flags = fcntl.fcntl(w, fcntl.F_GETFD) flags = fcntl.fcntl(w, fcntl.F_GETFD)
fcntl.fcntl(w, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) fcntl.fcntl(w, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
if close_fds == False: if close_fds == True:
pass
elif close_fds == True:
for i in xrange(3, MAXFD): for i in xrange(3, MAXFD):
if i != w: if i != w:
try: try:
os.close(i) os.close(i)
except: except:
pass pass
else: elif close_fds != False:
for i in close_fds: for i in close_fds:
os.close(i) os.close(i)
...@@ -386,7 +384,7 @@ def _eintr_wrapper(f, *args): ...@@ -386,7 +384,7 @@ def _eintr_wrapper(f, *args):
while True: while True:
try: try:
return f(*args) return f(*args)
except OSError, e: except OSError, e: # pragma: no cover
if e.errno == errno.EINTR: if e.errno == errno.EINTR:
continue continue
else: else:
...@@ -394,11 +392,11 @@ def _eintr_wrapper(f, *args): ...@@ -394,11 +392,11 @@ def _eintr_wrapper(f, *args):
try: try:
MAXFD = os.sysconf("SC_OPEN_MAX") MAXFD = os.sysconf("SC_OPEN_MAX")
except: except: # pragma: no cover
MAXFD = 256 MAXFD = 256
# Used to print extra info in nested exceptions # Used to print extra info in nested exceptions
def _custom_hook(t, v, tb): def _custom_hook(t, v, tb): # pragma: no cover
if hasattr(v, "child_traceback"): if hasattr(v, "child_traceback"):
sys.stderr.write("Nested exception, original traceback " + sys.stderr.write("Nested exception, original traceback " +
"(most recent call last):\n") "(most recent call last):\n")
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# vim:ts=4:sw=4:et:ai:sts=4 # vim:ts=4:sw=4:et:ai:sts=4
import netns, netns.subprocess, test_util import netns, netns.subprocess, test_util
import grp, os, pwd, signal, sys, unittest import grp, os, pwd, signal, socket, sys, time, unittest
from netns.subprocess import * from netns.subprocess import *
...@@ -115,10 +115,12 @@ class TestSubprocess(unittest.TestCase): ...@@ -115,10 +115,12 @@ class TestSubprocess(unittest.TestCase):
p = spawn('/bin/cat', stdout = w0, stdin = r1, close_fds = [r0, w1]) p = spawn('/bin/cat', stdout = w0, stdin = r1, close_fds = [r0, w1])
os.close(w0) os.close(w0)
os.close(r1) os.close(r1)
self.assertEquals(poll(p), None)
os.write(w1, "hello world\n") os.write(w1, "hello world\n")
os.close(w1) os.close(w1)
self.assertEquals(_readall(r0), "hello world\n") self.assertEquals(_readall(r0), "hello world\n")
os.close(r0) os.close(r0)
self.assertEquals(wait(p), 0)
def test_Subprocess_basic(self): def test_Subprocess_basic(self):
node = netns.Node(nonetns = True) node = netns.Node(nonetns = True)
...@@ -138,12 +140,29 @@ class TestSubprocess(unittest.TestCase): ...@@ -138,12 +140,29 @@ class TestSubprocess(unittest.TestCase):
# Test that the environment is cleared: sleep should not be found # Test that the environment is cleared: sleep should not be found
self.assertRaises(RuntimeError, Subprocess, node, self.assertRaises(RuntimeError, Subprocess, node,
'sleep', env = {'PATH': ''}) 'sleep', env = {'PATH': ''})
#import pdb; pdb.set_trace()
# Piping
r, w = os.pipe() r, w = os.pipe()
p = Subprocess(node, '/bin/echo', ['echo', 'hello world'], stdout = w) p = Subprocess(node, 'echo', ['echo', 'hello world'], stdout = w)
os.close(w) os.close(w)
self.assertEquals(_readall(r), "hello world\n") self.assertEquals(_readall(r), "hello world\n")
os.close(r) os.close(r)
p.wait()
p = Subprocess(node, 'sleep', ['sleep', '100'])
self.assertTrue(p.pid > 0)
self.assertEquals(p.poll(), None) # not finished
p.signal()
p.signal() # verify no-op (otherwise there will be an exception)
self.assertEquals(p.wait(), -signal.SIGTERM)
self.assertEquals(p.wait(), -signal.SIGTERM) # no-op
self.assertEquals(p.poll(), -signal.SIGTERM) # no-op
p = Subprocess(node, 'sleep', ['sleep', '100'])
os.kill(p.pid, signal.SIGTERM)
time.sleep(0.2)
p.signal() # since it has not been waited for, it should not raise
self.assertEquals(p.wait(), -signal.SIGTERM)
def test_Popen(self): def test_Popen(self):
node = netns.Node(nonetns = True, debug=0) node = netns.Node(nonetns = True, debug=0)
...@@ -151,7 +170,7 @@ class TestSubprocess(unittest.TestCase): ...@@ -151,7 +170,7 @@ class TestSubprocess(unittest.TestCase):
# repeat test with Popen interface # repeat test with Popen interface
r0, w0 = os.pipe() r0, w0 = os.pipe()
r1, w1 = os.pipe() r1, w1 = os.pipe()
p = Popen(node, '/bin/cat', stdout = w0, stdin = r1) p = Popen(node, 'cat', stdout = w0, stdin = r1)
os.close(w0) os.close(w0)
os.close(r1) os.close(r1)
os.write(w1, "hello world\n") os.write(w1, "hello world\n")
...@@ -159,18 +178,32 @@ class TestSubprocess(unittest.TestCase): ...@@ -159,18 +178,32 @@ class TestSubprocess(unittest.TestCase):
self.assertEquals(_readall(r0), "hello world\n") self.assertEquals(_readall(r0), "hello world\n")
os.close(r0) os.close(r0)
# now with a socketpair, not using integers
(s0, s1) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0)
p = Popen(node, 'cat', stdout = s0, stdin = s0)
s0.close()
s1.send("hello world\n")
self.assertEquals(s1.recv(512), "hello world\n")
s1.close()
# pipes # pipes
p = Popen(node, '/bin/cat', stdin = PIPE, stdout = PIPE) p = Popen(node, 'cat', stdin = PIPE, stdout = PIPE)
p.stdin.write("hello world\n") p.stdin.write("hello world\n")
p.stdin.close() p.stdin.close()
self.assertEquals(p.stdout.readlines(), ["hello world\n"]) self.assertEquals(p.stdout.readlines(), ["hello world\n"])
self.assertEquals(p.stderr, None) self.assertEquals(p.stderr, None)
self.assertEquals(p.wait(), 0) self.assertEquals(p.wait(), 0)
p = Popen(node, '/bin/cat', stdin = PIPE, stdout = PIPE) p = Popen(node, 'cat', stdin = PIPE, stdout = PIPE)
self.assertEquals(p.communicate(_longstring), (_longstring, None)) self.assertEquals(p.communicate(_longstring), (_longstring, None))
# p = Popen(node, 'cat', stdin = PIPE, stdout = PIPE)
p.stdin.write(_longstring)
self.assertEquals(p.communicate(), (_longstring, None))
p = Popen(node, 'cat', stdin = PIPE)
self.assertEquals(p.communicate(), (None, None))
p = Popen(node, '/bin/sh', ['sh', '-c', 'cat >&2'], p = Popen(node, '/bin/sh', ['sh', '-c', 'cat >&2'],
stdin = PIPE, stderr = PIPE) stdin = PIPE, stderr = PIPE)
p.stdin.write("hello world\n") p.stdin.write("hello world\n")
...@@ -231,7 +264,9 @@ class TestSubprocess(unittest.TestCase): ...@@ -231,7 +264,9 @@ class TestSubprocess(unittest.TestCase):
self.assertEquals(backticks(node, ["echo", "echo", "hello", "world"]), self.assertEquals(backticks(node, ["echo", "echo", "hello", "world"]),
"hello world\n") "hello world\n")
self.assertEquals(backticks(node, "echo hello world > /dev/null"), "") self.assertEquals(backticks(node, "echo hello world > /dev/null"), "")
self.assertEquals(backticks_raise(node, "true"), "")
self.assertRaises(RuntimeError, backticks_raise, node, "false") self.assertRaises(RuntimeError, backticks_raise, node, "false")
self.assertRaises(RuntimeError, backticks_raise, node, "kill $$")
def test_system(self): def test_system(self):
node = netns.Node(nonetns = True, debug=0) node = netns.Node(nonetns = True, debug=0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment