-
-
Notifications
You must be signed in to change notification settings - Fork 27
Closed
Description
Consider the following code. It consists of an echo server with a bug, and a unit test for the server:
#!/usr/bin/env python3
import pytest
import pytest_trio
import trio
import logging
log = logging.getLogger(__name__)
PORT = 12346
BUFSIZE = 16384
async def echo_server(server_sock):
try:
with server_sock:
await server_sock.recv(BUFSIZE)
raise RuntimeError('Damn!')
except Exception as exc:
await trio.sleep(0.1) # make problem deterministic
log.exception('Things went wrong...')
async def echo_listener(nursery):
with trio.socket.socket() as listen_sock:
await listen_sock.bind(("127.0.0.1", PORT))
listen_sock.listen()
while True:
server_sock, _ = await listen_sock.accept()
nursery.start_soon(echo_server, server_sock)
async def parent():
async with trio.open_nursery() as nursery:
nursery.start_soon(echo_listener, nursery)
@pytest.mark.trio
async def test_server(nursery):
nursery.start_soon(parent)
await trio.sleep(0.5) # wait for server to listen
with trio.socket.socket() as client_sock:
await client_sock.connect(("127.0.0.1", PORT))
await client_sock.send(b'hello, world')
resp = await client_sock.recv(BUFSIZE)
assert resp == b'hello, world'As expected, the unittest fails - but the root cause is nowhere to be seen:
$ python3 -m pytest tests/lograce.py
==================================== test session starts =====================================
platform linux -- Python 3.6.4, pytest-3.3.2, py-1.5.2, pluggy-0.6.0 -- /home/nikratio/consulting/iofabric/venv/bin/python3
cachedir: tests/.cache
rootdir: /home/nikratio/consulting/iofabric/gruagach/tests, inifile: pytest.ini
plugins: trio-0.3.0
collected 1 item
tests/lograce.py::test_server FAILED [100%]
========================================== FAILURES ==========================================
________________________________________ test_server _________________________________________
Traceback (most recent call last):
File "/home/nikratio/consulting/iofabric/gruagach/tests/lograce.py", line 41, in test_server
assert resp == b'hello, world'
AssertionError: assert b'' == b'hello, world'
Right contains more items, first extra item: 104
Full diff:
- b''
+ b'hello, world'
================================== 1 failed in 0.63 seconds ==================================
If we force the test to wait a moment before failing...
@pytest.mark.trio
async def test_server(nursery):
nursery.start_soon(parent)
await trio.sleep(0.5) # wait for server to listen
try:
with trio.socket.socket() as client_sock:
await client_sock.connect(("127.0.0.1", PORT))
await client_sock.send(b'hello, world')
resp = await client_sock.recv(BUFSIZE)
assert resp == b'hello, world'
finally:
await trio.sleep(0.5)...we get much better results:
$ python3 -m pytest tests/lograce.py
[...]
collected 1 item
tests/lograce.py::test_server FAILED [100%]
========================================== FAILURES ==========================================
________________________________________ test_server _________________________________________
Traceback (most recent call last):
File "/home/nikratio/consulting/iofabric/gruagach/tests/lograce.py", line 42, in test_server
assert resp == b'hello, world'
AssertionError: assert b'' == b'hello, world'
Right contains more items, first extra item: 104
Full diff:
- b''
+ b'hello, world'
------------------------------------- Captured log call --------------------------------------
21:55:13.383 ERROR lograce.echo_server: Things went wrong...
Traceback (most recent call last):
File "/home/nikratio/consulting/iofabric/gruagach/tests/lograce.py", line 15, in echo_server
raise RuntimeError('Damn!')
RuntimeError: Damn!
================================== 1 failed in 1.13 seconds ==================================
I believe the problem is that the failing test causes trio to cancel the server tasks before it has a chance to log its exception. Note that the call to trio.wait in echo_server is not necessay for the problem to occur, it just makes it much more likely.
I am not sure how to best fix this. Maybe the trio test runner could, if the test fails, give the other nursery task a short grace period in which they have a chance to report any errors before cancelling them?
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels