asyncio: Add asyncio tests for new task features.

This commit is contained in:
James Ward 2023-11-16 19:12:33 -05:00
parent adbb02d9e3
commit e0d3123138
No known key found for this signature in database
GPG Key ID: F53FE2DEDD7BBD79
14 changed files with 431 additions and 0 deletions

View File

@ -0,0 +1,54 @@
# Test the Task.add_done_callback() method
try:
import asyncio
except ImportError:
print("SKIP")
raise SystemExit
async def task(t, exc=None):
if t >= 0:
await asyncio.sleep(t)
if exc:
raise exc
def done_callback(t, er):
print("done", repr(t), repr(er))
async def main():
# Tasks that aren't done only execute done callback after finishing
print("=" * 10)
t = asyncio.create_task(task(-1))
t.add_done_callback(done_callback)
print("Waiting for task to complete")
await asyncio.sleep(0)
print("Task has completed")
# Task that are done run the callback immediately
print("=" * 10)
t = asyncio.create_task(task(-1))
await asyncio.sleep(0)
print("Task has completed")
t.add_done_callback(done_callback)
print("Callback Added")
# Task that starts, runs and finishes without an exception should return None
print("=" * 10)
t = asyncio.create_task(task(0.01))
t.add_done_callback(done_callback)
try:
t.add_done_callback(done_callback)
except RuntimeError as e:
print("Second call to add_done_callback emits error:", repr(e))
# Task that raises immediately should still run done callback
print("=" * 10)
t = asyncio.create_task(task(-1, ValueError))
t.add_done_callback(done_callback)
await asyncio.sleep(0)
asyncio.run(main())

View File

@ -0,0 +1,12 @@
==========
Waiting for task to complete
done <Task> StopIteration()
Task has completed
==========
Task has completed
done <Task> StopIteration()
Callback Added
==========
Second call to add_done_callback emits error: RuntimeError('>1 callback unsupported',)
==========
done <Task> ValueError()

View File

@ -0,0 +1,54 @@
# Test the `Task.cancelled` method
try:
import asyncio
except ImportError:
print("SKIP")
raise SystemExit
async def task(t):
await asyncio.sleep(t)
async def main():
# Cancel task immediately doesn't mark the task as cancelled
print("=" * 10)
t = asyncio.create_task(task(2))
t.cancel()
print("Expecting task to not be cancelled because it is not done:", t.cancelled())
# Cancel task immediately and wait for cancellation to complete
print("=" * 10)
t = asyncio.create_task(task(2))
t.cancel()
await asyncio.sleep(0)
print("Expecting Task to be Cancelled:", t.cancelled())
# Cancel task and wait for cancellation to complete
print("=" * 10)
t = asyncio.create_task(task(2))
await asyncio.sleep(0.01)
t.cancel()
await asyncio.sleep(0)
print("Expecting Task to be Cancelled:", t.cancelled())
# Cancel task multiple times after it has started
print("=" * 10)
t = asyncio.create_task(task(2))
await asyncio.sleep(0.01)
for _ in range(4):
t.cancel()
await asyncio.sleep(0.01)
print("Expecting Task to be Cancelled:", t.cancelled())
# Cancel task after it has finished
print("=" * 10)
t = asyncio.create_task(task(0.01))
await asyncio.sleep(0.05)
t.cancel()
print("Expecting task to not be Cancelled:", t.cancelled())
asyncio.run(main())

View File

@ -0,0 +1,10 @@
==========
Expecting task to not be cancelled because it is not done: False
==========
Expecting Task to be Cancelled: True
==========
Expecting Task to be Cancelled: True
==========
Expecting Task to be Cancelled: True
==========
Expecting task to not be Cancelled: False

View File

@ -0,0 +1,64 @@
# Test the Task.exception() method
try:
import asyncio
except ImportError:
print("SKIP")
raise SystemExit
async def task(t, exc=None):
if t >= 0:
await asyncio.sleep(t)
if exc:
raise exc
async def main():
# Task that is not done yet raises an InvalidStateError
print("=" * 10)
t = asyncio.create_task(task(1))
await asyncio.sleep(0)
try:
t.exception()
assert False, "Should not get here"
except Exception as e:
print("Tasks that aren't done yet raise an InvalidStateError:", repr(e))
# Task that is cancelled raises CancelledError
print("=" * 10)
t = asyncio.create_task(task(1))
t.cancel()
await asyncio.sleep(0)
try:
print(repr(t.exception()))
print(t.cancelled())
assert False, "Should not get here"
except asyncio.CancelledError as e:
print("Cancelled tasks cannot retrieve exception:", repr(e))
# Task that starts, runs and finishes without an exception should return None
print("=" * 10)
t = asyncio.create_task(task(0.01))
await t
print("None when no exception:", t.exception())
# Task that raises immediately should return that exception
print("=" * 10)
t = asyncio.create_task(task(-1, ValueError))
try:
await t
assert False, "Should not get here"
except ValueError as e:
pass
print("Returned Exception:", repr(t.exception()))
# Task returns `none` when somehow an exception isn't in data
print("=" * 10)
t = asyncio.create_task(task(-1))
await t
t.data = "Example"
print(t.exception())
asyncio.run(main())

View File

@ -0,0 +1,10 @@
==========
Tasks that aren't done yet raise an InvalidStateError: InvalidStateError()
==========
Cancelled tasks cannot retrieve exception: CancelledError()
==========
None when no exception: None
==========
Returned Exception: ValueError()
==========
None

View File

@ -0,0 +1,28 @@
# Test the `Task.get_coro()` method
try:
import asyncio
except ImportError:
print("SKIP")
raise SystemExit
async def action():
pass
async def main():
# Check that the coro we include is the same coro we get back
print("=" * 10)
coro = action()
t = asyncio.create_task(coro)
print(t.get_coro() == coro)
# Check that the coro prop matches the get_coro() result
print("=" * 10)
t = asyncio.create_task(action())
print(t.get_coro() == t.coro)
asyncio.run(main())

View File

@ -0,0 +1,4 @@
==========
True
==========
True

View File

@ -0,0 +1,39 @@
# Test hash unary operator for a Task
try:
import asyncio
except ImportError:
print("SKIP")
raise SystemExit
async def task():
pass
async def main():
# Confirm that the hash is an int
print("=" * 10)
t1 = asyncio.create_task(task())
t2 = asyncio.create_task(task())
print(type(hash(t2)))
print(type(hash(t1)))
# Check that two tasks don't have the same hash
print("=" * 10)
t1 = asyncio.create_task(task())
t2 = asyncio.create_task(task())
print(hash(t1) != hash(t2))
# Add tasks to a set
print("=" * 10)
t1 = asyncio.create_task(task())
t2 = asyncio.create_task(task())
tasks = set()
tasks.add(t1)
print(t1 in tasks)
print(t2 in tasks)
asyncio.run(main())

View File

@ -0,0 +1,8 @@
==========
<class 'int'>
<class 'int'>
==========
True
==========
True
False

View File

@ -0,0 +1,59 @@
# Test the Task.remove_done_callback() method
try:
import asyncio
except ImportError:
print("SKIP")
raise SystemExit
async def task(t, exc=None):
if t >= 0:
await asyncio.sleep(t)
if exc:
raise exc
def done_callback():
print("done")
def done_callback_2():
print("done 2")
async def main():
# Removing a callback returns 0 when no callbacks have been set
print("=" * 10)
t = asyncio.create_task(task(1))
print("Returns 0 when no done callback has been set:", t.remove_done_callback(done_callback))
# Done callback removal only works once
print("=" * 10)
t = asyncio.create_task(task(1))
t.add_done_callback(done_callback)
print(
"Returns 1 when a callback matches and is removed:", t.remove_done_callback(done_callback)
)
print(
"Returns 0 on second attempt to remove the callback:",
t.remove_done_callback(done_callback),
)
# Only removes done callback when match
print("=" * 10)
t = asyncio.create_task(task(0.01))
t.add_done_callback(done_callback)
print("Returns 0 when done callbacks don't match:", t.remove_done_callback(done_callback_2))
# A removed done callback does not execute
print("=" * 10)
t = asyncio.create_task(task(-1))
t.add_done_callback(done_callback)
t.remove_done_callback(done_callback)
print("Waiting for task to complete")
await t
print("Task completed")
asyncio.run(main())

View File

@ -0,0 +1,10 @@
==========
Returns 0 when no done callback has been set: 0
==========
Returns 1 when a callback matches and is removed: 1
Returns 0 on second attempt to remove the callback: 0
==========
Returns 0 when done callbacks don't match: 0
==========
Waiting for task to complete
Task completed

View File

@ -0,0 +1,69 @@
# Test the Task.result() method
try:
import asyncio
except ImportError:
print("SKIP")
raise SystemExit
async def task(t, exc=None, ret=None):
if t >= 0:
await asyncio.sleep(t)
if exc:
raise exc
return ret
async def main():
# Task that is not done yet raises an InvalidStateError
print("=" * 10)
t = asyncio.create_task(task(1))
await asyncio.sleep(0)
try:
t.result()
assert False, "Should not get here"
except Exception as e:
print("InvalidStateError if still running:", repr(e))
# Task that is cancelled raises CancelledError
print("=" * 10)
t = asyncio.create_task(task(1))
t.cancel()
await asyncio.sleep(0)
try:
t.result()
assert False, "Should not get here"
except asyncio.CancelledError as e:
print("CancelledError when retrieving result from cancelled task:", repr(e))
# Task that raises immediately should raise that exception when calling result
print("=" * 10)
t = asyncio.create_task(task(-1, ValueError))
try:
await t
assert False, "Should not get here"
except ValueError as e:
pass
try:
t.result()
assert False, "Should not get here"
except ValueError as e:
print("Error raised when result is attempted on task with error:", repr(e))
# Task that starts, runs and finishes without an exception or value should return None
print("=" * 10)
t = asyncio.create_task(task(0.01))
await t
print("Empty Result should be None:", t.result())
assert t.result() is None
# Task that starts, runs and finishes without exception should return result
print("=" * 10)
t = asyncio.create_task(task(0.01, None, "hello world"))
await t
print("Happy path, result is returned:", t.result())
asyncio.run(main())

View File

@ -0,0 +1,10 @@
==========
InvalidStateError if still running: InvalidStateError()
==========
CancelledError when retrieving result from cancelled task: CancelledError()
==========
Error raised when result is attempted on task with error: ValueError()
==========
Empty Result should be None: None
==========
Happy path, result is returned: hello world