Skip to content

Commit

Permalink
Merge pull request #2890 from jakkdl/test_improvements
Browse files Browse the repository at this point in the history
Cleanup of tests, and removal of _assert_raises
  • Loading branch information
Zac-HD authored Nov 28, 2023
2 parents 59731c3 + b9acf48 commit 592718b
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 92 deletions.
56 changes: 24 additions & 32 deletions src/trio/_core/_tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,10 @@ async def main() -> None:
nursery.start_soon(looper)
nursery.start_soon(crasher)

with pytest.raises(ValueError) as excinfo:
with pytest.raises(ValueError, match="argh"):
_core.run(main)

assert looper_record == ["cancelled"]
assert excinfo.value.args == ("argh",)


def test_main_and_task_both_crash() -> None:
Expand Down Expand Up @@ -433,7 +432,10 @@ async def test_cancel_scope_multierror_filtering() -> None:
async def crasher() -> NoReturn:
raise KeyError

try:
# This is outside the outer scope, so all the Cancelled
# exceptions should have been absorbed, leaving just a regular
# KeyError from crasher()
with pytest.raises(KeyError):
with _core.CancelScope() as outer:
try:
async with _core.open_nursery() as nursery:
Expand Down Expand Up @@ -461,15 +463,8 @@ async def crasher() -> NoReturn:
summary[type(exc)] += 1
assert summary == {_core.Cancelled: 3, KeyError: 1}
raise
except AssertionError: # pragma: no cover
raise
except BaseException as exc:
# This is outside the outer scope, so all the Cancelled
# exceptions should have been absorbed, leaving just a regular
# KeyError from crasher()
assert type(exc) is KeyError
else: # pragma: no cover
raise AssertionError()
else:
raise AssertionError("No ExceptionGroup")


async def test_precancelled_task() -> None:
Expand Down Expand Up @@ -785,9 +780,10 @@ async def task2() -> None:
await wait_all_tasks_blocked()
nursery.cancel_scope.__exit__(None, None, None)
finally:
with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(
RuntimeError, match="which had already been exited"
) as exc_info:
await nursery_mgr.__aexit__(*sys.exc_info())
assert "which had already been exited" in str(exc_info.value)
assert type(exc_info.value.__context__) is NonBaseMultiError
assert len(exc_info.value.__context__.exceptions) == 3
cancelled_in_context = False
Expand Down Expand Up @@ -1606,10 +1602,9 @@ async def child_xyzzy() -> None:
async def misguided() -> None:
await child_xyzzy()

with pytest.raises(TypeError) as excinfo:
with pytest.raises(TypeError, match="asyncio") as excinfo:
_core.run(misguided)

assert "asyncio" in str(excinfo.value)
# The traceback should point to the location of the foreign await
assert any( # pragma: no branch
entry.name == "child_xyzzy" for entry in excinfo.traceback
Expand All @@ -1618,11 +1613,10 @@ async def misguided() -> None:

async def test_asyncio_function_inside_nursery_does_not_explode() -> None:
# Regression test for https://github.com/python-trio/trio/issues/552
with pytest.raises(TypeError) as excinfo:
with pytest.raises(TypeError, match="asyncio"):
async with _core.open_nursery() as nursery:
nursery.start_soon(sleep_forever)
await create_asyncio_future_in_new_loop()
assert "asyncio" in str(excinfo.value)


async def test_trivial_yields() -> None:
Expand Down Expand Up @@ -1890,12 +1884,11 @@ async def test_nursery_stop_iteration() -> None:
async def fail() -> NoReturn:
raise ValueError

try:
with pytest.raises(ExceptionGroup) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(fail)
raise StopIteration
except MultiError as e:
assert tuple(map(type, e.exceptions)) == (StopIteration, ValueError)
assert tuple(map(type, excinfo.value.exceptions)) == (StopIteration, ValueError)


async def test_nursery_stop_async_iteration() -> None:
Expand Down Expand Up @@ -1944,7 +1937,7 @@ async def test_traceback_frame_removal() -> None:
async def my_child_task() -> NoReturn:
raise KeyError()

try:
with pytest.raises(ExceptionGroup) as excinfo:
# Trick: For now cancel/nursery scopes still leave a bunch of tb gunk
# behind. But if there's a MultiError, they leave it on the MultiError,
# which lets us get a clean look at the KeyError itself. Someday I
Expand All @@ -1953,16 +1946,15 @@ async def my_child_task() -> NoReturn:
async with _core.open_nursery() as nursery:
nursery.start_soon(my_child_task)
nursery.start_soon(my_child_task)
except MultiError as exc:
first_exc = exc.exceptions[0]
assert isinstance(first_exc, KeyError)
# The top frame in the exception traceback should be inside the child
# task, not trio/contextvars internals. And there's only one frame
# inside the child task, so this will also detect if our frame-removal
# is too eager.
tb = first_exc.__traceback__
assert tb is not None
assert tb.tb_frame.f_code is my_child_task.__code__
first_exc = excinfo.value.exceptions[0]
assert isinstance(first_exc, KeyError)
# The top frame in the exception traceback should be inside the child
# task, not trio/contextvars internals. And there's only one frame
# inside the child task, so this will also detect if our frame-removal
# is too eager.
tb = first_exc.__traceback__
assert tb is not None
assert tb.tb_frame.f_code is my_child_task.__code__


def test_contextvar_support() -> None:
Expand Down
98 changes: 42 additions & 56 deletions src/trio/_tests/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@
from contextlib import asynccontextmanager, contextmanager, suppress
from functools import partial
from ssl import SSLContext
from typing import TYPE_CHECKING, Any, AsyncIterator, Iterator, NoReturn
from typing import (
TYPE_CHECKING,
Any,
AsyncIterator,
Awaitable,
Callable,
Iterator,
NoReturn,
)

import pytest

Expand Down Expand Up @@ -344,33 +352,21 @@ async def test_PyOpenSSLEchoStream_gives_resource_busy_errors() -> None:
# PyOpenSSLEchoStream, so this makes sure that if we do have a bug then
# PyOpenSSLEchoStream will notice and complain.

s = PyOpenSSLEchoStream()
with pytest.raises(_core.BusyResourceError) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(s.send_all, b"x")
nursery.start_soon(s.send_all, b"x")
assert "simultaneous" in str(excinfo.value)

s = PyOpenSSLEchoStream()
with pytest.raises(_core.BusyResourceError) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(s.send_all, b"x")
nursery.start_soon(s.wait_send_all_might_not_block)
assert "simultaneous" in str(excinfo.value)

s = PyOpenSSLEchoStream()
with pytest.raises(_core.BusyResourceError) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(s.wait_send_all_might_not_block)
nursery.start_soon(s.wait_send_all_might_not_block)
assert "simultaneous" in str(excinfo.value)
async def do_test(
func1: str, args1: tuple[object, ...], func2: str, args2: tuple[object, ...]
) -> None:
s = PyOpenSSLEchoStream()
with pytest.raises(_core.BusyResourceError, match="simultaneous"):
async with _core.open_nursery() as nursery:
nursery.start_soon(getattr(s, func1), *args1)
nursery.start_soon(getattr(s, func2), *args2)

s = PyOpenSSLEchoStream()
with pytest.raises(_core.BusyResourceError) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(s.receive_some, 1)
nursery.start_soon(s.receive_some, 1)
assert "simultaneous" in str(excinfo.value)
await do_test("send_all", (b"x",), "send_all", (b"x",))
await do_test("send_all", (b"x",), "wait_send_all_might_not_block", ())
await do_test(
"wait_send_all_might_not_block", (), "wait_send_all_might_not_block", ()
)
await do_test("receive_some", (1,), "receive_some", (1,))


@contextmanager # type: ignore[misc] # decorated contains Any
Expand Down Expand Up @@ -727,45 +723,35 @@ async def sleeper_with_slow_wait_writable_and_expect(method: str) -> None:


async def test_resource_busy_errors(client_ctx: SSLContext) -> None:
async def do_send_all() -> None:
S: TypeAlias = trio.SSLStream[
trio.StapledStream[trio.abc.SendStream, trio.abc.ReceiveStream]
]

async def do_send_all(s: S) -> None:
with assert_checkpoints():
await s.send_all(b"x")

async def do_receive_some() -> None:
async def do_receive_some(s: S) -> None:
with assert_checkpoints():
await s.receive_some(1)

async def do_wait_send_all_might_not_block() -> None:
async def do_wait_send_all_might_not_block(s: S) -> None:
with assert_checkpoints():
await s.wait_send_all_might_not_block()

s, _ = ssl_lockstep_stream_pair(client_ctx)
with pytest.raises(_core.BusyResourceError) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(do_send_all)
nursery.start_soon(do_send_all)
assert "another task" in str(excinfo.value)

s, _ = ssl_lockstep_stream_pair(client_ctx)
with pytest.raises(_core.BusyResourceError) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(do_receive_some)
nursery.start_soon(do_receive_some)
assert "another task" in str(excinfo.value)

s, _ = ssl_lockstep_stream_pair(client_ctx)
with pytest.raises(_core.BusyResourceError) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(do_send_all)
nursery.start_soon(do_wait_send_all_might_not_block)
assert "another task" in str(excinfo.value)
async def do_test(
func1: Callable[[S], Awaitable[None]], func2: Callable[[S], Awaitable[None]]
) -> None:
s, _ = ssl_lockstep_stream_pair(client_ctx)
with pytest.raises(_core.BusyResourceError, match="another task"):
async with _core.open_nursery() as nursery:
nursery.start_soon(func1, s)
nursery.start_soon(func2, s)

s, _ = ssl_lockstep_stream_pair(client_ctx)
with pytest.raises(_core.BusyResourceError) as excinfo:
async with _core.open_nursery() as nursery:
nursery.start_soon(do_wait_send_all_might_not_block)
nursery.start_soon(do_wait_send_all_might_not_block)
assert "another task" in str(excinfo.value)
await do_test(do_send_all, do_send_all)
await do_test(do_receive_some, do_receive_some)
await do_test(do_send_all, do_wait_send_all_might_not_block)
await do_test(do_wait_send_all_might_not_block, do_wait_send_all_might_not_block)


async def test_wait_writable_calls_underlying_wait_writable() -> None:
Expand Down
6 changes: 2 additions & 4 deletions src/trio/_tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,19 @@ async def test_ConflictDetector() -> None:
with ul2:
print("ok")

with pytest.raises(_core.BusyResourceError) as excinfo:
with pytest.raises(_core.BusyResourceError, match="ul1"):
with ul1:
with ul1:
pass # pragma: no cover
assert "ul1" in str(excinfo.value)

async def wait_with_ul1() -> None:
with ul1:
await wait_all_tasks_blocked()

with pytest.raises(_core.BusyResourceError) as excinfo:
with pytest.raises(_core.BusyResourceError, match="ul1"):
async with _core.open_nursery() as nursery:
nursery.start_soon(wait_with_ul1)
nursery.start_soon(wait_with_ul1)
assert "ul1" in str(excinfo.value)


def test_module_metadata_is_fixed_up() -> None:
Expand Down
2 changes: 2 additions & 0 deletions src/trio/testing/_check_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ async def __aexit__(
await aclose_forcefully(self._second)


# This is used in this file instead of pytest.raises in order to avoid a dependency
# on pytest, as the check_* functions are publicly exported.
@contextmanager
def _assert_raises(exc: type[BaseException]) -> Generator[None, None, None]:
__tracebackhide__ = True
Expand Down

0 comments on commit 592718b

Please sign in to comment.