2016-04-10 22:26:28 +00:00
|
|
|
import asyncio
|
|
|
|
import io
|
|
|
|
import os
|
2018-05-23 16:46:27 +00:00
|
|
|
import socket
|
2016-04-10 22:26:28 +00:00
|
|
|
|
|
|
|
from uvloop import _testbase as tb
|
|
|
|
|
|
|
|
|
|
|
|
# All tests are copied from asyncio (mostly as-is)
|
|
|
|
|
|
|
|
|
|
|
|
class MyReadPipeProto(asyncio.Protocol):
|
|
|
|
done = None
|
|
|
|
|
|
|
|
def __init__(self, loop=None):
|
|
|
|
self.state = ['INITIAL']
|
|
|
|
self.nbytes = 0
|
|
|
|
self.transport = None
|
|
|
|
if loop is not None:
|
|
|
|
self.done = asyncio.Future(loop=loop)
|
|
|
|
|
|
|
|
def connection_made(self, transport):
|
|
|
|
self.transport = transport
|
|
|
|
assert self.state == ['INITIAL'], self.state
|
|
|
|
self.state.append('CONNECTED')
|
|
|
|
|
|
|
|
def data_received(self, data):
|
|
|
|
assert self.state == ['INITIAL', 'CONNECTED'], self.state
|
|
|
|
self.nbytes += len(data)
|
|
|
|
|
|
|
|
def eof_received(self):
|
|
|
|
assert self.state == ['INITIAL', 'CONNECTED'], self.state
|
|
|
|
self.state.append('EOF')
|
|
|
|
|
|
|
|
def connection_lost(self, exc):
|
|
|
|
if 'EOF' not in self.state:
|
|
|
|
self.state.append('EOF') # It is okay if EOF is missed.
|
|
|
|
assert self.state == ['INITIAL', 'CONNECTED', 'EOF'], self.state
|
|
|
|
self.state.append('CLOSED')
|
|
|
|
if self.done:
|
|
|
|
self.done.set_result(None)
|
|
|
|
|
|
|
|
|
|
|
|
class MyWritePipeProto(asyncio.BaseProtocol):
|
|
|
|
done = None
|
2020-05-09 18:12:43 +00:00
|
|
|
paused = False
|
2016-04-10 22:26:28 +00:00
|
|
|
|
|
|
|
def __init__(self, loop=None):
|
|
|
|
self.state = 'INITIAL'
|
|
|
|
self.transport = None
|
|
|
|
if loop is not None:
|
|
|
|
self.done = asyncio.Future(loop=loop)
|
|
|
|
|
|
|
|
def connection_made(self, transport):
|
|
|
|
self.transport = transport
|
|
|
|
assert self.state == 'INITIAL', self.state
|
|
|
|
self.state = 'CONNECTED'
|
|
|
|
|
|
|
|
def connection_lost(self, exc):
|
|
|
|
assert self.state == 'CONNECTED', self.state
|
|
|
|
self.state = 'CLOSED'
|
|
|
|
if self.done:
|
|
|
|
self.done.set_result(None)
|
|
|
|
|
2020-05-09 18:12:43 +00:00
|
|
|
def pause_writing(self):
|
|
|
|
self.paused = True
|
|
|
|
|
|
|
|
def resume_writing(self):
|
|
|
|
self.paused = False
|
|
|
|
|
2016-04-10 22:26:28 +00:00
|
|
|
|
|
|
|
class _BasePipeTest:
|
|
|
|
def test_read_pipe(self):
|
|
|
|
proto = MyReadPipeProto(loop=self.loop)
|
|
|
|
|
|
|
|
rpipe, wpipe = os.pipe()
|
|
|
|
pipeobj = io.open(rpipe, 'rb', 1024)
|
|
|
|
|
2019-10-23 22:09:56 +00:00
|
|
|
async def connect():
|
|
|
|
t, p = await self.loop.connect_read_pipe(
|
2016-04-10 22:26:28 +00:00
|
|
|
lambda: proto, pipeobj)
|
|
|
|
self.assertIs(p, proto)
|
|
|
|
self.assertIs(t, proto.transport)
|
|
|
|
self.assertEqual(['INITIAL', 'CONNECTED'], proto.state)
|
|
|
|
self.assertEqual(0, proto.nbytes)
|
|
|
|
|
|
|
|
self.loop.run_until_complete(connect())
|
|
|
|
|
|
|
|
os.write(wpipe, b'1')
|
2018-05-23 16:46:27 +00:00
|
|
|
tb.run_until(self.loop, lambda: proto.nbytes >= 1)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(1, proto.nbytes)
|
|
|
|
|
|
|
|
os.write(wpipe, b'2345')
|
2018-05-23 16:46:27 +00:00
|
|
|
tb.run_until(self.loop, lambda: proto.nbytes >= 5)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(['INITIAL', 'CONNECTED'], proto.state)
|
|
|
|
self.assertEqual(5, proto.nbytes)
|
|
|
|
|
|
|
|
os.close(wpipe)
|
|
|
|
self.loop.run_until_complete(proto.done)
|
|
|
|
self.assertEqual(
|
|
|
|
['INITIAL', 'CONNECTED', 'EOF', 'CLOSED'], proto.state)
|
|
|
|
# extra info is available
|
|
|
|
self.assertIsNotNone(proto.transport.get_extra_info('pipe'))
|
|
|
|
|
|
|
|
def test_read_pty_output(self):
|
|
|
|
proto = MyReadPipeProto(loop=self.loop)
|
|
|
|
|
|
|
|
master, slave = os.openpty()
|
|
|
|
master_read_obj = io.open(master, 'rb', 0)
|
|
|
|
|
2019-10-23 22:09:56 +00:00
|
|
|
async def connect():
|
|
|
|
t, p = await self.loop.connect_read_pipe(
|
|
|
|
lambda: proto, master_read_obj)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertIs(p, proto)
|
|
|
|
self.assertIs(t, proto.transport)
|
|
|
|
self.assertEqual(['INITIAL', 'CONNECTED'], proto.state)
|
|
|
|
self.assertEqual(0, proto.nbytes)
|
|
|
|
|
|
|
|
self.loop.run_until_complete(connect())
|
|
|
|
|
|
|
|
os.write(slave, b'1')
|
2018-05-23 16:46:27 +00:00
|
|
|
tb.run_until(self.loop, lambda: proto.nbytes)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(1, proto.nbytes)
|
|
|
|
|
|
|
|
os.write(slave, b'2345')
|
2018-05-23 16:46:27 +00:00
|
|
|
tb.run_until(self.loop, lambda: proto.nbytes >= 5)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(['INITIAL', 'CONNECTED'], proto.state)
|
|
|
|
self.assertEqual(5, proto.nbytes)
|
|
|
|
|
2016-04-12 17:12:50 +00:00
|
|
|
# On Linux, transport raises EIO when slave is closed --
|
|
|
|
# ignore it.
|
|
|
|
self.loop.set_exception_handler(lambda loop, ctx: None)
|
2016-04-10 22:26:28 +00:00
|
|
|
os.close(slave)
|
2018-05-22 22:08:54 +00:00
|
|
|
proto.transport.close()
|
2016-04-10 22:26:28 +00:00
|
|
|
self.loop.run_until_complete(proto.done)
|
2016-04-12 17:12:50 +00:00
|
|
|
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(
|
|
|
|
['INITIAL', 'CONNECTED', 'EOF', 'CLOSED'], proto.state)
|
|
|
|
# extra info is available
|
|
|
|
self.assertIsNotNone(proto.transport.get_extra_info('pipe'))
|
|
|
|
|
|
|
|
def test_write_pipe(self):
|
|
|
|
rpipe, wpipe = os.pipe()
|
2016-05-18 22:57:35 +00:00
|
|
|
os.set_blocking(rpipe, False)
|
2016-04-10 22:26:28 +00:00
|
|
|
pipeobj = io.open(wpipe, 'wb', 1024)
|
|
|
|
|
|
|
|
proto = MyWritePipeProto(loop=self.loop)
|
|
|
|
connect = self.loop.connect_write_pipe(lambda: proto, pipeobj)
|
|
|
|
transport, p = self.loop.run_until_complete(connect)
|
|
|
|
self.assertIs(p, proto)
|
|
|
|
self.assertIs(transport, proto.transport)
|
|
|
|
self.assertEqual('CONNECTED', proto.state)
|
|
|
|
|
|
|
|
transport.write(b'1')
|
|
|
|
|
|
|
|
data = bytearray()
|
2016-05-07 15:05:33 +00:00
|
|
|
|
2016-04-10 22:26:28 +00:00
|
|
|
def reader(data):
|
2016-05-18 22:57:35 +00:00
|
|
|
try:
|
|
|
|
chunk = os.read(rpipe, 1024)
|
|
|
|
except BlockingIOError:
|
|
|
|
return len(data)
|
2016-04-10 22:26:28 +00:00
|
|
|
data += chunk
|
|
|
|
return len(data)
|
|
|
|
|
2018-05-23 16:46:27 +00:00
|
|
|
tb.run_until(self.loop, lambda: reader(data) >= 1)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(b'1', data)
|
|
|
|
|
|
|
|
transport.write(b'2345')
|
2018-05-23 16:46:27 +00:00
|
|
|
tb.run_until(self.loop, lambda: reader(data) >= 5)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(b'12345', data)
|
|
|
|
self.assertEqual('CONNECTED', proto.state)
|
|
|
|
|
|
|
|
os.close(rpipe)
|
|
|
|
|
|
|
|
# extra info is available
|
|
|
|
self.assertIsNotNone(proto.transport.get_extra_info('pipe'))
|
|
|
|
|
|
|
|
# close connection
|
|
|
|
proto.transport.close()
|
|
|
|
self.loop.run_until_complete(proto.done)
|
|
|
|
self.assertEqual('CLOSED', proto.state)
|
|
|
|
|
|
|
|
def test_write_pipe_disconnect_on_close(self):
|
2018-05-23 16:46:27 +00:00
|
|
|
rsock, wsock = socket.socketpair()
|
2016-04-10 22:26:28 +00:00
|
|
|
rsock.setblocking(False)
|
2016-05-18 22:57:35 +00:00
|
|
|
|
2016-04-10 22:26:28 +00:00
|
|
|
pipeobj = io.open(wsock.detach(), 'wb', 1024)
|
|
|
|
|
|
|
|
proto = MyWritePipeProto(loop=self.loop)
|
|
|
|
connect = self.loop.connect_write_pipe(lambda: proto, pipeobj)
|
|
|
|
transport, p = self.loop.run_until_complete(connect)
|
|
|
|
self.assertIs(p, proto)
|
|
|
|
self.assertIs(transport, proto.transport)
|
|
|
|
self.assertEqual('CONNECTED', proto.state)
|
|
|
|
|
|
|
|
transport.write(b'1')
|
|
|
|
data = self.loop.run_until_complete(self.loop.sock_recv(rsock, 1024))
|
|
|
|
self.assertEqual(b'1', data)
|
|
|
|
|
|
|
|
rsock.close()
|
|
|
|
|
|
|
|
self.loop.run_until_complete(proto.done)
|
|
|
|
self.assertEqual('CLOSED', proto.state)
|
|
|
|
|
|
|
|
def test_write_pty(self):
|
|
|
|
master, slave = os.openpty()
|
2016-05-18 22:57:35 +00:00
|
|
|
os.set_blocking(master, False)
|
|
|
|
|
2016-04-10 22:26:28 +00:00
|
|
|
slave_write_obj = io.open(slave, 'wb', 0)
|
|
|
|
|
|
|
|
proto = MyWritePipeProto(loop=self.loop)
|
|
|
|
connect = self.loop.connect_write_pipe(lambda: proto, slave_write_obj)
|
|
|
|
transport, p = self.loop.run_until_complete(connect)
|
|
|
|
self.assertIs(p, proto)
|
|
|
|
self.assertIs(transport, proto.transport)
|
|
|
|
self.assertEqual('CONNECTED', proto.state)
|
|
|
|
|
|
|
|
transport.write(b'1')
|
|
|
|
|
|
|
|
data = bytearray()
|
2016-05-07 15:05:33 +00:00
|
|
|
|
2016-04-10 22:26:28 +00:00
|
|
|
def reader(data):
|
2016-05-18 22:57:35 +00:00
|
|
|
try:
|
|
|
|
chunk = os.read(master, 1024)
|
|
|
|
except BlockingIOError:
|
|
|
|
return len(data)
|
2016-04-10 22:26:28 +00:00
|
|
|
data += chunk
|
|
|
|
return len(data)
|
|
|
|
|
2018-05-23 16:46:27 +00:00
|
|
|
tb.run_until(self.loop, lambda: reader(data) >= 1,
|
2019-02-15 22:03:58 +00:00
|
|
|
timeout=10)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(b'1', data)
|
|
|
|
|
|
|
|
transport.write(b'2345')
|
2018-05-23 16:46:27 +00:00
|
|
|
tb.run_until(self.loop, lambda: reader(data) >= 5,
|
2019-02-15 22:03:58 +00:00
|
|
|
timeout=10)
|
2016-04-10 22:26:28 +00:00
|
|
|
self.assertEqual(b'12345', data)
|
|
|
|
self.assertEqual('CONNECTED', proto.state)
|
|
|
|
|
|
|
|
os.close(master)
|
|
|
|
|
|
|
|
# extra info is available
|
|
|
|
self.assertIsNotNone(proto.transport.get_extra_info('pipe'))
|
|
|
|
|
|
|
|
# close connection
|
|
|
|
proto.transport.close()
|
|
|
|
self.loop.run_until_complete(proto.done)
|
|
|
|
self.assertEqual('CLOSED', proto.state)
|
|
|
|
|
2020-05-09 18:12:43 +00:00
|
|
|
def test_write_buffer_full(self):
|
|
|
|
rpipe, wpipe = os.pipe()
|
|
|
|
pipeobj = io.open(wpipe, 'wb', 1024)
|
|
|
|
|
|
|
|
proto = MyWritePipeProto(loop=self.loop)
|
|
|
|
connect = self.loop.connect_write_pipe(lambda: proto, pipeobj)
|
|
|
|
transport, p = self.loop.run_until_complete(connect)
|
|
|
|
self.assertIs(p, proto)
|
|
|
|
self.assertIs(transport, proto.transport)
|
|
|
|
self.assertEqual('CONNECTED', proto.state)
|
|
|
|
|
|
|
|
for i in range(32):
|
|
|
|
transport.write(b'x' * 32768)
|
|
|
|
if proto.paused:
|
|
|
|
transport.write(b'x' * 32768)
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
self.fail("Didn't reach a full buffer")
|
|
|
|
|
|
|
|
os.close(rpipe)
|
|
|
|
self.loop.run_until_complete(asyncio.wait_for(proto.done, 1))
|
|
|
|
self.assertEqual('CLOSED', proto.state)
|
|
|
|
|
2016-04-10 22:26:28 +00:00
|
|
|
|
|
|
|
class Test_UV_Pipes(_BasePipeTest, tb.UVTestCase):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Test_AIO_Pipes(_BasePipeTest, tb.AIOTestCase):
|
|
|
|
pass
|