def test_forwarding_from_unreadable_fd(self): loop = self.make_event_loop() pipe_read, pipe_write = stream.make_pipe() forwarder = stream.FDForwarder(loop, open("/dev/null", "w"), pipe_write) del pipe_write loop.once_safely() self.assertEquals(os.read(pipe_read.fileno(), 1000), "")
def test_fds_are_freed(self): pipe_read, pipe_write = stream.make_pipe() fd = pipe_read.fileno() fcntl.fcntl(fd, fcntl.F_GETFL) del pipe_read self.assertRaises(IOError, lambda: fcntl.fcntl(fd, fcntl.F_GETFL))
def test_forwarding_to_unwritable_fd(self): loop = self.make_event_loop() pipe_read, pipe_write = stream.make_pipe() forwarder = stream.FDForwarder(loop, pipe_read, open("/dev/null", "r")) del pipe_read loop.once_safely() self.assertEquals(poll_fd(pipe_write), select.POLLERR | select.POLLOUT)
def find_pipe_buffer_size(): # The size of a pipe's buffer is 64k on Linux, but let's not # assume that. pipe_read, pipe_write = stream.make_pipe() fcntl.fcntl(pipe_write.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) data = "x" * 4096 written = 0 while poll_fd(pipe_write) & select.POLLOUT != 0: written += os.write(pipe_write.fileno(), data) return written
def test_writing_to_closed_pipe(self): loop = self.make_event_loop() pipe_read, pipe_write = stream.make_pipe() writer = stream.FDBufferedWriter(loop, pipe_write) writer.write("hello") del pipe_read self.assertEquals(writer.is_finished_writing(), False) loop.once_safely() self.assertEquals(writer.is_finished_writing(), True) self.assertFalse(loop.is_listening())
def test_writing(self): loop = self.make_event_loop() pipe_read, pipe_write = stream.make_pipe() writer = stream.FDBufferedWriter(loop, pipe_write) self.assertEquals(writer.buffered_size(), 0) writer.write("hello") self.assertEquals(writer.buffered_size(), 5) loop.once_safely() # Should have written all of buffer to the pipe now self.assertEquals(writer.buffered_size(), 0)
def test_writing_end_of_stream_with_data_buffered(self): loop = self.make_event_loop() pipe_read, pipe_write = stream.make_pipe() writer = stream.FDBufferedWriter(loop, pipe_write) del pipe_write writer.write("hello") writer.end_of_stream() loop.once_safely() self.assertEquals(poll_fd(pipe_read), select.POLLHUP | select.POLLIN) self.assertEquals(os.read(pipe_read.fileno(), 100), "hello") self.assertEquals(poll_fd(pipe_read), select.POLLHUP) self.assertEquals(os.read(pipe_read.fileno(), 100), "")
def test_fd_flags_not_changed(self): loop = self.make_event_loop() pipe_read, pipe_write = stream.make_pipe() def check_flags(): flags = fcntl.fcntl(pipe_write.fileno(), fcntl.F_GETFL) self.assertEquals(flags, os.O_WRONLY) check_flags() writer = stream.FDBufferedWriter(loop, pipe_write) check_flags() writer.write("hello") loop.once_safely() # If FDBufferedWriter sets O_NONBLOCK, it should do so only # temporarily, because it could confuse other processes that # share the FD. Changing the flags temporarily is still not # ideal though. check_flags()
def test_poll_annoying_behaviour(self): pipe_read, pipe_write = stream.make_pipe() # It would be more useful if poll() returned POLLIN and # POLLOUT for unreadable and unwritable FDs respectively, # indicating that read() and write() would not block. # We do not rely on the behaviour tested here; in fact, we # work around it. self.assertEquals(poll_fd(pipe_read) & select.POLLOUT, 0) self.assertEquals(poll_fd(pipe_write) & select.POLLIN, 0) # However, select() behaves more usefully: read_fds, write_fds, except_fds = select.select( [], [open(os.devnull, "r")], [], 0) self.assertEquals(len(write_fds), 1) read_fds, write_fds, except_fds = select.select( [open(os.devnull, "w")], [], [], 0) self.assertEquals(len(read_fds), 1)
def test_writing_to_full_buffer(self): # Checks that the writer does not write when the FD's buffer # is full, and checks that it does not block. loop = self.make_event_loop() pipe_read, pipe_write = stream.make_pipe() writer = stream.FDBufferedWriter(loop, pipe_write) bufferfuls = 5 size = find_pipe_buffer_size() * bufferfuls writer.write("x" * size) loop.once_safely() # Nothing is reading pipe_read, so writer should still have # unwritten data. assert writer.buffered_size() > 0 assert writer.buffered_size() < size # But reading should allow the backlog to clear. for i in range(bufferfuls): self.assertEquals(poll_fd(pipe_read), select.POLLIN) os.read(pipe_read.fileno(), size) loop.run_awhile() self.assertEquals(writer.buffered_size(), 0)
def test_on_finished_callback(self): got = [] def on_finished(): got.append("finish") loop = self.make_event_loop() pipe_read, pipe_write = stream.make_pipe() writer = stream.FDBufferedWriter(loop, pipe_write, on_unwritable=on_finished) writer.write("hello") loop.once_safely() self.assertEquals(writer.buffered_size(), 0) # Writer is not finished merely because it has no data buffered. self.assertEquals(got, []) writer.write("world") writer.end_of_stream() # Nor is the writer finished because end_of_stream() has been called. self.assertEquals(got, []) loop.once_safely() # Writer is finished once end_of_stream() has been called and # either all buffered data has been written or an error was # reached. self.assertEquals(got, ["finish"])
def get_poll_flag_table(): output = [] pipe_size = find_pipe_buffer_size() def example(name, fd): flags = poll_fd(fd) output.append("%-30s %s" % (decode_poll_flags(flags), name)) class Wrapper(object): def __init__(self, fd): self._fd = fd def fileno(self): return self._fd def unwrap(fd): return os.dup(fd.fileno()) def unwrap_pair((fd1, fd2)): return unwrap(fd1), unwrap(fd2) def pair_pending_data(pair, size): read_fd, write_fd = pair os.write(write_fd.fileno(), "x" * size) return pair def example_pair(name, maker, i): fd_pair = unwrap_pair(maker()) fd = fd_pair[i] example(name, Wrapper(fd)) os.close(fd_pair[1 - i]) example(name + " (other closed)", Wrapper(fd)) os.close(fd_pair[i]) def shutdown(pair, shut_type): # The socket is not necessarily AF_UNIX but the socket module # shouldn't actually use those arguments. sock = socket.fromfd(pair[1].fileno(), socket.AF_UNIX, socket.SOCK_STREAM) sock.shutdown(shut_type) return pair def example_socket(name, maker): example_pair(name, maker, 0) example_pair(name + " (SHUT_RD)", lambda: shutdown(maker(), socket.SHUT_RD), 0) example_pair(name + " (SHUT_WR)", lambda: shutdown(maker(), socket.SHUT_WR), 0) example_pair(name + " (SHUT_RDWR)", lambda: shutdown(maker(), socket.SHUT_RDWR), 0) def make_pty(): fd1, fd2 = os.openpty() return stream.WrappedFD(fd1), stream.WrappedFD(fd2) def make_pty_eof(): fd1, fd2 = make_pty() # Send Ctrl-D, the console EOF character os.write(fd1.fileno(), chr(4)) return fd1, fd2 for side, name in [(0, "read"), (1, "write")]: example_pair( "pipe: %s" % name, lambda: stream.make_pipe(), side) example_pair( "pipe: %s, pending data" % name, lambda: pair_pending_data(stream.make_pipe(), pipe_size), side) example_socket("socket: Unix", stream.socketpair) example_socket("socket: TCP", plash.comms.cap_test.tcp_socketpair) example_pair("tty master", make_pty, 0) example_pair("tty slave", make_pty, 1) example_pair("tty slave (send Ctrl-D)", make_pty_eof, 1) example("/dev/null: write", open("/dev/null", "w")) example("/dev/null: read", open("/dev/null", "r")) example("file: write", open("/tmp/file", "w")) example("file: read", open("/tmp/file", "r")) return output
def _make_forwarded_pipe(self, loop): pipe_read2, pipe_write = stream.make_pipe() pipe_read, pipe_write2 = stream.make_pipe() forwarder = stream.FDForwarder(loop, pipe_read2, pipe_write2) return pipe_read, pipe_write, forwarder
def test_pipes(self): pipe_read, pipe_write = stream.make_pipe() self.assertTrue(stream.fd_is_readable(pipe_read)) self.assertFalse(stream.fd_is_writable(pipe_read)) self.assertFalse(stream.fd_is_readable(pipe_write)) self.assertTrue(stream.fd_is_writable(pipe_write))