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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy