Skip to content

Commit 0dd9d49

Browse files
committed
tests: fix main tests failing
TestMain are failing due to a clash with the main loop used by kirk and initialized in the main.py implementation. Create a new isolated loop inside test to avoid this issue and fix it for all python versions including 3.7/9. This will also fix the github workflows dependencies which were stuck to old versions of pytest and pytest-asyncio. Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
1 parent 57592d4 commit 0dd9d49

5 files changed

Lines changed: 58 additions & 6 deletions

File tree

.github/workflows/test_ltx.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ name: "Test LTX"
44
on: [push, pull_request]
55

66
env:
7-
PYTHON_PKGS: pytest<8.3.5 pytest-asyncio<1.0 msgpack
7+
PYTHON_PKGS: pytest pytest-asyncio msgpack
88

99
jobs:
1010
python3:

.github/workflows/test_ssh.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ name: "Test SSH"
44
on: push
55

66
env:
7-
PYTHON_PKGS: pytest<8.3.5 pytest-asyncio<1.0 build asyncssh
7+
PYTHON_PKGS: pytest pytest-asyncio build asyncssh
88
TEST_SSH_KEY_FILE: /home/runner/.ssh/id_rsa
99
TEST_SSH_USERNAME: runner
1010
TEST_SSH_PASSWORD: password

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ name: "Test packages"
44
on: [push, pull_request]
55

66
env:
7-
PYTHON_PKGS: pytest<8.3.5 pytest-asyncio<1.0 build
7+
PYTHON_PKGS: pytest pytest-asyncio build
88

99
jobs:
1010
python3-deprecated:

libkirk/sut.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,13 @@ async def get_info(self) -> Dict[str, str]:
247247

248248
return ret
249249

250-
_tainted_lock = asyncio.Lock()
251-
_tainted_status = asyncio.Queue(maxsize=1)
250+
# Lazily initialised inside get_tainted_info() so that the
251+
# asyncio primitives are always created on the *running* loop.
252+
# Class-level assignment would bind them to the loop that was
253+
# current at import time, which breaks on Python 3.7-3.9 when
254+
# tests run on a different loop than the session loop.
255+
_tainted_lock: Optional[asyncio.Lock] = None
256+
_tainted_status: Optional[asyncio.Queue] = None
252257

253258
async def get_tainted_info(self) -> Tuple[int, List[str]]:
254259
"""
@@ -261,6 +266,12 @@ async def get_tainted_info(self) -> Tuple[int, List[str]]:
261266
if not await self.is_running:
262267
raise SUTError("SUT is not running")
263268

269+
# Initialise on first use, always inside a running loop.
270+
if self._tainted_lock is None:
271+
self._tainted_lock = asyncio.Lock()
272+
if self._tainted_status is None:
273+
self._tainted_status = asyncio.Queue(maxsize=1)
274+
264275
if self._tainted_lock.locked() and self._tainted_status.qsize() > 0:
265276
status = await self._tainted_status.get()
266277
return status

libkirk/tests/test_main.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,57 @@
22
Unittests for main module.
33
"""
44

5+
import asyncio
56
import json
67
import os
78
import pwd
89
import sys
910

1011
import pytest
1112

12-
import libkirk.sut
13+
import libkirk
1314
import libkirk.com
1415
import libkirk.main
16+
import libkirk.sut
17+
from libkirk.evt import EventsHandler
18+
19+
20+
@pytest.fixture(autouse=True)
21+
def isolated_loop(monkeypatch):
22+
"""
23+
Give every TestMain test its own event loop so that
24+
libkirk.main.run() cannot cancel tasks belonging to
25+
other (async) tests, and cannot leave the shared
26+
session loop in a dirty state.
27+
"""
28+
# Remember which loop the session is using so we can restore it.
29+
session_loop = libkirk.get_event_loop()
30+
31+
loop = asyncio.new_event_loop()
32+
asyncio.set_event_loop(loop)
33+
34+
# Re-discover plugins now that the isolated loop is current, so that
35+
# every asyncio.Lock() inside plugin __init__ methods binds to this loop.
36+
currdir = os.path.dirname(os.path.realpath(libkirk.com.__file__))
37+
libkirk.com.discover(os.path.join(currdir, "channels"), extend=False)
38+
libkirk.sut.discover(currdir, extend=False)
39+
40+
fresh_events = EventsHandler()
41+
monkeypatch.setattr(libkirk, "get_event_loop", lambda: loop)
42+
monkeypatch.setattr(libkirk, "events", fresh_events)
43+
44+
yield loop
45+
46+
# Drain and close the isolated loop.
47+
try:
48+
libkirk.cancel_tasks(loop)
49+
if not loop.is_closed():
50+
loop.run_until_complete(fresh_events.stop())
51+
finally:
52+
if not loop.is_closed():
53+
loop.close()
54+
# Restore the session loop so subsequent async tests still work.
55+
asyncio.set_event_loop(session_loop)
1556

1657

1758
class TestMain:

0 commit comments

Comments
 (0)