Skip to content

Commit 921984b

Browse files
committed
tests/extmod: Add asyncio tests for new task features.
Signed-off-by: James Ward <james@notjam.es>
1 parent 15d877f commit 921984b

18 files changed

+483
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Test the Task.add_done_callback() method
2+
3+
try:
4+
import asyncio
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
10+
async def task(t, exc=None):
11+
if t >= 0:
12+
await asyncio.sleep(t)
13+
if exc:
14+
raise exc
15+
16+
17+
def done_callback(t, er):
18+
print("done", repr(t), repr(er))
19+
20+
21+
async def main():
22+
# Tasks that aren't done only execute done callback after finishing
23+
print("=" * 10)
24+
t = asyncio.create_task(task(-1))
25+
t.add_done_callback(done_callback)
26+
print("Waiting for task to complete")
27+
await asyncio.sleep(0)
28+
print("Task has completed")
29+
30+
# Task that are done run the callback immediately
31+
print("=" * 10)
32+
t = asyncio.create_task(task(-1))
33+
await asyncio.sleep(0)
34+
print("Task has completed")
35+
t.add_done_callback(done_callback)
36+
print("Callback Added")
37+
38+
# Task that starts, runs and finishes without an exception should return None
39+
print("=" * 10)
40+
t = asyncio.create_task(task(0.01))
41+
t.add_done_callback(done_callback)
42+
try:
43+
t.add_done_callback(done_callback)
44+
except RuntimeError as e:
45+
print("Second call to add_done_callback emits error:", repr(e))
46+
47+
# Task that raises immediately should still run done callback
48+
print("=" * 10)
49+
t = asyncio.create_task(task(-1, ValueError))
50+
t.add_done_callback(done_callback)
51+
await asyncio.sleep(0)
52+
53+
54+
asyncio.run(main())
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
==========
2+
Waiting for task to complete
3+
done <Task> StopIteration()
4+
Task has completed
5+
==========
6+
Task has completed
7+
done <Task> StopIteration()
8+
Callback Added
9+
==========
10+
Second call to add_done_callback emits error: RuntimeError('Tasks only support one done callback.',)
11+
==========
12+
done <Task> ValueError()
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Test the `Task.cancelled` method
2+
3+
try:
4+
import asyncio
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
10+
async def task(t):
11+
await asyncio.sleep(t)
12+
13+
14+
async def main():
15+
# Cancel task immediately doesn't mark the task as cancelled
16+
print("=" * 10)
17+
t = asyncio.create_task(task(2))
18+
t.cancel()
19+
print("Expecting task to not be cancelled because it is not done:", t.cancelled())
20+
21+
# Cancel task immediately and wait for cancellation to complete
22+
print("=" * 10)
23+
t = asyncio.create_task(task(2))
24+
t.cancel()
25+
await asyncio.sleep(0)
26+
print("Expecting Task to be Cancelled:", t.cancelled())
27+
28+
# Cancel task and wait for cancellation to complete
29+
print("=" * 10)
30+
t = asyncio.create_task(task(2))
31+
await asyncio.sleep(0.01)
32+
t.cancel()
33+
await asyncio.sleep(0)
34+
print("Expecting Task to be Cancelled:", t.cancelled())
35+
36+
# Cancel task multiple times after it has started
37+
print("=" * 10)
38+
t = asyncio.create_task(task(2))
39+
await asyncio.sleep(0.01)
40+
for _ in range(4):
41+
t.cancel()
42+
await asyncio.sleep(0.01)
43+
44+
print("Expecting Task to be Cancelled:", t.cancelled())
45+
46+
# Cancel task after it has finished
47+
print("=" * 10)
48+
t = asyncio.create_task(task(0.01))
49+
await asyncio.sleep(0.05)
50+
t.cancel()
51+
print("Expecting task to not be Cancelled:", t.cancelled())
52+
53+
54+
asyncio.run(main())
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
==========
2+
Expecting task to not be cancelled because it is not done: False
3+
==========
4+
Expecting Task to be Cancelled: True
5+
==========
6+
Expecting Task to be Cancelled: True
7+
==========
8+
Expecting Task to be Cancelled: True
9+
==========
10+
Expecting task to not be Cancelled: False
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Test the Task.exception() method
2+
3+
try:
4+
import asyncio
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
10+
async def task(t, exc=None):
11+
if t >= 0:
12+
await asyncio.sleep(t)
13+
if exc:
14+
raise exc
15+
16+
17+
async def main():
18+
# Task that is not done yet raises an InvalidStateError
19+
print("=" * 10)
20+
t = asyncio.create_task(task(1))
21+
await asyncio.sleep(0)
22+
try:
23+
t.exception()
24+
assert False, "Should not get here"
25+
except Exception as e:
26+
print("Tasks that aren't done yet raise an InvalidStateError:", repr(e))
27+
28+
# Task that is cancelled raises CancelledError
29+
print("=" * 10)
30+
t = asyncio.create_task(task(1))
31+
t.cancel()
32+
await asyncio.sleep(0)
33+
try:
34+
print(repr(t.exception()))
35+
print(t.cancelled())
36+
assert False, "Should not get here"
37+
except asyncio.CancelledError as e:
38+
print("Cancelled tasks cannot retrieve exception:", repr(e))
39+
40+
# Task that starts, runs and finishes without an exception should return None
41+
print("=" * 10)
42+
t = asyncio.create_task(task(0.01))
43+
await t
44+
print("None when no exception:", t.exception())
45+
46+
# Task that raises immediately should return that exception
47+
print("=" * 10)
48+
t = asyncio.create_task(task(-1, ValueError))
49+
try:
50+
await t
51+
assert False, "Should not get here"
52+
except ValueError as e:
53+
pass
54+
print("Returned Exception:", repr(t.exception()))
55+
56+
# Task returns `none` when somehow an exception isn't in data
57+
print("=" * 10)
58+
t = asyncio.create_task(task(-1))
59+
await t
60+
t.data = "Example"
61+
print(t.exception())
62+
63+
64+
asyncio.run(main())
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
==========
2+
Tasks that aren't done yet raise an InvalidStateError: InvalidStateError()
3+
==========
4+
Cancelled tasks cannot retrieve exception: CancelledError()
5+
==========
6+
None when no exception: None
7+
==========
8+
Returned Exception: ValueError()
9+
==========
10+
None

tests/extmod/asyncio_task_get_coro.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Test the `Task.get_coro()` method
2+
3+
try:
4+
import asyncio
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
10+
async def action():
11+
pass
12+
13+
14+
async def main():
15+
# Check that the coro we include is the same coro we get back
16+
print("=" * 10)
17+
18+
coro = action()
19+
t = asyncio.create_task(coro)
20+
print(t.get_coro() == coro)
21+
22+
# Check that the coro prop matches the get_coro() result
23+
print("=" * 10)
24+
t = asyncio.create_task(action())
25+
print(t.get_coro() == t.coro)
26+
27+
28+
asyncio.run(main())
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
==========
2+
True
3+
==========
4+
True

tests/extmod/asyncio_task_hash.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Test hash unary operator for a Task
2+
3+
try:
4+
import asyncio
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
10+
async def task():
11+
pass
12+
13+
14+
async def main():
15+
# Confirm that the hash is an int
16+
print("=" * 10)
17+
t1 = asyncio.create_task(task())
18+
t2 = asyncio.create_task(task())
19+
print(type(hash(t2)))
20+
print(type(hash(t1)))
21+
22+
# Check that two tasks don't have the same hash
23+
print("=" * 10)
24+
t1 = asyncio.create_task(task())
25+
t2 = asyncio.create_task(task())
26+
print(hash(t1) != hash(t2))
27+
28+
# Add tasks to a set
29+
print("=" * 10)
30+
t1 = asyncio.create_task(task())
31+
t2 = asyncio.create_task(task())
32+
33+
tasks = set()
34+
tasks.add(t1)
35+
print(t1 in tasks)
36+
print(t2 in tasks)
37+
38+
39+
asyncio.run(main())

tests/extmod/asyncio_task_hash.py.exp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
==========
2+
<class 'int'>
3+
<class 'int'>
4+
==========
5+
True
6+
==========
7+
True
8+
False

0 commit comments

Comments
 (0)