From fd37c4cd21f34b4d615378333a9df70063b9945e Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 20 Feb 2023 00:35:25 +0100 Subject: [PATCH 1/2] add asynctest base class with awaitEqual and selftest --- tests/lib/asynctest.py | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/lib/asynctest.py diff --git a/tests/lib/asynctest.py b/tests/lib/asynctest.py new file mode 100644 index 000000000..74832737b --- /dev/null +++ b/tests/lib/asynctest.py @@ -0,0 +1,55 @@ +import time +import unittest +from unittest.async_case import TestCase, IsolatedAsyncioTestCase +import asyncio + + +class AsyncTestBase(IsolatedAsyncioTestCase): + async def awaitEqual(self, func, value, *msg, timeout=100): + timeout_seconds = float(timeout) / 1000.0 + delay = timeout_seconds / 100.0 + stop = time.time() + timeout_seconds + result = None + ex = None + while True: + try: + result = func(self) + except Exception as e: + ex = e + else: + if result == value: + return + + # wait using exponential delay + await asyncio.sleep(delay) + delay = min(stop - time.time(), delay * 2.0) + if delay < 0: + break + + if ex: + raise ex + + self.fail(f"awaitEqual({func.__name__}, {value}) timed out") + + +class TestAsyncTestCase(AsyncTestBase): + def setUp(self): + self.text = 1 + + async def modify(self, v, delay=0.01): + await asyncio.sleep(delay) + self.text = v + + async def test_await_delayed_value_change(self): + self.assertEqual(self.text, 1) + fut = asyncio.create_task(self.modify(2)) + await self.awaitEqual( + lambda t: self.text, + 2, + ) + await fut + self.assertEqual(self.text, 2) + + +if __name__ == "__main__": + asyncio.run(unittest.main()) From 4b8c352f4bb26566191aa4eec3e6c67ccec155a2 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 20 Feb 2023 02:34:24 +0100 Subject: [PATCH 2/2] add more await funcs, use asyncio.wait_for to manage timeout --- tests/lib/asynctest.py | 101 ++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 32 deletions(-) diff --git a/tests/lib/asynctest.py b/tests/lib/asynctest.py index 74832737b..2ad40231f 100644 --- a/tests/lib/asynctest.py +++ b/tests/lib/asynctest.py @@ -1,3 +1,24 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# input-remapper - GUI for device specific keyboard mappings +# Copyright (C) 2023 sezanzeb +# +# This file is part of input-remapper. +# +# input-remapper is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# input-remapper is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with input-remapper. If not, see . + + import time import unittest from unittest.async_case import TestCase, IsolatedAsyncioTestCase @@ -5,34 +26,45 @@ class AsyncTestBase(IsolatedAsyncioTestCase): - async def awaitEqual(self, func, value, *msg, timeout=100): - timeout_seconds = float(timeout) / 1000.0 - delay = timeout_seconds / 100.0 - stop = time.time() + timeout_seconds - result = None - ex = None - while True: - try: - result = func(self) - except Exception as e: - ex = e - else: - if result == value: - return - - # wait using exponential delay - await asyncio.sleep(delay) - delay = min(stop - time.time(), delay * 2.0) - if delay < 0: - break - - if ex: - raise ex - - self.fail(f"awaitEqual({func.__name__}, {value}) timed out") - - -class TestAsyncTestCase(AsyncTestBase): + async def awaitEqual(self, func, value, *msg, timeout: float = 0.1): + await self.awaitTrue(lambda: func() == value, *msg, timeout=timeout) + + async def awaitNotEqual(self, func, value, *msg, timeout: float = 0.1): + await self.awaitTrue(lambda: func() != value, *msg, timeout=timeout) + + async def awaitIs(self, func, value, *msg, timeout: float = 0.1): + await self.awaitTrue(lambda: func() is value, *msg, timeout=timeout) + + async def awaitIsNot(self, func, value, *msg, timeout: float = 0.1): + await self.awaitTrue(lambda: func() is not value, *msg, timeout=timeout) + + async def awaitNone(self, func, *msg, timeout: float = 0.1): + await self.awaitTrue(lambda: func() is None, *msg, timeout=timeout) + + async def awaitNotNone(self, func, *msg, timeout: float = 0.1): + await self.awaitTrue(lambda: func() is not None, *msg, timeout=timeout) + + async def awaitFalse(self, func, *msg, timeout: float = 0.1): + await self.awaitTrue(lambda: func() is False, *msg, timeout=timeout) + + async def awaitTrue(self, func, *msg, timeout: float = 0.1): + delay = timeout / 100.0 + + async def await_success(delay): + while True: + try: + if func() is True: + return + except Exception as e: + ex = e + + delay *= 1.5 # use exponential delay + await asyncio.sleep(delay) + + await asyncio.wait_for(await_success(delay), timeout) + + +class TestAsyncTestBase(AsyncTestBase): def setUp(self): self.text = 1 @@ -43,13 +75,18 @@ async def modify(self, v, delay=0.01): async def test_await_delayed_value_change(self): self.assertEqual(self.text, 1) fut = asyncio.create_task(self.modify(2)) - await self.awaitEqual( - lambda t: self.text, - 2, - ) + await self.awaitEqual(lambda: self.text, 2) await fut self.assertEqual(self.text, 2) + async def test_await_timeout(self): + try: + await self.awaitEqual(lambda: False, True) + except asyncio.exceptions.TimeoutError: + pass + else: + self.fail("awaitEqual must time out") + if __name__ == "__main__": asyncio.run(unittest.main())