diff options
author | Daniel P. Berrangé <berrange@redhat.com> | 2022-05-17 15:01:19 +0100 |
---|---|---|
committer | Daniel P. Berrangé <berrange@redhat.com> | 2022-06-08 16:43:52 +0100 |
commit | e3fab09382d4a6c439820ed5e6930e57716dcb50 (patch) | |
tree | 75bd89dbae775c6e0b255453a67dbde3a824f879 | |
parent | b9f79758c973db088b5c687425c6613796fdb250 (diff) | |
download | libvirt-python-e3fab09382d4a6c439820ed5e6930e57716dcb50.tar.gz |
tests: use mocks to allow calling virEventRegisterImpl many times
We currently have to run each of the test_aio.py test cases in a
separate process, because libvirt.virEventRegisterImpl can only be
called once per process. This leads to quite unpleasant console
output when running tests.
By introducing a mock for libvirt.virEventRegisterImpl we can
regain the ability to run everything in a single process. The only
caveat is that it relies on tests to fully cleanup, but in practice
this is ok for our current tests.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
-rw-r--r-- | pytest.ini | 3 | ||||
-rwxr-xr-x | setup.py | 14 | ||||
-rw-r--r-- | tests/eventmock.py | 82 | ||||
-rw-r--r-- | tests/test_aio.py | 28 |
4 files changed, 102 insertions, 25 deletions
diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index b23017c..0000000 --- a/pytest.ini +++ /dev/null @@ -1,3 +0,0 @@ -[pytest] -markers = - separate_process: mark test as requiring its own process @@ -310,19 +310,7 @@ class my_test(Command): os.environ["PYTHONPATH"] = self.build_platlib pytest = self.find_pytest_path() - - # Run the normal tests. - subprocess.check_call([pytest, "-m", "not separate_process"]) - - # Run the tests that require their own process. - testlist = subprocess.run( - [pytest, "--collect-only", "--quiet", "-m", "separate_process"], - check=True, stdout=subprocess.PIPE) - testlist = testlist.stdout.decode("utf-8").splitlines() - testlist = filter( - lambda test: test and "tests collected" not in test, testlist) - for test in testlist: - subprocess.check_call([pytest, test]) + subprocess.check_call([pytest]) class my_clean(Command): def run(self): diff --git a/tests/eventmock.py b/tests/eventmock.py new file mode 100644 index 0000000..7298086 --- /dev/null +++ b/tests/eventmock.py @@ -0,0 +1,82 @@ + +import libvirt +import libvirtmod + +_add_handle_impl = None +_update_handle_impl = None +_remove_handle_impl = None + +_add_timeout_impl = None +_update_timeout_impl = None +_remove_timeout_impl = None + +_registered = False + +def _add_handle(fd: int, event: int, cb: libvirt._EventCB, opaque: libvirt._T) -> int: + global _add_handle_impl + assert _add_handle_impl != None + return _add_handle_impl(fd, event, cb, opaque) + +def _update_handle(watch: int, event: int) -> None: + global _update_handle_impl + assert _update_handle_impl != None + _update_handle_impl(watch, event) + +def _remove_handle(watch: int) -> int: + global _remove_handle_impl + assert _remove_handle_impl != None + return _remove_handle_impl(watch) + +def _add_timeout(timeout: int, cb: libvirt._TimerCB, opaque: libvirt._T) -> int: + global _add_timeout_impl + assert _add_timeout_impl != None + return _add_timeout_impl(timeout, cb, opaque) + +def _update_timeout(timer: int, timeout: int) -> None: + global _update_timeout_impl + assert _update_timeout_impl != None + _update_timeout_impl(timer, timeout) + +def _remove_timeout(timer: int) -> int: + global _remove_timeout_impl + assert _remove_timeout_impl != None + return _remove_timeout_impl(timer) + +# libvirt.virEventRegisterImpl() is a one time call per process +# This method is intended to be used with mock patching, so that +# tests can get the appearance of being able to call +# virEventRegisterImpl many times. +# +# Note, this relies on the tests closing all connection objects +# and not leaving any handles/timers pending when they stop +# running their event loop impl. + +def virEventRegisterImplMock(add_handle_impl, + update_handle_impl, + remove_handle_impl, + add_timeout_impl, + update_timeout_impl, + remove_timeout_impl): + global _add_handle_impl + global _update_handle_impl + global _remove_handle_impl + global _add_timeout_impl + global _update_timeout_impl + global _remove_timeout_impl + + _add_handle_impl = add_handle_impl + _update_handle_impl = update_handle_impl + _remove_handle_impl = remove_handle_impl + _add_timeout_impl = add_timeout_impl + _update_timeout_impl = update_timeout_impl + _remove_timeout_impl = remove_timeout_impl + + global _registered + if not _registered: + libvirtmod.virEventRegisterImpl(_add_handle, + _update_handle, + _remove_timeout, + _add_timeout, + _update_timeout, + _remove_timeout) + _registered = True diff --git a/tests/test_aio.py b/tests/test_aio.py index fb8c6b6..c90934d 100644 --- a/tests/test_aio.py +++ b/tests/test_aio.py @@ -3,7 +3,9 @@ import libvirt import libvirtaio import sys import unittest -import pytest +from unittest import mock + +import eventmock class TestLibvirtAio(unittest.TestCase): @@ -70,8 +72,9 @@ class TestLibvirtAio(unittest.TestCase): if domainStopped: dom.create() - @pytest.mark.separate_process - def testEventsWithManualLoopSetup(self): + @mock.patch('libvirt.virEventRegisterImpl', + side_effect=eventmock.virEventRegisterImplMock) + def testEventsWithManualLoopSetup(self, mock_event_register): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) @@ -79,15 +82,19 @@ class TestLibvirtAio(unittest.TestCase): loop.close() asyncio.set_event_loop(None) + mock_event_register.assert_called_once() - @pytest.mark.separate_process + @mock.patch('libvirt.virEventRegisterImpl', + side_effect=eventmock.virEventRegisterImplMock) @unittest.skipIf(sys.version_info < (3,7), "test requires Python 3.7+") - def testEventsWithAsyncioRun(self): + def testEventsWithAsyncioRun(self, mock_event_register): asyncio.run(self._run(register=True)) + mock_event_register.assert_called_once() - @pytest.mark.separate_process + @mock.patch('libvirt.virEventRegisterImpl', + side_effect=eventmock.virEventRegisterImplMock) @unittest.skipIf(sys.version_info >= (3,10), "test not compatible with Python 3.10+") - def testEventsPreInit(self): + def testEventsPreInit(self, mock_event_register): # Initialize libvirt events before setting the event loop. This is not recommended. # But is supported in older version of Python for the sake of back-compat. loop = asyncio.new_event_loop() @@ -98,9 +105,11 @@ class TestLibvirtAio(unittest.TestCase): loop.close() asyncio.set_event_loop(None) + mock_event_register.assert_called_once() - @pytest.mark.separate_process - def testEventsImplicitLoopInit(self): + @mock.patch('libvirt.virEventRegisterImpl', + side_effect=eventmock.virEventRegisterImplMock) + def testEventsImplicitLoopInit(self, mock_event_register): # Allow virEventRegisterAsyncIOImpl() to init the event loop by calling # asyncio.get_event_loop(). This is not recommended and probably only works by # accident. But is supported for now for the sake of back-compat. For Python @@ -112,3 +121,4 @@ class TestLibvirtAio(unittest.TestCase): loop.close() asyncio.set_event_loop(None) + mock_event_register.assert_called_once() |