Skip to content

Commit 21ef0f4

Browse files
committed
Moved subprocess test to async loop
increasing problems with sync loop
1 parent f4006a9 commit 21ef0f4

5 files changed

Lines changed: 393 additions & 327 deletions

File tree

tests/python/test_events.py

Lines changed: 5 additions & 327 deletions
Original file line numberDiff line numberDiff line change
@@ -222,45 +222,6 @@ def connection_lost(self, exc):
222222
self.done.set_result(None)
223223

224224

225-
class MySubprocessProtocol(asyncio.SubprocessProtocol):
226-
def __init__(self, loop):
227-
self.state = 'INITIAL'
228-
self.transport = None
229-
self.connected = asyncio.Future(loop=loop)
230-
self.completed = asyncio.Future(loop=loop)
231-
self.disconnects = {fd: asyncio.Future(loop=loop) for fd in range(3)}
232-
self.data = {1: b'', 2: b''}
233-
self.returncode = None
234-
self.got_data = {1: asyncio.Event(loop=loop), 2: asyncio.Event(loop=loop)}
235-
236-
def connection_made(self, transport):
237-
self.transport = transport
238-
assert self.state == 'INITIAL', self.state
239-
self.state = 'CONNECTED'
240-
self.connected.set_result(None)
241-
242-
def connection_lost(self, exc):
243-
assert self.state == 'CONNECTED', self.state
244-
self.state = 'CLOSED'
245-
self.completed.set_result(None)
246-
247-
def pipe_data_received(self, fd, data):
248-
assert self.state == 'CONNECTED', self.state
249-
self.data[fd] += data
250-
self.got_data[fd].set()
251-
252-
def pipe_connection_lost(self, fd, exc):
253-
assert self.state == 'CONNECTED', self.state
254-
if exc:
255-
self.disconnects[fd].set_exception(exc)
256-
else:
257-
self.disconnects[fd].set_result(exc)
258-
259-
def process_exited(self):
260-
assert self.state == 'CONNECTED', self.state
261-
self.returncode = self.transport.get_returncode()
262-
263-
264225
class EventLoopTestsMixin:
265226
def setUp(self):
266227
super().setUp()
@@ -1796,296 +1757,13 @@ def test():
17961757
self.loop.add_signal_handler(signal.SIGTERM, func)
17971758

17981759

1799-
class SubprocessTestsMixin:
1800-
def check_terminated(self, returncode):
1801-
if sys.platform == 'win32':
1802-
self.assertIsInstance(returncode, int)
1803-
# expect 1 but sometimes get 0
1804-
else:
1805-
self.assertEqual(-signal.SIGTERM, returncode)
1806-
1807-
def check_killed(self, returncode):
1808-
if sys.platform == 'win32':
1809-
self.assertIsInstance(returncode, int)
1810-
# expect 1 but sometimes get 0
1811-
else:
1812-
self.assertEqual(-signal.SIGKILL, returncode)
1813-
1814-
def test_subprocess_exec(self):
1815-
prog = os.path.join(os.path.dirname(__file__), 'echo.py')
1816-
1817-
connect = self.loop.subprocess_exec(
1818-
functools.partial(MySubprocessProtocol, self.loop), sys.executable, prog
1819-
)
1820-
transp, proto = self.loop.run_until_complete(connect)
1821-
self.assertIsInstance(proto, MySubprocessProtocol)
1822-
self.loop.run_until_complete(proto.connected)
1823-
self.assertEqual('CONNECTED', proto.state)
1824-
1825-
stdin = transp.get_pipe_transport(0)
1826-
stdin.write(b'Python The Winner')
1827-
self.loop.run_until_complete(proto.got_data[1].wait())
1828-
with test_utils.disable_logger():
1829-
transp.close()
1830-
self.loop.run_until_complete(proto.completed)
1831-
self.check_killed(proto.returncode)
1832-
self.assertEqual(b'Python The Winner', proto.data[1])
1833-
1834-
def test_subprocess_interactive(self):
1835-
prog = os.path.join(os.path.dirname(__file__), 'echo.py')
1836-
1837-
connect = self.loop.subprocess_exec(
1838-
functools.partial(MySubprocessProtocol, self.loop), sys.executable, prog
1839-
)
1840-
transp, proto = self.loop.run_until_complete(connect)
1841-
self.assertIsInstance(proto, MySubprocessProtocol)
1842-
self.loop.run_until_complete(proto.connected)
1843-
self.assertEqual('CONNECTED', proto.state)
1844-
1845-
stdin = transp.get_pipe_transport(0)
1846-
stdin.write(b'Python ')
1847-
self.loop.run_until_complete(proto.got_data[1].wait())
1848-
proto.got_data[1].clear()
1849-
self.assertEqual(b'Python ', proto.data[1])
1850-
1851-
stdin.write(b'The Winner')
1852-
self.loop.run_until_complete(proto.got_data[1].wait())
1853-
self.assertEqual(b'Python The Winner', proto.data[1])
1854-
1855-
with test_utils.disable_logger():
1856-
transp.close()
1857-
self.loop.run_until_complete(proto.completed)
1858-
self.check_killed(proto.returncode)
1859-
1860-
def test_subprocess_shell(self):
1861-
connect = self.loop.subprocess_shell(
1862-
functools.partial(MySubprocessProtocol, self.loop), 'echo Python'
1863-
)
1864-
transp, proto = self.loop.run_until_complete(connect)
1865-
self.assertIsInstance(proto, MySubprocessProtocol)
1866-
self.loop.run_until_complete(proto.connected)
1867-
1868-
transp.get_pipe_transport(0).close()
1869-
self.loop.run_until_complete(proto.completed)
1870-
self.assertEqual(0, proto.returncode)
1871-
self.assertTrue(all(f.done() for f in proto.disconnects.values()))
1872-
self.assertEqual(proto.data[1].rstrip(b'\r\n'), b'Python')
1873-
self.assertEqual(proto.data[2], b'')
1874-
transp.close()
1875-
1876-
def test_subprocess_exitcode(self):
1877-
connect = self.loop.subprocess_shell(
1878-
functools.partial(MySubprocessProtocol, self.loop),
1879-
'exit 7',
1880-
stdin=None,
1881-
stdout=None,
1882-
stderr=None
1883-
)
1884-
transp, proto = self.loop.run_until_complete(connect)
1885-
self.assertIsInstance(proto, MySubprocessProtocol)
1886-
self.loop.run_until_complete(proto.completed)
1887-
self.assertEqual(7, proto.returncode)
1888-
transp.close()
1889-
1890-
def test_subprocess_close_after_finish(self):
1891-
connect = self.loop.subprocess_shell(
1892-
functools.partial(MySubprocessProtocol, self.loop),
1893-
'exit 7',
1894-
stdin=None,
1895-
stdout=None,
1896-
stderr=None
1897-
)
1898-
transp, proto = self.loop.run_until_complete(connect)
1899-
self.assertIsInstance(proto, MySubprocessProtocol)
1900-
self.assertIsNone(transp.get_pipe_transport(0))
1901-
self.assertIsNone(transp.get_pipe_transport(1))
1902-
self.assertIsNone(transp.get_pipe_transport(2))
1903-
self.loop.run_until_complete(proto.completed)
1904-
self.assertEqual(7, proto.returncode)
1905-
self.assertIsNone(transp.close())
1906-
1907-
def test_subprocess_kill(self):
1908-
prog = os.path.join(os.path.dirname(__file__), 'echo.py')
1909-
1910-
connect = self.loop.subprocess_exec(
1911-
functools.partial(MySubprocessProtocol, self.loop), sys.executable, prog
1912-
)
1913-
transp, proto = self.loop.run_until_complete(connect)
1914-
self.assertIsInstance(proto, MySubprocessProtocol)
1915-
self.loop.run_until_complete(proto.connected)
1916-
1917-
transp.kill()
1918-
self.loop.run_until_complete(proto.completed)
1919-
self.check_killed(proto.returncode)
1920-
transp.close()
1921-
1922-
def test_subprocess_terminate(self):
1923-
prog = os.path.join(os.path.dirname(__file__), 'echo.py')
1924-
1925-
connect = self.loop.subprocess_exec(
1926-
functools.partial(MySubprocessProtocol, self.loop), sys.executable, prog
1927-
)
1928-
transp, proto = self.loop.run_until_complete(connect)
1929-
self.assertIsInstance(proto, MySubprocessProtocol)
1930-
self.loop.run_until_complete(proto.connected)
1931-
1932-
transp.terminate()
1933-
self.loop.run_until_complete(proto.completed)
1934-
self.check_terminated(proto.returncode)
1935-
transp.close()
1936-
1937-
@unittest.skipIf(sys.platform == 'win32', "Don't have SIGHUP")
1938-
def test_subprocess_send_signal(self):
1939-
# bpo-31034: Make sure that we get the default signal handler (killing
1940-
# the process). The parent process may have decided to ignore SIGHUP,
1941-
# and signal handlers are inherited.
1942-
old_handler = signal.signal(signal.SIGHUP, signal.SIG_DFL)
1943-
try:
1944-
prog = os.path.join(os.path.dirname(__file__), 'echo.py')
1945-
1946-
connect = self.loop.subprocess_exec(
1947-
functools.partial(MySubprocessProtocol, self.loop), sys.executable, prog
1948-
)
1949-
transp, proto = self.loop.run_until_complete(connect)
1950-
self.assertIsInstance(proto, MySubprocessProtocol)
1951-
self.loop.run_until_complete(proto.connected)
1952-
1953-
transp.send_signal(signal.SIGHUP)
1954-
self.loop.run_until_complete(proto.completed)
1955-
self.assertEqual(-signal.SIGHUP, proto.returncode)
1956-
transp.close()
1957-
finally:
1958-
signal.signal(signal.SIGHUP, old_handler)
1959-
1960-
def test_subprocess_stderr(self):
1961-
prog = os.path.join(os.path.dirname(__file__), 'echo2.py')
1962-
1963-
connect = self.loop.subprocess_exec(
1964-
functools.partial(MySubprocessProtocol, self.loop), sys.executable, prog
1965-
)
1966-
transp, proto = self.loop.run_until_complete(connect)
1967-
self.assertIsInstance(proto, MySubprocessProtocol)
1968-
self.loop.run_until_complete(proto.connected)
1969-
1970-
stdin = transp.get_pipe_transport(0)
1971-
stdin.write(b'test')
1972-
1973-
self.loop.run_until_complete(proto.completed)
1974-
1975-
transp.close()
1976-
self.assertEqual(b'OUT:test', proto.data[1])
1977-
self.assertTrue(proto.data[2].startswith(b'ERR:test'), proto.data[2])
1978-
self.assertEqual(0, proto.returncode)
1979-
1980-
def test_subprocess_stderr_redirect_to_stdout(self):
1981-
prog = os.path.join(os.path.dirname(__file__), 'echo2.py')
1982-
1983-
connect = self.loop.subprocess_exec(
1984-
functools.partial(MySubprocessProtocol, self.loop),
1985-
sys.executable,
1986-
prog,
1987-
stderr=subprocess.STDOUT
1988-
)
1989-
transp, proto = self.loop.run_until_complete(connect)
1990-
self.assertIsInstance(proto, MySubprocessProtocol)
1991-
self.loop.run_until_complete(proto.connected)
1992-
1993-
stdin = transp.get_pipe_transport(0)
1994-
self.assertIsNotNone(transp.get_pipe_transport(1))
1995-
self.assertIsNone(transp.get_pipe_transport(2))
1996-
1997-
stdin.write(b'test')
1998-
self.loop.run_until_complete(proto.completed)
1999-
self.assertTrue(proto.data[1].startswith(b'OUT:testERR:test'), proto.data[1])
2000-
self.assertEqual(b'', proto.data[2])
2001-
2002-
transp.close()
2003-
self.assertEqual(0, proto.returncode)
2004-
2005-
def test_subprocess_close_client_stream(self):
2006-
prog = os.path.join(os.path.dirname(__file__), 'echo3.py')
2007-
2008-
connect = self.loop.subprocess_exec(
2009-
functools.partial(MySubprocessProtocol, self.loop), sys.executable, prog
2010-
)
2011-
transp, proto = self.loop.run_until_complete(connect)
2012-
self.assertIsInstance(proto, MySubprocessProtocol)
2013-
self.loop.run_until_complete(proto.connected)
2014-
2015-
stdin = transp.get_pipe_transport(0)
2016-
stdout = transp.get_pipe_transport(1)
2017-
stdin.write(b'test')
2018-
self.loop.run_until_complete(proto.got_data[1].wait())
2019-
self.assertEqual(b'OUT:test', proto.data[1])
2020-
2021-
stdout.close()
2022-
self.loop.run_until_complete(proto.disconnects[1])
2023-
stdin.write(b'xxx')
2024-
self.loop.run_until_complete(proto.got_data[2].wait())
2025-
if sys.platform != 'win32':
2026-
self.assertEqual(b'ERR:BrokenPipeError', proto.data[2])
2027-
else:
2028-
# After closing the read-end of a pipe, writing to the
2029-
# write-end using os.write() fails with errno==EINVAL and
2030-
# GetLastError()==ERROR_INVALID_NAME on Windows!?! (Using
2031-
# WriteFile() we get ERROR_BROKEN_PIPE as expected.)
2032-
self.assertEqual(b'ERR:OSError', proto.data[2])
2033-
with test_utils.disable_logger():
2034-
transp.close()
2035-
self.loop.run_until_complete(proto.completed)
2036-
self.check_killed(proto.returncode)
2037-
2038-
def test_subprocess_wait_no_same_group(self):
2039-
# start the new process in a new session
2040-
connect = self.loop.subprocess_shell(
2041-
functools.partial(MySubprocessProtocol, self.loop),
2042-
'exit 7',
2043-
stdin=None,
2044-
stdout=None,
2045-
stderr=None,
2046-
start_new_session=True
2047-
)
2048-
_, proto = yield self.loop.run_until_complete(connect)
2049-
self.assertIsInstance(proto, MySubprocessProtocol)
2050-
self.loop.run_until_complete(proto.completed)
2051-
self.assertEqual(7, proto.returncode)
2052-
2053-
def test_subprocess_exec_invalid_args(self):
2054-
@asyncio.coroutine
2055-
def connect(**kwds):
2056-
yield from self.loop.subprocess_exec(asyncio.SubprocessProtocol, 'pwd', **kwds)
2057-
2058-
with self.assertRaises(ValueError):
2059-
self.loop.run_until_complete(connect(universal_newlines=True))
2060-
with self.assertRaises(ValueError):
2061-
self.loop.run_until_complete(connect(bufsize=4096))
2062-
with self.assertRaises(ValueError):
2063-
self.loop.run_until_complete(connect(shell=True))
2064-
2065-
def test_subprocess_shell_invalid_args(self):
2066-
@asyncio.coroutine
2067-
def connect(cmd=None, **kwds):
2068-
if not cmd:
2069-
cmd = 'pwd'
2070-
yield from self.loop.subprocess_shell(asyncio.SubprocessProtocol, cmd, **kwds)
2071-
2072-
with self.assertRaises(ValueError):
2073-
self.loop.run_until_complete(connect(['ls', '-l']))
2074-
with self.assertRaises(ValueError):
2075-
self.loop.run_until_complete(connect(universal_newlines=True))
2076-
with self.assertRaises(ValueError):
2077-
self.loop.run_until_complete(connect(bufsize=4096))
2078-
with self.assertRaises(ValueError):
2079-
self.loop.run_until_complete(connect(shell=False))
2080-
2081-
20821760
if sys.platform == 'win32':
20831761

20841762
class SelectEventLoopTests(EventLoopTestsMixin, test_utils.TestCase):
20851763
def create_event_loop(self):
20861764
return asyncio.SelectorEventLoop()
20871765

2088-
class ProactorEventLoopTests(EventLoopTestsMixin, SubprocessTestsMixin, test_utils.TestCase):
1766+
class ProactorEventLoopTests(EventLoopTestsMixin, test_utils.TestCase):
20891767
def create_event_loop(self):
20901768
return asyncio.ProactorEventLoop()
20911769

@@ -2158,7 +1836,7 @@ async def main():
21581836

21591837
if False and hasattr(selectors, 'KqueueSelector'):
21601838

2161-
class KqueueEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin,
1839+
class KqueueEventLoopTests(UnixEventLoopTestsMixin,
21621840
test_utils.TestCase):
21631841
def create_event_loop(self):
21641842
return asyncio.SelectorEventLoop(selectors.KqueueSelector())
@@ -2180,20 +1858,20 @@ def test_write_pty(self):
21801858

21811859
if False and hasattr(selectors, 'EpollSelector'):
21821860

2183-
class EPollEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin,
1861+
class EPollEventLoopTests(UnixEventLoopTestsMixin,
21841862
test_utils.TestCase):
21851863
def create_event_loop(self):
21861864
return asyncio.SelectorEventLoop(selectors.EpollSelector())
21871865

21881866
if False and hasattr(selectors, 'PollSelector'):
21891867

2190-
class PollEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin,
1868+
class PollEventLoopTests(UnixEventLoopTestsMixin,
21911869
test_utils.TestCase):
21921870
def create_event_loop(self):
21931871
return asyncio.SelectorEventLoop(selectors.PollSelector())
21941872

21951873
# Should always exist.
2196-
class TrioEventLoopTests(UnixEventLoopTestsMixin, SubprocessTestsMixin, test_utils.TestCase):
1874+
class TrioEventLoopTests(UnixEventLoopTestsMixin, test_utils.TestCase):
21971875
def create_event_loop(self):
21981876
import trio_asyncio.sync
21991877
return trio_asyncio.sync.SyncTrioEventLoop()

0 commit comments

Comments
 (0)