From c4935f30490d0446e16a51dbf7a6397b771cf804 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Nov 2019 21:08:22 +1100 Subject: [PATCH] tests/extmod: Add uasyncio tests. All .exp files are included because they require CPython 3.8 which may not always be available. --- tests/extmod/uasyncio_await_return.py | 26 ++++++ tests/extmod/uasyncio_await_return.py.exp | 2 + tests/extmod/uasyncio_basic.py | 43 ++++++++++ tests/extmod/uasyncio_basic.py.exp | 5 ++ tests/extmod/uasyncio_basic2.py | 24 ++++++ tests/extmod/uasyncio_basic2.py.exp | 4 + tests/extmod/uasyncio_cancel_fair.py | 37 +++++++++ tests/extmod/uasyncio_cancel_fair.py.exp | 24 ++++++ tests/extmod/uasyncio_cancel_fair2.py | 37 +++++++++ tests/extmod/uasyncio_cancel_fair2.py.exp | 8 ++ tests/extmod/uasyncio_cancel_self.py | 31 +++++++ tests/extmod/uasyncio_cancel_self.py.exp | 2 + tests/extmod/uasyncio_cancel_task.py | 85 ++++++++++++++++++++ tests/extmod/uasyncio_cancel_task.py.exp | 31 +++++++ tests/extmod/uasyncio_event.py | 98 +++++++++++++++++++++++ tests/extmod/uasyncio_event.py.exp | 33 ++++++++ tests/extmod/uasyncio_event_fair.py | 40 +++++++++ tests/extmod/uasyncio_event_fair.py.exp | 16 ++++ tests/extmod/uasyncio_exception.py | 60 ++++++++++++++ tests/extmod/uasyncio_exception.py.exp | 7 ++ tests/extmod/uasyncio_fair.py | 32 ++++++++ tests/extmod/uasyncio_fair.py.exp | 12 +++ tests/extmod/uasyncio_gather.py | 49 ++++++++++++ tests/extmod/uasyncio_gather.py.exp | 10 +++ tests/extmod/uasyncio_get_event_loop.py | 20 +++++ tests/extmod/uasyncio_heaplock.py | 46 +++++++++++ tests/extmod/uasyncio_heaplock.py.exp | 11 +++ tests/extmod/uasyncio_lock.py | 97 ++++++++++++++++++++++ tests/extmod/uasyncio_lock.py.exp | 41 ++++++++++ tests/extmod/uasyncio_lock_cancel.py | 55 +++++++++++++ tests/extmod/uasyncio_lock_cancel.py.exp | 11 +++ tests/extmod/uasyncio_wait_for.py | 62 ++++++++++++++ tests/extmod/uasyncio_wait_for.py.exp | 15 ++++ tests/extmod/uasyncio_wait_task.py | 77 ++++++++++++++++++ tests/extmod/uasyncio_wait_task.py.exp | 10 +++ 35 files changed, 1161 insertions(+) create mode 100644 tests/extmod/uasyncio_await_return.py create mode 100644 tests/extmod/uasyncio_await_return.py.exp create mode 100644 tests/extmod/uasyncio_basic.py create mode 100644 tests/extmod/uasyncio_basic.py.exp create mode 100644 tests/extmod/uasyncio_basic2.py create mode 100644 tests/extmod/uasyncio_basic2.py.exp create mode 100644 tests/extmod/uasyncio_cancel_fair.py create mode 100644 tests/extmod/uasyncio_cancel_fair.py.exp create mode 100644 tests/extmod/uasyncio_cancel_fair2.py create mode 100644 tests/extmod/uasyncio_cancel_fair2.py.exp create mode 100644 tests/extmod/uasyncio_cancel_self.py create mode 100644 tests/extmod/uasyncio_cancel_self.py.exp create mode 100644 tests/extmod/uasyncio_cancel_task.py create mode 100644 tests/extmod/uasyncio_cancel_task.py.exp create mode 100644 tests/extmod/uasyncio_event.py create mode 100644 tests/extmod/uasyncio_event.py.exp create mode 100644 tests/extmod/uasyncio_event_fair.py create mode 100644 tests/extmod/uasyncio_event_fair.py.exp create mode 100644 tests/extmod/uasyncio_exception.py create mode 100644 tests/extmod/uasyncio_exception.py.exp create mode 100644 tests/extmod/uasyncio_fair.py create mode 100644 tests/extmod/uasyncio_fair.py.exp create mode 100644 tests/extmod/uasyncio_gather.py create mode 100644 tests/extmod/uasyncio_gather.py.exp create mode 100644 tests/extmod/uasyncio_get_event_loop.py create mode 100644 tests/extmod/uasyncio_heaplock.py create mode 100644 tests/extmod/uasyncio_heaplock.py.exp create mode 100644 tests/extmod/uasyncio_lock.py create mode 100644 tests/extmod/uasyncio_lock.py.exp create mode 100644 tests/extmod/uasyncio_lock_cancel.py create mode 100644 tests/extmod/uasyncio_lock_cancel.py.exp create mode 100644 tests/extmod/uasyncio_wait_for.py create mode 100644 tests/extmod/uasyncio_wait_for.py.exp create mode 100644 tests/extmod/uasyncio_wait_task.py create mode 100644 tests/extmod/uasyncio_wait_task.py.exp diff --git a/tests/extmod/uasyncio_await_return.py b/tests/extmod/uasyncio_await_return.py new file mode 100644 index 0000000000..d375c9ea97 --- /dev/null +++ b/tests/extmod/uasyncio_await_return.py @@ -0,0 +1,26 @@ +# Test that tasks return their value correctly to the caller + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def foo(): + return 42 + + +async def main(): + # Call function directly via an await + print(await foo()) + + # Create a task and await on it + task = asyncio.create_task(foo()) + print(await task) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_await_return.py.exp b/tests/extmod/uasyncio_await_return.py.exp new file mode 100644 index 0000000000..daaac9e303 --- /dev/null +++ b/tests/extmod/uasyncio_await_return.py.exp @@ -0,0 +1,2 @@ +42 +42 diff --git a/tests/extmod/uasyncio_basic.py b/tests/extmod/uasyncio_basic.py new file mode 100644 index 0000000000..f6685fa674 --- /dev/null +++ b/tests/extmod/uasyncio_basic.py @@ -0,0 +1,43 @@ +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +try: + import utime + + ticks = utime.ticks_ms + ticks_diff = utime.ticks_diff +except: + import time + + ticks = lambda: int(time.time() * 1000) + ticks_diff = lambda t1, t0: t1 - t0 + + +async def delay_print(t, s): + await asyncio.sleep(t) + print(s) + + +async def main(): + print("start") + + await asyncio.sleep(0.001) + print("after sleep") + + t0 = ticks() + await delay_print(0.02, "short") + t1 = ticks() + await delay_print(0.04, "long") + t2 = ticks() + + print("took {} {}".format(round(ticks_diff(t1, t0), -1), round(ticks_diff(t2, t1), -1))) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_basic.py.exp b/tests/extmod/uasyncio_basic.py.exp new file mode 100644 index 0000000000..447d05d5e0 --- /dev/null +++ b/tests/extmod/uasyncio_basic.py.exp @@ -0,0 +1,5 @@ +start +after sleep +short +long +took 20 40 diff --git a/tests/extmod/uasyncio_basic2.py b/tests/extmod/uasyncio_basic2.py new file mode 100644 index 0000000000..a2167e48ee --- /dev/null +++ b/tests/extmod/uasyncio_basic2.py @@ -0,0 +1,24 @@ +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def forever(): + print("forever start") + await asyncio.sleep(10) + + +async def main(): + print("main start") + asyncio.create_task(forever()) + await asyncio.sleep(0.001) + print("main done") + return 42 + + +print(asyncio.run(main())) diff --git a/tests/extmod/uasyncio_basic2.py.exp b/tests/extmod/uasyncio_basic2.py.exp new file mode 100644 index 0000000000..3ca3521728 --- /dev/null +++ b/tests/extmod/uasyncio_basic2.py.exp @@ -0,0 +1,4 @@ +main start +forever start +main done +42 diff --git a/tests/extmod/uasyncio_cancel_fair.py b/tests/extmod/uasyncio_cancel_fair.py new file mode 100644 index 0000000000..9a7b35c161 --- /dev/null +++ b/tests/extmod/uasyncio_cancel_fair.py @@ -0,0 +1,37 @@ +# Test fairness of cancelling a task +# That tasks which continuously cancel each other don't take over the scheduler + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(id, other): + for i in range(3): + try: + print("start", id) + await asyncio.sleep(0) + print("done", id) + except asyncio.CancelledError as er: + print("cancelled", id) + if other is not None: + print(id, "cancels", other) + tasks[other].cancel() + + +async def main(): + global tasks + tasks = [ + asyncio.create_task(task(0, 1)), + asyncio.create_task(task(1, 0)), + asyncio.create_task(task(2, None)), + ] + await tasks[2] + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_cancel_fair.py.exp b/tests/extmod/uasyncio_cancel_fair.py.exp new file mode 100644 index 0000000000..8f5da08e4c --- /dev/null +++ b/tests/extmod/uasyncio_cancel_fair.py.exp @@ -0,0 +1,24 @@ +start 0 +start 1 +start 2 +done 0 +0 cancels 1 +start 0 +cancelled 1 +1 cancels 0 +start 1 +done 2 +start 2 +cancelled 0 +0 cancels 1 +start 0 +cancelled 1 +1 cancels 0 +start 1 +done 2 +start 2 +cancelled 0 +0 cancels 1 +cancelled 1 +1 cancels 0 +done 2 diff --git a/tests/extmod/uasyncio_cancel_fair2.py b/tests/extmod/uasyncio_cancel_fair2.py new file mode 100644 index 0000000000..46e40f71b1 --- /dev/null +++ b/tests/extmod/uasyncio_cancel_fair2.py @@ -0,0 +1,37 @@ +# Test fairness of cancelling a task +# That tasks which keeps being cancelled by multiple other tasks gets a chance to run + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task_a(): + try: + while True: + print("sleep a") + await asyncio.sleep(0) + except asyncio.CancelledError: + print("cancelled a") + + +async def task_b(id, other): + while other.cancel(): + print("sleep b", id) + await asyncio.sleep(0) + print("done b", id) + + +async def main(): + t = asyncio.create_task(task_a()) + for i in range(3): + asyncio.create_task(task_b(i, t)) + await t + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_cancel_fair2.py.exp b/tests/extmod/uasyncio_cancel_fair2.py.exp new file mode 100644 index 0000000000..e9dd649b45 --- /dev/null +++ b/tests/extmod/uasyncio_cancel_fair2.py.exp @@ -0,0 +1,8 @@ +sleep a +sleep b 0 +sleep b 1 +sleep b 2 +cancelled a +done b 0 +done b 1 +done b 2 diff --git a/tests/extmod/uasyncio_cancel_self.py b/tests/extmod/uasyncio_cancel_self.py new file mode 100644 index 0000000000..660ae66389 --- /dev/null +++ b/tests/extmod/uasyncio_cancel_self.py @@ -0,0 +1,31 @@ +# Test a task cancelling itself (currently unsupported) + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(): + print("task start") + global_task.cancel() + + +async def main(): + global global_task + global_task = asyncio.create_task(task()) + try: + await global_task + except asyncio.CancelledError: + print("main cancel") + print("main done") + + +try: + asyncio.run(main()) +except RuntimeError as er: + print(er) diff --git a/tests/extmod/uasyncio_cancel_self.py.exp b/tests/extmod/uasyncio_cancel_self.py.exp new file mode 100644 index 0000000000..36fcb0a3f4 --- /dev/null +++ b/tests/extmod/uasyncio_cancel_self.py.exp @@ -0,0 +1,2 @@ +task start +cannot cancel self diff --git a/tests/extmod/uasyncio_cancel_task.py b/tests/extmod/uasyncio_cancel_task.py new file mode 100644 index 0000000000..ec60d85545 --- /dev/null +++ b/tests/extmod/uasyncio_cancel_task.py @@ -0,0 +1,85 @@ +# Test cancelling a task + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(s, allow_cancel): + try: + print("task start") + await asyncio.sleep(s) + print("task done") + except asyncio.CancelledError as er: + print("task cancel") + if allow_cancel: + raise er + + +async def task2(allow_cancel): + print("task 2") + try: + await asyncio.create_task(task(0.05, allow_cancel)) + except asyncio.CancelledError as er: + print("task 2 cancel") + raise er + print("task 2 done") + + +async def main(): + # Cancel task immediately + t = asyncio.create_task(task(2, True)) + print(t.cancel()) + + # Cancel task after it has started + t = asyncio.create_task(task(2, True)) + await asyncio.sleep(0.01) + print(t.cancel()) + print("main sleep") + await asyncio.sleep(0.01) + + # Cancel task multiple times after it has started + t = asyncio.create_task(task(2, True)) + await asyncio.sleep(0.01) + for _ in range(4): + print(t.cancel()) + print("main sleep") + await asyncio.sleep(0.01) + + # Await on a cancelled task + print("main wait") + try: + await t + except asyncio.CancelledError: + print("main got CancelledError") + + # Cancel task after it has finished + t = asyncio.create_task(task(0.01, False)) + await asyncio.sleep(0.05) + print(t.cancel()) + + # Nested: task2 waits on task, task2 is cancelled (should cancel task then task2) + print("----") + t = asyncio.create_task(task2(True)) + await asyncio.sleep(0.01) + print("main cancel") + t.cancel() + print("main sleep") + await asyncio.sleep(0.1) + + # Nested: task2 waits on task, task2 is cancelled but task doesn't allow it (task2 should continue) + print("----") + t = asyncio.create_task(task2(False)) + await asyncio.sleep(0.01) + print("main cancel") + t.cancel() + print("main sleep") + await asyncio.sleep(0.1) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_cancel_task.py.exp b/tests/extmod/uasyncio_cancel_task.py.exp new file mode 100644 index 0000000000..031b94023f --- /dev/null +++ b/tests/extmod/uasyncio_cancel_task.py.exp @@ -0,0 +1,31 @@ +True +task start +True +main sleep +task cancel +task start +True +True +True +True +main sleep +task cancel +main wait +main got CancelledError +task start +task done +False +---- +task 2 +task start +main cancel +main sleep +task cancel +task 2 cancel +---- +task 2 +task start +main cancel +main sleep +task cancel +task 2 done diff --git a/tests/extmod/uasyncio_event.py b/tests/extmod/uasyncio_event.py new file mode 100644 index 0000000000..fb8eb9ffa4 --- /dev/null +++ b/tests/extmod/uasyncio_event.py @@ -0,0 +1,98 @@ +# Test Event class + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(id, ev): + print("start", id) + print(await ev.wait()) + print("end", id) + + +async def task_delay_set(t, ev): + await asyncio.sleep(t) + print("set event") + ev.set() + + +async def main(): + ev = asyncio.Event() + + # Set and clear without anything waiting, and test is_set() + print(ev.is_set()) + ev.set() + print(ev.is_set()) + ev.clear() + print(ev.is_set()) + + # Create 2 tasks waiting on the event + print("----") + asyncio.create_task(task(1, ev)) + asyncio.create_task(task(2, ev)) + print("yield") + await asyncio.sleep(0) + print("set event") + ev.set() + print("yield") + await asyncio.sleep(0) + + # Create a task waiting on the already-set event + print("----") + asyncio.create_task(task(3, ev)) + print("yield") + await asyncio.sleep(0) + + # Clear event, start a task, then set event again + print("----") + print("clear event") + ev.clear() + asyncio.create_task(task(4, ev)) + await asyncio.sleep(0) + print("set event") + ev.set() + await asyncio.sleep(0) + + # Cancel a task waiting on an event (set event then cancel task) + print("----") + ev = asyncio.Event() + t = asyncio.create_task(task(5, ev)) + await asyncio.sleep(0) + ev.set() + t.cancel() + await asyncio.sleep(0.1) + + # Cancel a task waiting on an event (cancel task then set event) + print("----") + ev = asyncio.Event() + t = asyncio.create_task(task(6, ev)) + await asyncio.sleep(0) + t.cancel() + ev.set() + await asyncio.sleep(0.1) + + # Wait for an event that does get set in time + print("----") + ev.clear() + asyncio.create_task(task_delay_set(0.01, ev)) + await asyncio.wait_for(ev.wait(), 0.1) + await asyncio.sleep(0) + + # Wait for an event that doesn't get set in time + print("----") + ev.clear() + asyncio.create_task(task_delay_set(0.1, ev)) + try: + await asyncio.wait_for(ev.wait(), 0.01) + except asyncio.TimeoutError: + print("TimeoutError") + await ev.wait() + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_event.py.exp b/tests/extmod/uasyncio_event.py.exp new file mode 100644 index 0000000000..3188f291e5 --- /dev/null +++ b/tests/extmod/uasyncio_event.py.exp @@ -0,0 +1,33 @@ +False +True +False +---- +yield +start 1 +start 2 +set event +yield +True +end 1 +True +end 2 +---- +yield +start 3 +True +end 3 +---- +clear event +start 4 +set event +True +end 4 +---- +start 5 +---- +start 6 +---- +set event +---- +TimeoutError +set event diff --git a/tests/extmod/uasyncio_event_fair.py b/tests/extmod/uasyncio_event_fair.py new file mode 100644 index 0000000000..37eca5faef --- /dev/null +++ b/tests/extmod/uasyncio_event_fair.py @@ -0,0 +1,40 @@ +# Test fairness of Event.set() +# That tasks which continuously wait on events don't take over the scheduler + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task1(id): + for i in range(4): + print("sleep", id) + await asyncio.sleep(0) + + +async def task2(id, ev): + for i in range(4): + ev.set() + ev.clear() + print("wait", id) + await ev.wait() + + +async def main(): + ev = asyncio.Event() + tasks = [ + asyncio.create_task(task1(0)), + asyncio.create_task(task2(2, ev)), + asyncio.create_task(task1(1)), + asyncio.create_task(task2(3, ev)), + ] + await tasks[1] + ev.set() + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_event_fair.py.exp b/tests/extmod/uasyncio_event_fair.py.exp new file mode 100644 index 0000000000..fe2e3d6e47 --- /dev/null +++ b/tests/extmod/uasyncio_event_fair.py.exp @@ -0,0 +1,16 @@ +sleep 0 +wait 2 +sleep 1 +wait 3 +sleep 0 +sleep 1 +wait 2 +sleep 0 +sleep 1 +wait 3 +sleep 0 +sleep 1 +wait 2 +wait 3 +wait 2 +wait 3 diff --git a/tests/extmod/uasyncio_exception.py b/tests/extmod/uasyncio_exception.py new file mode 100644 index 0000000000..aae55d6320 --- /dev/null +++ b/tests/extmod/uasyncio_exception.py @@ -0,0 +1,60 @@ +# Test general exception handling + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + +# main task raising an exception +async def main(): + print("main start") + raise ValueError(1) + print("main done") + + +try: + asyncio.run(main()) +except ValueError as er: + print("ValueError", er.args[0]) + +# sub-task raising an exception +async def task(): + print("task start") + raise ValueError(2) + print("task done") + + +async def main(): + print("main start") + t = asyncio.create_task(task()) + await t + print("main done") + + +try: + asyncio.run(main()) +except ValueError as er: + print("ValueError", er.args[0]) + +# main task raising an exception with sub-task not yet scheduled +# TODO not currently working, task is never scheduled +async def task(): + # print('task run') uncomment this line when it works + pass + + +async def main(): + print("main start") + asyncio.create_task(task()) + raise ValueError(3) + print("main done") + + +try: + asyncio.run(main()) +except ValueError as er: + print("ValueError", er.args[0]) diff --git a/tests/extmod/uasyncio_exception.py.exp b/tests/extmod/uasyncio_exception.py.exp new file mode 100644 index 0000000000..b2ee860170 --- /dev/null +++ b/tests/extmod/uasyncio_exception.py.exp @@ -0,0 +1,7 @@ +main start +ValueError 1 +main start +task start +ValueError 2 +main start +ValueError 3 diff --git a/tests/extmod/uasyncio_fair.py b/tests/extmod/uasyncio_fair.py new file mode 100644 index 0000000000..9b04454bc1 --- /dev/null +++ b/tests/extmod/uasyncio_fair.py @@ -0,0 +1,32 @@ +# Test fairness of scheduler + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(id, t): + print("task start", id) + while True: + if t > 0: + print("task work", id) + await asyncio.sleep(t) + + +async def main(): + t1 = asyncio.create_task(task(1, -0.01)) + t2 = asyncio.create_task(task(2, 0.1)) + t3 = asyncio.create_task(task(3, 0.2)) + await asyncio.sleep(0.5) + t1.cancel() + t2.cancel() + t3.cancel() + print("finish") + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_fair.py.exp b/tests/extmod/uasyncio_fair.py.exp new file mode 100644 index 0000000000..4428943f46 --- /dev/null +++ b/tests/extmod/uasyncio_fair.py.exp @@ -0,0 +1,12 @@ +task start 1 +task start 2 +task work 2 +task start 3 +task work 3 +task work 2 +task work 3 +task work 2 +task work 2 +task work 3 +task work 2 +finish diff --git a/tests/extmod/uasyncio_gather.py b/tests/extmod/uasyncio_gather.py new file mode 100644 index 0000000000..2697a6278b --- /dev/null +++ b/tests/extmod/uasyncio_gather.py @@ -0,0 +1,49 @@ +# test uasyncio.gather() function + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def factorial(name, number): + f = 1 + for i in range(2, number + 1): + print("Task {}: Compute factorial({})...".format(name, i)) + await asyncio.sleep(0.01) + f *= i + print("Task {}: factorial({}) = {}".format(name, number, f)) + return f + + +async def task(id): + print("start", id) + await asyncio.sleep(0.2) + print("end", id) + + +async def gather_task(): + print("gather_task") + await asyncio.gather(task(1), task(2)) + print("gather_task2") + + +async def main(): + # Simple gather with return values + print(await asyncio.gather(factorial("A", 2), factorial("B", 3), factorial("C", 4),)) + + # Cancel a multi gather + # TODO doesn't work, Task should not forward cancellation from gather to sub-task + # but rather CancelledError should cancel the gather directly, which will then cancel + # all sub-tasks explicitly + # t = asyncio.create_task(gather_task()) + # await asyncio.sleep(0.1) + # t.cancel() + # await asyncio.sleep(0.01) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_gather.py.exp b/tests/extmod/uasyncio_gather.py.exp new file mode 100644 index 0000000000..a37578d7eb --- /dev/null +++ b/tests/extmod/uasyncio_gather.py.exp @@ -0,0 +1,10 @@ +Task A: Compute factorial(2)... +Task B: Compute factorial(2)... +Task C: Compute factorial(2)... +Task A: factorial(2) = 2 +Task B: Compute factorial(3)... +Task C: Compute factorial(3)... +Task B: factorial(3) = 6 +Task C: Compute factorial(4)... +Task C: factorial(4) = 24 +[2, 6, 24] diff --git a/tests/extmod/uasyncio_get_event_loop.py b/tests/extmod/uasyncio_get_event_loop.py new file mode 100644 index 0000000000..8ccbd6814e --- /dev/null +++ b/tests/extmod/uasyncio_get_event_loop.py @@ -0,0 +1,20 @@ +# Test get_event_loop() + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def main(): + print("start") + await asyncio.sleep(0.01) + print("end") + + +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) diff --git a/tests/extmod/uasyncio_heaplock.py b/tests/extmod/uasyncio_heaplock.py new file mode 100644 index 0000000000..771d3f0d97 --- /dev/null +++ b/tests/extmod/uasyncio_heaplock.py @@ -0,0 +1,46 @@ +# test that basic scheduling of tasks, and uasyncio.sleep_ms, does not use the heap + +import micropython + +# strict stackless builds can't call functions without allocating a frame on the heap +try: + f = lambda: 0 + micropython.heap_lock() + f() + micropython.heap_unlock() +except RuntimeError: + print("SKIP") + raise SystemExit + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(id, n, t): + for i in range(n): + print(id, i) + await asyncio.sleep_ms(t) + + +async def main(): + t1 = asyncio.create_task(task(1, 4, 10)) + t2 = asyncio.create_task(task(2, 4, 25)) + + micropython.heap_lock() + + print("start") + await asyncio.sleep_ms(1) + print("sleep") + await asyncio.sleep_ms(100) + print("finish") + + micropython.heap_unlock() + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_heaplock.py.exp b/tests/extmod/uasyncio_heaplock.py.exp new file mode 100644 index 0000000000..a967cc3197 --- /dev/null +++ b/tests/extmod/uasyncio_heaplock.py.exp @@ -0,0 +1,11 @@ +start +1 0 +2 0 +sleep +1 1 +1 2 +2 1 +1 3 +2 2 +2 3 +finish diff --git a/tests/extmod/uasyncio_lock.py b/tests/extmod/uasyncio_lock.py new file mode 100644 index 0000000000..096a8c82be --- /dev/null +++ b/tests/extmod/uasyncio_lock.py @@ -0,0 +1,97 @@ +# Test Lock class + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task_loop(id, lock): + print("task start", id) + for i in range(3): + async with lock: + print("task have", id, i) + print("task end", id) + + +async def task_sleep(lock): + async with lock: + print("task have", lock.locked()) + await asyncio.sleep(0.2) + print("task release", lock.locked()) + await lock.acquire() + print("task have again") + lock.release() + + +async def task_cancel(id, lock, to_cancel=None): + try: + async with lock: + print("task got", id) + await asyncio.sleep(0.1) + print("task release", id) + if to_cancel: + to_cancel[0].cancel() + except asyncio.CancelledError: + print("task cancel", id) + + +async def main(): + lock = asyncio.Lock() + + # Basic acquire/release + print(lock.locked()) + await lock.acquire() + print(lock.locked()) + await asyncio.sleep(0) + lock.release() + print(lock.locked()) + await asyncio.sleep(0) + + # Use with "async with" + async with lock: + print("have lock") + + # 3 tasks wanting the lock + print("----") + asyncio.create_task(task_loop(1, lock)) + asyncio.create_task(task_loop(2, lock)) + t3 = asyncio.create_task(task_loop(3, lock)) + await lock.acquire() + await asyncio.sleep(0) + lock.release() + await t3 + + # 2 sleeping tasks both wanting the lock + print("----") + asyncio.create_task(task_sleep(lock)) + await asyncio.sleep(0.1) + await task_sleep(lock) + + # 3 tasks, the first cancelling the second, the third should still run + print("----") + ts = [None] + asyncio.create_task(task_cancel(0, lock, ts)) + ts[0] = asyncio.create_task(task_cancel(1, lock)) + asyncio.create_task(task_cancel(2, lock)) + await asyncio.sleep(0.3) + print(lock.locked()) + + # 3 tasks, the second and third being cancelled while waiting on the lock + print("----") + t0 = asyncio.create_task(task_cancel(0, lock)) + t1 = asyncio.create_task(task_cancel(1, lock)) + t2 = asyncio.create_task(task_cancel(2, lock)) + await asyncio.sleep(0.05) + t1.cancel() + await asyncio.sleep(0.1) + t2.cancel() + await asyncio.sleep(0.1) + print(lock.locked()) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_lock.py.exp b/tests/extmod/uasyncio_lock.py.exp new file mode 100644 index 0000000000..a37dfcbd2e --- /dev/null +++ b/tests/extmod/uasyncio_lock.py.exp @@ -0,0 +1,41 @@ +False +True +False +have lock +---- +task start 1 +task start 2 +task start 3 +task have 1 0 +task have 2 0 +task have 3 0 +task have 1 1 +task have 2 1 +task have 3 1 +task have 1 2 +task end 1 +task have 2 2 +task end 2 +task have 3 2 +task end 3 +---- +task have True +task release False +task have True +task release False +task have again +task have again +---- +task got 0 +task release 0 +task cancel 1 +task got 2 +task release 2 +False +---- +task got 0 +task cancel 1 +task release 0 +task got 2 +task cancel 2 +False diff --git a/tests/extmod/uasyncio_lock_cancel.py b/tests/extmod/uasyncio_lock_cancel.py new file mode 100644 index 0000000000..85b8df8483 --- /dev/null +++ b/tests/extmod/uasyncio_lock_cancel.py @@ -0,0 +1,55 @@ +# Test that locks work when cancelling multiple waiters on the lock + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(i, lock, lock_flag): + print("task", i, "start") + try: + await lock.acquire() + except asyncio.CancelledError: + print("task", i, "cancel") + return + print("task", i, "lock_flag", lock_flag[0]) + lock_flag[0] = True + await asyncio.sleep(0) + lock.release() + lock_flag[0] = False + print("task", i, "done") + + +async def main(): + # Create a lock and acquire it so the tasks below must wait + lock = asyncio.Lock() + await lock.acquire() + lock_flag = [True] + + # Create 4 tasks and let them all run + t0 = asyncio.create_task(task(0, lock, lock_flag)) + t1 = asyncio.create_task(task(1, lock, lock_flag)) + t2 = asyncio.create_task(task(2, lock, lock_flag)) + t3 = asyncio.create_task(task(3, lock, lock_flag)) + await asyncio.sleep(0) + + # Cancel 2 of the tasks (which are waiting on the lock) and release the lock + t1.cancel() + t2.cancel() + lock.release() + lock_flag[0] = False + + # Let the tasks run to completion + for _ in range(4): + await asyncio.sleep(0) + + # The locke should be unlocked + print(lock.locked()) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_lock_cancel.py.exp b/tests/extmod/uasyncio_lock_cancel.py.exp new file mode 100644 index 0000000000..49ae253887 --- /dev/null +++ b/tests/extmod/uasyncio_lock_cancel.py.exp @@ -0,0 +1,11 @@ +task 0 start +task 1 start +task 2 start +task 3 start +task 1 cancel +task 2 cancel +task 0 lock_flag False +task 0 done +task 3 lock_flag False +task 3 done +False diff --git a/tests/extmod/uasyncio_wait_for.py b/tests/extmod/uasyncio_wait_for.py new file mode 100644 index 0000000000..92fd174b84 --- /dev/null +++ b/tests/extmod/uasyncio_wait_for.py @@ -0,0 +1,62 @@ +# Test asyncio.wait_for + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(id, t): + print("task start", id) + await asyncio.sleep(t) + print("task end", id) + return id * 2 + + +async def task_catch(): + print("task_catch start") + try: + await asyncio.sleep(0.2) + except asyncio.CancelledError: + print("ignore cancel") + print("task_catch done") + + +async def task_raise(): + print("task start") + raise ValueError + + +async def main(): + # When task finished before the timeout + print(await asyncio.wait_for(task(1, 0.01), 10)) + + # When timeout passes and task is cancelled + try: + print(await asyncio.wait_for(task(2, 10), 0.01)) + except asyncio.TimeoutError: + print("timeout") + + # When timeout passes and task is cancelled, but task ignores the cancellation request + try: + print(await asyncio.wait_for(task_catch(), 0.1)) + except asyncio.TimeoutError: + print("TimeoutError") + + # When task raises an exception + try: + print(await asyncio.wait_for(task_raise(), 1)) + except ValueError: + print("ValueError") + + # Timeout of None means wait forever + print(await asyncio.wait_for(task(3, 0.1), None)) + + print("finish") + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_wait_for.py.exp b/tests/extmod/uasyncio_wait_for.py.exp new file mode 100644 index 0000000000..41a5f2481e --- /dev/null +++ b/tests/extmod/uasyncio_wait_for.py.exp @@ -0,0 +1,15 @@ +task start 1 +task end 1 +2 +task start 2 +timeout +task_catch start +ignore cancel +task_catch done +TimeoutError +task start +ValueError +task start 3 +task end 3 +6 +finish diff --git a/tests/extmod/uasyncio_wait_task.py b/tests/extmod/uasyncio_wait_task.py new file mode 100644 index 0000000000..3c79320c9f --- /dev/null +++ b/tests/extmod/uasyncio_wait_task.py @@ -0,0 +1,77 @@ +# Test waiting on a task + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +try: + import utime + + ticks = utime.ticks_ms + ticks_diff = utime.ticks_diff +except: + import time + + ticks = lambda: int(time.time() * 1000) + ticks_diff = lambda t1, t0: t1 - t0 + + +async def task(t): + print("task", t) + + +async def delay_print(t, s): + await asyncio.sleep(t) + print(s) + + +async def task_raise(): + print("task_raise") + raise ValueError + + +async def main(): + print("start") + + # Wait on a task + t = asyncio.create_task(task(1)) + await t + + # Wait on a task that's already done + t = asyncio.create_task(task(2)) + await asyncio.sleep(0.001) + await t + + # Wait again on same task + await t + + print("----") + + # Create 2 tasks + ts1 = asyncio.create_task(delay_print(0.04, "hello")) + ts2 = asyncio.create_task(delay_print(0.08, "world")) + + # Time how long the tasks take to finish, they should execute in parallel + print("start") + t0 = ticks() + await ts1 + t1 = ticks() + await ts2 + t2 = ticks() + print("took {} {}".format(round(ticks_diff(t1, t0), -1), round(ticks_diff(t2, t1), -1))) + + # Wait on a task that raises an exception + t = asyncio.create_task(task_raise()) + try: + await t + except ValueError: + print("ValueError") + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_wait_task.py.exp b/tests/extmod/uasyncio_wait_task.py.exp new file mode 100644 index 0000000000..ee4e70fb4e --- /dev/null +++ b/tests/extmod/uasyncio_wait_task.py.exp @@ -0,0 +1,10 @@ +start +task 1 +task 2 +---- +start +hello +world +took 40 40 +task_raise +ValueError