705 lines
20 KiB
Python
705 lines
20 KiB
Python
import asyncio
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
|
|
from toga.handlers import AsyncResult, NativeHandler, wrapped_handler
|
|
|
|
|
|
class ExampleAsyncResult(AsyncResult):
|
|
RESULT_TYPE = "Test"
|
|
|
|
|
|
def test_noop_handler():
|
|
"""None can be wrapped as a valid handler."""
|
|
obj = Mock()
|
|
|
|
wrapped = wrapped_handler(obj, None)
|
|
|
|
assert wrapped._raw is None
|
|
|
|
# This does nothing, but doesn't raise an error.
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
|
|
|
|
def test_noop_handler_with_cleanup():
|
|
"""Cleanup is still performed when a no-op handler is used."""
|
|
obj = Mock()
|
|
cleanup = Mock()
|
|
|
|
wrapped = wrapped_handler(obj, None, cleanup=cleanup)
|
|
|
|
assert wrapped._raw is None
|
|
|
|
# This does nothing, but doesn't raise an error.
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
|
|
# Cleanup method was invoked
|
|
cleanup.assert_called_once_with(obj, None)
|
|
|
|
|
|
def test_noop_handler_with_cleanup_error(capsys):
|
|
"""If cleanup on a no-op handler raises an error, it is logged."""
|
|
obj = Mock()
|
|
cleanup = Mock(side_effect=Exception("Problem in cleanup"))
|
|
|
|
wrapped = wrapped_handler(obj, None, cleanup=cleanup)
|
|
|
|
assert wrapped._raw is None
|
|
|
|
# This does nothing, but doesn't raise an error.
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
|
|
# Cleanup method was invoked
|
|
cleanup.assert_called_once_with(obj, None)
|
|
|
|
# Evidence of the handler cleanup error is in the log.
|
|
assert (
|
|
"Error in handler cleanup: Problem in cleanup\nTraceback (most recent call last):\n"
|
|
in capsys.readouterr().err
|
|
)
|
|
|
|
|
|
def test_function_handler():
|
|
"""A function can be used as a handler."""
|
|
obj = Mock()
|
|
handler_call = {}
|
|
|
|
def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
|
|
wrapped = wrapped_handler(obj, handler)
|
|
|
|
# Raw handler is the original function
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
}
|
|
|
|
|
|
def test_function_handler_error(capsys):
|
|
"""A function handler can raise an error."""
|
|
obj = Mock()
|
|
handler_call = {}
|
|
|
|
def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
raise Exception("Problem in handler")
|
|
|
|
wrapped = wrapped_handler(obj, handler)
|
|
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke handler. The exception is swallowed
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
}
|
|
|
|
# Evidence of the handler error is in the log.
|
|
assert (
|
|
"Error in handler: Problem in handler\nTraceback (most recent call last):\n"
|
|
in capsys.readouterr().err
|
|
)
|
|
|
|
|
|
def test_function_handler_with_cleanup():
|
|
"""A function handler can have a cleanup method."""
|
|
obj = Mock()
|
|
cleanup = Mock()
|
|
handler_call = {}
|
|
|
|
def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
return 42
|
|
|
|
wrapped = wrapped_handler(obj, handler, cleanup=cleanup)
|
|
|
|
# Raw handler is the original function
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke handler
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
}
|
|
|
|
# Cleanup method was invoked
|
|
cleanup.assert_called_once_with(obj, 42)
|
|
|
|
|
|
def test_function_handler_with_cleanup_error(capsys):
|
|
"""A function handler can have a cleanup method that raises an error."""
|
|
obj = Mock()
|
|
cleanup = Mock(side_effect=Exception("Problem in cleanup"))
|
|
handler_call = {}
|
|
|
|
def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
return 42
|
|
|
|
wrapped = wrapped_handler(obj, handler, cleanup=cleanup)
|
|
|
|
# Raw handler is the original function
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke handler. The exception in cleanup is swallowed
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
}
|
|
|
|
# Cleanup method was invoked
|
|
cleanup.assert_called_once_with(obj, 42)
|
|
|
|
# Evidence of the handler cleanup error is in the log.
|
|
assert (
|
|
"Error in handler cleanup: Problem in cleanup\nTraceback (most recent call last):\n"
|
|
in capsys.readouterr().err
|
|
)
|
|
|
|
|
|
def test_generator_handler(event_loop):
|
|
"""A generator can be used as a handler."""
|
|
obj = Mock()
|
|
handler_call = {}
|
|
|
|
def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
yield 0.01 # A short sleep
|
|
handler_call["slept"] = True
|
|
yield # A yield without a sleep
|
|
handler_call["done"] = True
|
|
|
|
wrapped = wrapped_handler(obj, handler)
|
|
|
|
# Raw handler is the original generator
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper inside an active run loop.
|
|
async def waiter():
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
count = 0
|
|
while not handler_call.get("done", False) and count < 5:
|
|
await asyncio.sleep(0.01)
|
|
count += 1
|
|
|
|
event_loop.run_until_complete(waiter())
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
"slept": True,
|
|
"done": True,
|
|
}
|
|
|
|
|
|
def test_generator_handler_error(event_loop, capsys):
|
|
"""A generator can raise an error."""
|
|
obj = Mock()
|
|
handler_call = {}
|
|
|
|
def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
yield 0.01 # A short sleep
|
|
raise Exception("Problem in handler")
|
|
|
|
wrapped = wrapped_handler(obj, handler)
|
|
|
|
# Raw handler is the original generator
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper inside an active run loop.
|
|
async def waiter():
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
count = 0
|
|
while not handler_call.get("done", False) and count < 5:
|
|
await asyncio.sleep(0.01)
|
|
count += 1
|
|
|
|
event_loop.run_until_complete(waiter())
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
}
|
|
|
|
# Evidence of the handler cleanup error is in the log.
|
|
assert (
|
|
"Error in long running handler: Problem in handler\nTraceback (most recent call last):\n"
|
|
in capsys.readouterr().err
|
|
)
|
|
|
|
|
|
def test_generator_handler_with_cleanup(event_loop):
|
|
"""A generator can have cleanup."""
|
|
obj = Mock()
|
|
cleanup = Mock()
|
|
handler_call = {}
|
|
|
|
def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
yield 0.01 # A short sleep
|
|
handler_call["slept"] = True
|
|
yield # A yield without a sleep
|
|
handler_call["done"] = True
|
|
return 42
|
|
|
|
wrapped = wrapped_handler(obj, handler, cleanup=cleanup)
|
|
|
|
# Raw handler is the original generator
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper inside an active run loop.
|
|
async def waiter():
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
count = 0
|
|
while not handler_call.get("done", False) and count < 5:
|
|
await asyncio.sleep(0.01)
|
|
count += 1
|
|
|
|
event_loop.run_until_complete(waiter())
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
"slept": True,
|
|
"done": True,
|
|
}
|
|
|
|
# Cleanup method was invoked
|
|
cleanup.assert_called_once_with(obj, 42)
|
|
|
|
|
|
def test_generator_handler_with_cleanup_error(event_loop, capsys):
|
|
"""A generator can raise an error during cleanup."""
|
|
obj = Mock()
|
|
cleanup = Mock(side_effect=Exception("Problem in cleanup"))
|
|
handler_call = {}
|
|
|
|
def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
yield 0.01 # A short sleep
|
|
handler_call["slept"] = True
|
|
yield # A yield without a sleep
|
|
handler_call["done"] = True
|
|
return 42
|
|
|
|
wrapped = wrapped_handler(obj, handler, cleanup=cleanup)
|
|
|
|
# Raw handler is the original generator
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper inside an active run loop.
|
|
async def waiter():
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
count = 0
|
|
while not handler_call.get("done", False) and count < 5:
|
|
await asyncio.sleep(0.01)
|
|
count += 1
|
|
|
|
event_loop.run_until_complete(waiter())
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
"slept": True,
|
|
"done": True,
|
|
}
|
|
|
|
# Cleanup method was invoked
|
|
cleanup.assert_called_once_with(obj, 42)
|
|
|
|
# Evidence of the handler cleanup error is in the log.
|
|
assert (
|
|
"Error in long running handler cleanup: Problem in cleanup\nTraceback (most recent call last):\n"
|
|
in capsys.readouterr().err
|
|
)
|
|
|
|
|
|
def test_coroutine_handler(event_loop):
|
|
"""A coroutine can be used as a handler."""
|
|
obj = Mock()
|
|
handler_call = {}
|
|
|
|
async def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
await asyncio.sleep(0.01) # A short sleep
|
|
handler_call["done"] = True
|
|
|
|
wrapped = wrapped_handler(obj, handler)
|
|
|
|
# Raw handler is the original coroutine
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper inside an active run loop.
|
|
async def waiter():
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
count = 0
|
|
while not handler_call.get("done", False) and count < 5:
|
|
await asyncio.sleep(0.01)
|
|
count += 1
|
|
|
|
event_loop.run_until_complete(waiter())
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
"done": True,
|
|
}
|
|
|
|
|
|
def test_coroutine_handler_error(event_loop, capsys):
|
|
"""A coroutine can raise an error."""
|
|
obj = Mock()
|
|
handler_call = {}
|
|
|
|
async def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
await asyncio.sleep(0.01) # A short sleep
|
|
raise Exception("Problem in handler")
|
|
|
|
wrapped = wrapped_handler(obj, handler)
|
|
|
|
# Raw handler is the original coroutine
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper inside an active run loop.
|
|
async def waiter():
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
count = 0
|
|
while not handler_call.get("done", False) and count < 5:
|
|
await asyncio.sleep(0.01)
|
|
count += 1
|
|
|
|
event_loop.run_until_complete(waiter())
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
}
|
|
|
|
# Evidence of the handler cleanup error is in the log.
|
|
assert (
|
|
"Error in async handler: Problem in handler\nTraceback (most recent call last):\n"
|
|
in capsys.readouterr().err
|
|
)
|
|
|
|
|
|
def test_coroutine_handler_with_cleanup(event_loop):
|
|
"""A coroutine can have cleanup."""
|
|
obj = Mock()
|
|
cleanup = Mock()
|
|
handler_call = {}
|
|
|
|
async def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
await asyncio.sleep(0.01) # A short sleep
|
|
handler_call["done"] = True
|
|
return 42
|
|
|
|
wrapped = wrapped_handler(obj, handler, cleanup=cleanup)
|
|
|
|
# Raw handler is the original coroutine
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper inside an active run loop.
|
|
async def waiter():
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
count = 0
|
|
while not handler_call.get("done", False) and count < 5:
|
|
await asyncio.sleep(0.01)
|
|
count += 1
|
|
|
|
event_loop.run_until_complete(waiter())
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
"done": True,
|
|
}
|
|
|
|
# Cleanup method was invoked
|
|
cleanup.assert_called_once_with(obj, 42)
|
|
|
|
|
|
def test_coroutine_handler_with_cleanup_error(event_loop, capsys):
|
|
"""A coroutine can raise an error during cleanup."""
|
|
obj = Mock()
|
|
cleanup = Mock(side_effect=Exception("Problem in cleanup"))
|
|
handler_call = {}
|
|
|
|
async def handler(*args, **kwargs):
|
|
handler_call["args"] = args
|
|
handler_call["kwargs"] = kwargs
|
|
await asyncio.sleep(0.01) # A short sleep
|
|
handler_call["done"] = True
|
|
return 42
|
|
|
|
wrapped = wrapped_handler(obj, handler, cleanup=cleanup)
|
|
|
|
# Raw handler is the original coroutine
|
|
assert wrapped._raw == handler
|
|
|
|
# Invoke wrapper inside an active run loop.
|
|
async def waiter():
|
|
wrapped("arg1", "arg2", kwarg1=3, kwarg2=4)
|
|
count = 0
|
|
while not handler_call.get("done", False) and count < 5:
|
|
await asyncio.sleep(0.01)
|
|
count += 1
|
|
|
|
event_loop.run_until_complete(waiter())
|
|
|
|
# Handler arguments are as expected.
|
|
assert handler_call == {
|
|
"args": (obj, "arg1", "arg2"),
|
|
"kwargs": {"kwarg1": 3, "kwarg2": 4},
|
|
"done": True,
|
|
}
|
|
|
|
# Cleanup method was invoked
|
|
cleanup.assert_called_once_with(obj, 42)
|
|
|
|
# Evidence of the handler cleanup error is in the log.
|
|
assert (
|
|
"Error in async handler cleanup: Problem in cleanup\nTraceback (most recent call last):\n"
|
|
in capsys.readouterr().err
|
|
)
|
|
|
|
|
|
def test_native_handler():
|
|
"""A native function can be used as a handler."""
|
|
obj = Mock()
|
|
native_method = Mock()
|
|
|
|
handler = NativeHandler(native_method)
|
|
|
|
wrapped = wrapped_handler(obj, handler)
|
|
|
|
# Native method is returned as the handler.
|
|
assert wrapped == native_method
|
|
|
|
|
|
def test_async_result_non_comparable(event_loop):
|
|
"""An async result can't be compared or evaluated."""
|
|
result = ExampleAsyncResult(None)
|
|
|
|
# repr for the result is useful
|
|
assert repr(result) == "<Async Test result; future=<Future pending>>"
|
|
|
|
# Result cannot be compared.
|
|
|
|
with pytest.raises(
|
|
RuntimeError,
|
|
match=r"Can't check Test result directly; use await or an on_result handler",
|
|
):
|
|
result == 42
|
|
|
|
with pytest.raises(
|
|
RuntimeError,
|
|
match=r"Can't check Test result directly; use await or an on_result handler",
|
|
):
|
|
result < 42
|
|
|
|
with pytest.raises(
|
|
RuntimeError,
|
|
match=r"Can't check Test result directly; use await or an on_result handler",
|
|
):
|
|
result <= 42
|
|
|
|
with pytest.raises(
|
|
RuntimeError,
|
|
match=r"Can't check Test result directly; use await or an on_result handler",
|
|
):
|
|
result > 42
|
|
|
|
with pytest.raises(
|
|
RuntimeError,
|
|
match=r"Can't check Test result directly; use await or an on_result handler",
|
|
):
|
|
result >= 42
|
|
|
|
with pytest.raises(
|
|
RuntimeError,
|
|
match=r"Can't check Test result directly; use await or an on_result handler",
|
|
):
|
|
result != 42
|
|
|
|
|
|
def test_async_result(event_loop):
|
|
"""An async result can be set."""
|
|
result = ExampleAsyncResult()
|
|
|
|
result.set_result(42)
|
|
|
|
# Evaluate the future
|
|
async_answer = event_loop.run_until_complete(result.future)
|
|
|
|
# The answer was returned, and passed to the callback
|
|
assert async_answer == 42
|
|
|
|
|
|
def test_async_result_cancelled(event_loop):
|
|
"""An async result can be set even if the future is cancelled."""
|
|
result = ExampleAsyncResult()
|
|
|
|
# cancel the future.
|
|
result.future.cancel()
|
|
|
|
result.set_result(42)
|
|
|
|
# Evaluate the future. This will raise an error
|
|
with pytest.raises(asyncio.CancelledError):
|
|
event_loop.run_until_complete(result.future)
|
|
|
|
|
|
def test_async_exception(event_loop):
|
|
"""An async result can raise an exception."""
|
|
result = ExampleAsyncResult()
|
|
|
|
result.set_exception(ValueError("Bad stuff"))
|
|
|
|
# Evaluate the future; this will raise an exception
|
|
with pytest.raises(ValueError, match=r"Bad stuff"):
|
|
event_loop.run_until_complete(result.future)
|
|
|
|
|
|
def test_async_exception_cancelled(event_loop):
|
|
"""An async result can raise an exception even if the future is cancelled."""
|
|
result = ExampleAsyncResult()
|
|
|
|
# Cancel the future
|
|
result.future.cancel()
|
|
|
|
result.set_exception(ValueError("Bad stuff"))
|
|
|
|
# Evaluate the future. This will raise an error
|
|
with pytest.raises(asyncio.CancelledError):
|
|
event_loop.run_until_complete(result.future)
|
|
|
|
|
|
######################################################################
|
|
# 2023-12: Backwards compatibility
|
|
######################################################################
|
|
|
|
|
|
def test_async_result_sync(event_loop):
|
|
"""The deprecated behavior of using a synchronous result handler is supported."""
|
|
on_result = Mock()
|
|
|
|
with pytest.warns(
|
|
DeprecationWarning,
|
|
match=r"Synchronous `on_result` handlers have been deprecated;",
|
|
):
|
|
result = ExampleAsyncResult(on_result)
|
|
|
|
result.set_result(42)
|
|
|
|
# Evaluate the future
|
|
async_answer = event_loop.run_until_complete(result.future)
|
|
|
|
# The answer was returned, and passed to the callback
|
|
assert async_answer == 42
|
|
on_result.assert_called_once_with(42)
|
|
|
|
|
|
def test_async_result_cancelled_sync(event_loop):
|
|
"""A deprecated on_result handler won't be fired on a cancelled future."""
|
|
on_result = Mock()
|
|
|
|
with pytest.warns(
|
|
DeprecationWarning,
|
|
match=r"Synchronous `on_result` handlers have been deprecated;",
|
|
):
|
|
result = ExampleAsyncResult(on_result)
|
|
|
|
# cancel the future.
|
|
result.future.cancel()
|
|
|
|
result.set_result(42)
|
|
|
|
# Evaluate the future. This will raise an error
|
|
with pytest.raises(asyncio.CancelledError):
|
|
event_loop.run_until_complete(result.future)
|
|
|
|
# The callback wasn't called
|
|
on_result.assert_not_called()
|
|
|
|
|
|
def test_async_exception_sync(event_loop):
|
|
"""A deprecated on_result handler can raise an exception."""
|
|
on_result = Mock()
|
|
|
|
with pytest.warns(
|
|
DeprecationWarning,
|
|
match=r"Synchronous `on_result` handlers have been deprecated;",
|
|
):
|
|
result = ExampleAsyncResult(on_result)
|
|
|
|
result.set_exception(ValueError("Bad stuff"))
|
|
|
|
# Evaluate the future; this will raise an exception
|
|
with pytest.raises(ValueError, match=r"Bad stuff"):
|
|
event_loop.run_until_complete(result.future)
|
|
|
|
# The answer was returned, and passed to the callback
|
|
on_result.assert_called_once()
|
|
assert on_result.call_args[0] == (None,)
|
|
assert isinstance(on_result.call_args[1]["exception"], ValueError)
|
|
|
|
|
|
def test_async_exception_cancelled_sync(event_loop):
|
|
"""An async result can raise an exception even if the future is cancelled."""
|
|
on_result = Mock()
|
|
|
|
with pytest.warns(
|
|
DeprecationWarning,
|
|
match=r"Synchronous `on_result` handlers have been deprecated;",
|
|
):
|
|
result = ExampleAsyncResult(on_result)
|
|
|
|
# Cancel the future
|
|
result.future.cancel()
|
|
|
|
result.set_exception(ValueError("Bad stuff"))
|
|
|
|
# Evaluate the future. This will raise an error
|
|
with pytest.raises(asyncio.CancelledError):
|
|
event_loop.run_until_complete(result.future)
|
|
|
|
# The callback wasn't called
|
|
on_result.assert_not_called()
|