1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Tests for L{twisted.python.sendmsg}.
11 from socket import SOL_SOCKET, AF_INET, AF_INET6, socket, error
14 from socket import AF_UNIX, socketpair
16 nonUNIXSkip = "Platform does not support AF_UNIX sockets"
20 from struct import pack
21 from os import devnull, pipe, read, close, environ
23 from twisted.internet.defer import Deferred
24 from twisted.internet.error import ProcessDone
25 from twisted.trial.unittest import TestCase
26 from twisted.internet.defer import inlineCallbacks
27 from twisted.internet import reactor
28 from twisted.python.filepath import FilePath
29 from twisted.python.runtime import platform
31 from twisted.internet.protocol import ProcessProtocol
33 if platform.isLinux():
34 from socket import MSG_DONTWAIT
37 # It would be nice to be able to test flags on more platforms, but finding a
38 # flag that works *at all* is somewhat challenging.
39 dontWaitSkip = "MSG_DONTWAIT is only known to work as intended on Linux"
42 from twisted.python.sendmsg import SCM_RIGHTS, send1msg, recv1msg, getsockfam
44 importSkip = "Cannot import twisted.python.sendmsg"
49 class ExitedWithStderr(Exception):
51 A process exited with some stderr.
56 Dump the errors in a pretty way in the event of a subprocess traceback.
58 return '\n'.join([''] + list(self.args))
61 class StartStopProcessProtocol(ProcessProtocol):
63 An L{IProcessProtocol} with a Deferred for events where the subprocess
66 @ivar started: A L{Deferred} which fires with this protocol's
67 L{IProcessTransport} provider when it is connected to one.
69 @ivar stopped: A L{Deferred} which fires with the process output or a
70 failure if the process produces output on standard error.
72 @ivar output: A C{str} used to accumulate standard output.
74 @ivar errors: A C{str} used to accumulate standard error.
77 self.started = Deferred()
78 self.stopped = Deferred()
83 def connectionMade(self):
84 self.started.callback(self.transport)
87 def outReceived(self, data):
91 def errReceived(self, data):
95 def processEnded(self, reason):
96 if reason.check(ProcessDone):
97 self.stopped.callback(self.output)
99 self.stopped.errback(ExitedWithStderr(
100 self.errors, self.output))
106 A list which cannot be iterated sometimes.
108 This is a C{list} subclass to get past the type check in L{send1msg}, not as
109 an example of how real programs might want to interact with L{send1msg} (or
110 anything else). A custom C{list} subclass makes it easier to trigger
111 certain error cases in the implementation.
113 @ivar iterate: A flag which indicates whether an instance of L{BadList} will
114 allow iteration over itself or not. If C{False}, an attempt to iterate
115 over the instance will raise an exception.
121 Allow normal list iteration, or raise an exception.
123 If C{self.iterate} is C{True}, it will be flipped to C{False} and then
124 normal iteration will proceed. If C{self.iterate} is C{False},
125 L{RuntimeError} is raised instead.
129 return super(BadList, self).__iter__()
130 raise RuntimeError("Something bad happened")
134 class WorseList(list):
136 A list which at first gives the appearance of being iterable, but then
139 See L{BadList} for a warning about not writing code like this.
143 Return an iterator which will raise an exception as soon as C{next} is
146 class BadIterator(object):
148 raise RuntimeError("This is a really bad case.")
153 class SendmsgTestCase(TestCase):
155 Tests for sendmsg extension module and associated file-descriptor sending
158 if nonUNIXSkip is not None:
160 elif importSkip is not None:
165 Create a pair of UNIX sockets.
167 self.input, self.output = socketpair(AF_UNIX)
172 Close the sockets opened by setUp.
178 def test_sendmsgBadArguments(self):
180 The argument types accepted by L{send1msg} are:
183 2. read-only character buffer
187 The 3rd and 4th arguments are optional. If fewer than two arguments or
188 more than four arguments are passed, or if any of the arguments passed
189 are not compatible with these types, L{TypeError} is raised.
191 # Exercise the wrong number of arguments cases
192 self.assertRaises(TypeError, send1msg)
193 self.assertRaises(TypeError, send1msg, 1)
194 self.assertRaises(TypeError, send1msg, 1, "hello world", 2, [], object())
196 # Exercise the wrong type of arguments cases
197 self.assertRaises(TypeError, send1msg, object(), "hello world", 2, [])
198 self.assertRaises(TypeError, send1msg, 1, object(), 2, [])
199 self.assertRaises(TypeError, send1msg, 1, "hello world", object(), [])
200 self.assertRaises(TypeError, send1msg, 1, "hello world", 2, object())
203 def test_badAncillaryIter(self):
205 If iteration over the ancillary data list fails (at the point of the
206 C{__iter__} call), the exception with which it fails is propagated to
207 the caller of L{send1msg}.
210 badList.append((1, 2, "hello world"))
211 badList.iterate = False
213 self.assertRaises(RuntimeError, send1msg, 1, "hello world", 2, badList)
215 # Hit the second iteration
216 badList.iterate = True
217 self.assertRaises(RuntimeError, send1msg, 1, "hello world", 2, badList)
220 def test_badAncillaryNext(self):
222 If iteration over the ancillary data list fails (at the point of a
223 C{next} call), the exception with which it fails is propagated to the
224 caller of L{send1msg}.
226 worseList = WorseList()
227 self.assertRaises(RuntimeError, send1msg, 1, "hello world", 2, worseList)
230 def test_sendmsgBadAncillaryItem(self):
232 The ancillary data list contains three-tuples with element types of:
236 3. read-only character buffer
238 If a tuple in the ancillary data list does not elements of these types,
239 L{TypeError} is raised.
241 # Exercise the wrong number of arguments cases
242 self.assertRaises(TypeError, send1msg, 1, "hello world", 2, [()])
243 self.assertRaises(TypeError, send1msg, 1, "hello world", 2, [(1,)])
244 self.assertRaises(TypeError, send1msg, 1, "hello world", 2, [(1, 2)])
247 send1msg, 1, "hello world", 2, [(1, 2, "goodbye", object())])
249 # Exercise the wrong type of arguments cases
250 exc = self.assertRaises(
251 TypeError, send1msg, 1, "hello world", 2, [object()])
253 "send1msg argument 3 expected list of tuple, "
254 "got list containing object",
258 send1msg, 1, "hello world", 2, [(object(), 1, "goodbye")])
261 send1msg, 1, "hello world", 2, [(1, object(), "goodbye")])
264 send1msg, 1, "hello world", 2, [(1, 1, object())])
267 def test_syscallError(self):
269 If the underlying C{sendmsg} call fails, L{send1msg} raises
270 L{socket.error} with its errno set to the underlying errno value.
272 probe = file(devnull)
275 exc = self.assertRaises(error, send1msg, fd, "hello, world")
276 self.assertEqual(exc.args[0], errno.EBADF)
279 def test_syscallErrorWithControlMessage(self):
281 The behavior when the underlying C{sendmsg} call fails is the same
282 whether L{send1msg} is passed ancillary data or not.
284 probe = file(devnull)
287 exc = self.assertRaises(
288 error, send1msg, fd, "hello, world", 0, [(0, 0, "0123")])
289 self.assertEqual(exc.args[0], errno.EBADF)
292 def test_roundtrip(self):
294 L{recv1msg} will retrieve a message sent via L{send1msg}.
296 message = "hello, world!"
299 send1msg(self.input.fileno(), message, 0))
301 result = recv1msg(fd=self.output.fileno())
302 self.assertEquals(result, (message, 0, []))
305 def test_shortsend(self):
307 L{send1msg} returns the number of bytes which it was able to send.
309 message = "x" * 1024 * 1024
310 self.input.setblocking(False)
311 sent = send1msg(self.input.fileno(), message)
312 # Sanity check - make sure we did fill the send buffer and then some
313 self.assertTrue(sent < len(message))
314 received = recv1msg(self.output.fileno(), 0, len(message))
315 self.assertEqual(len(received[0]), sent)
318 def test_roundtripEmptyAncillary(self):
320 L{send1msg} treats an empty ancillary data list the same way it treats
321 receiving no argument for the ancillary parameter at all.
323 send1msg(self.input.fileno(), "hello, world!", 0, [])
325 result = recv1msg(fd=self.output.fileno())
326 self.assertEquals(result, ("hello, world!", 0, []))
329 def test_flags(self):
331 The C{flags} argument to L{send1msg} is passed on to the underlying
332 C{sendmsg} call, to affect it in whatever way is defined by those flags.
334 # Just exercise one flag with simple, well-known behavior. MSG_DONTWAIT
335 # makes the send a non-blocking call, even if the socket is in blocking
336 # mode. See also test_flags in RecvmsgTestCase
337 for i in range(1024):
339 send1msg(self.input.fileno(), "x" * 1024, MSG_DONTWAIT)
341 self.assertEqual(e.args[0], errno.EAGAIN)
345 "Failed to fill up the send buffer, "
346 "or maybe send1msg blocked for a while")
347 if dontWaitSkip is not None:
348 test_flags.skip = dontWaitSkip
351 def test_wrongTypeAncillary(self):
353 L{send1msg} will show a helpful exception message when given the wrong
354 type of object for the 'ancillary' argument.
356 error = self.assertRaises(TypeError,
357 send1msg, self.input.fileno(),
358 "hello, world!", 0, 4321)
359 self.assertEquals(str(error),
360 "send1msg argument 3 expected list, got int")
363 def spawn(self, script):
365 Start a script that is a peer of this test as a subprocess.
367 @param script: the module name of the script in this directory (no
368 package prefix, no '.py')
371 @rtype: L{StartStopProcessProtocol}
373 sspp = StartStopProcessProtocol()
374 reactor.spawnProcess(
375 sspp, sys.executable, [
377 FilePath(__file__).sibling(script + ".py").path,
378 str(self.output.fileno()),
381 childFDs={0: "w", 1: "r", 2: "r",
382 self.output.fileno(): self.output.fileno()}
388 def test_sendSubProcessFD(self):
390 Calling L{sendsmsg} with SOL_SOCKET, SCM_RIGHTS, and a platform-endian
391 packed file descriptor number should send that file descriptor to a
392 different process, where it can be retrieved by using L{recv1msg}.
394 sspp = self.spawn("pullpipe")
396 pipeOut, pipeIn = pipe()
397 self.addCleanup(close, pipeOut)
400 self.input.fileno(), "blonk", 0,
401 [(SOL_SOCKET, SCM_RIGHTS, pack("i", pipeIn))])
405 self.assertEquals(read(pipeOut, 1024), "Test fixture data: blonk.\n")
406 # Make sure that the pipe is actually closed now.
407 self.assertEquals(read(pipeOut, 1024), "")
411 class RecvmsgTestCase(TestCase):
413 Tests for L{recv1msg} (primarily error handling cases).
415 if importSkip is not None:
418 def test_badArguments(self):
420 The argument types accepted by L{recv1msg} are:
427 The 2nd, 3rd, and 4th arguments are optional. If fewer than one
428 argument or more than four arguments are passed, or if any of the
429 arguments passed are not compatible with these types, L{TypeError} is
432 # Exercise the wrong number of arguments cases
433 self.assertRaises(TypeError, recv1msg)
434 self.assertRaises(TypeError, recv1msg, 1, 2, 3, 4, object())
436 # Exercise the wrong type of arguments cases
437 self.assertRaises(TypeError, recv1msg, object(), 2, 3, 4)
438 self.assertRaises(TypeError, recv1msg, 1, object(), 3, 4)
439 self.assertRaises(TypeError, recv1msg, 1, 2, object(), 4)
440 self.assertRaises(TypeError, recv1msg, 1, 2, 3, object())
443 def test_cmsgSpaceOverflow(self):
445 L{recv1msg} raises L{OverflowError} if passed a value for the
446 C{cmsg_size} argument which exceeds C{SOCKLEN_MAX}.
448 self.assertRaises(OverflowError, recv1msg, 0, 0, 0, 0x7FFFFFFF)
451 def test_syscallError(self):
453 If the underlying C{recvmsg} call fails, L{recv1msg} raises
454 L{socket.error} with its errno set to the underlying errno value.
456 probe = file(devnull)
459 exc = self.assertRaises(error, recv1msg, fd)
460 self.assertEqual(exc.args[0], errno.EBADF)
463 def test_flags(self):
465 The C{flags} argument to L{recv1msg} is passed on to the underlying
466 C{recvmsg} call, to affect it in whatever way is defined by those flags.
468 # See test_flags in SendmsgTestCase
469 reader, writer = socketpair(AF_UNIX)
470 exc = self.assertRaises(
471 error, recv1msg, reader.fileno(), MSG_DONTWAIT)
472 self.assertEqual(exc.args[0], errno.EAGAIN)
473 if dontWaitSkip is not None:
474 test_flags.skip = dontWaitSkip
478 class GetSocketFamilyTests(TestCase):
480 Tests for L{getsockfam}, a helper which reveals the address family of an
483 if importSkip is not None:
486 def _socket(self, addressFamily):
488 Create a new socket using the given address family and return that
489 socket's file descriptor. The socket will automatically be closed when
490 the test is torn down.
492 s = socket(addressFamily)
493 self.addCleanup(s.close)
497 def test_badArguments(self):
499 L{getsockfam} accepts a single C{int} argument. If it is called in some
500 other way, L{TypeError} is raised.
502 self.assertRaises(TypeError, getsockfam)
503 self.assertRaises(TypeError, getsockfam, 1, 2)
505 self.assertRaises(TypeError, getsockfam, object())
508 def test_syscallError(self):
510 If the underlying C{getsockname} call fails, L{getsockfam} raises
511 L{socket.error} with its errno set to the underlying errno value.
513 probe = file(devnull)
516 exc = self.assertRaises(error, getsockfam, fd)
517 self.assertEqual(errno.EBADF, exc.args[0])
522 When passed the file descriptor of a socket created with the C{AF_INET}
523 address family, L{getsockfam} returns C{AF_INET}.
525 self.assertEqual(AF_INET, getsockfam(self._socket(AF_INET)))
528 def test_inet6(self):
530 When passed the file descriptor of a socket created with the C{AF_INET6}
531 address family, L{getsockfam} returns C{AF_INET6}.
533 self.assertEqual(AF_INET6, getsockfam(self._socket(AF_INET6)))
538 When passed the file descriptor of a socket created with the C{AF_UNIX}
539 address family, L{getsockfam} returns C{AF_UNIX}.
541 self.assertEqual(AF_UNIX, getsockfam(self._socket(AF_UNIX)))
542 if nonUNIXSkip is not None:
543 test_unix.skip = nonUNIXSkip