Skip to content

Commit 323ba55

Browse files
committed
session: implement forcibly stop
Change the way we complete session by introducing forcibly stop. The way that kirk works after this patch is the following: - if user sends SIGINT or CTRL+C, we simply wait for the running tests to complete and finally stop the session - if user sends SIGINT or CTRL+C again, we stop any execution on the SUT and we terminate the testing suite, flagging the killed test as TSKIP Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com> Closes: #78
1 parent 6cdb545 commit 323ba55

File tree

3 files changed

+35
-26
lines changed

3 files changed

+35
-26
lines changed

libkirk/main.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -460,20 +460,12 @@ async def session_run() -> None:
460460
exit_code = RC_ERROR
461461
finally:
462462
try:
463-
# at this point loop has been closed, so we can collect all
464-
# tasks and cancel them
465-
loop.run_until_complete(
466-
# pyrefly: ignore[bad-argument-type]
467-
asyncio.gather(
468-
*[
469-
session.stop(),
470-
libkirk.events.stop(),
471-
]
472-
)
473-
)
474-
libkirk.cancel_tasks(loop)
463+
loop.run_until_complete(session.stop())
475464
except KeyboardInterrupt:
476-
pass
465+
loop.run_until_complete(session.stop())
466+
467+
libkirk.cancel_tasks(loop)
468+
loop.run_until_complete(libkirk.events.stop())
477469

478470
parser.exit(exit_code)
479471

libkirk/scheduler.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import asyncio
1010
import logging
1111
import os
12+
import signal
1213
import sys
1314
import time
1415
from typing import (
@@ -147,7 +148,7 @@ def __init__(
147148
self._timeout = 0.0 if timeout < 0.0 else timeout
148149
self._max_workers = 1 if max_workers < 1 else max_workers
149150
self._results = []
150-
self._stop = False
151+
self._stop_cnt = 0
151152
self._stopped = False
152153
self._running_tests_sem = asyncio.Semaphore(1)
153154
self._schedule_lock = asyncio.Lock()
@@ -203,7 +204,12 @@ def stopped(self) -> bool:
203204

204205
async def stop(self) -> None:
205206
self._logger.info("Stopping tests execution")
206-
self._stop = True
207+
self._stop_cnt += 1
208+
209+
if self._stop_cnt > 1:
210+
# by stopping SUT first, we cause scheduler to complete
211+
# current test immediatelly without waiting
212+
await self._sut.stop()
207213

208214
try:
209215
# we enter in the semaphore queue in order to get highest
@@ -217,7 +223,7 @@ async def stop(self) -> None:
217223
async with self._schedule_lock:
218224
pass
219225
finally:
220-
self._stop = False
226+
self._stop_cnt = 0
221227
self._stopped = True
222228

223229
self._logger.info("All tests have been completed")
@@ -227,7 +233,7 @@ async def _run_test(self, test: Test) -> None:
227233
Run a single test and populate the results array.
228234
"""
229235
async with self._running_tests_sem:
230-
if self._stop:
236+
if self._stop_cnt > 0:
231237
self._logger.info("Test '%s' has been stopped", test.name)
232238
return None
233239

@@ -294,6 +300,13 @@ async def _run_test(self, test: Test) -> None:
294300
"exec_time": exec_time,
295301
}
296302

303+
# we won't consider tests killed by kirk during forcibly stop,
304+
# but only if they have been killed by an external application
305+
# or kernel OOM
306+
if test_data["returncode"] == -signal.SIGKILL and self._stop_cnt > 1:
307+
self._logger.info("Test killed: %s", test.name)
308+
return
309+
297310
results = await self._framework.read_result(
298311
test,
299312
test_data["stdout"],
@@ -304,6 +317,12 @@ async def _run_test(self, test: Test) -> None:
304317
self._logger.debug("results=%s", results)
305318
self._results.append(results)
306319

320+
await libkirk.events.fire("test_completed", results)
321+
await self._write_kmsg(test, results)
322+
323+
self._logger.info("Test completed: %s", test.name)
324+
self._logger.debug(results)
325+
307326
# raise kernel errors at the end so we can collect test results
308327
if status == self.KERNEL_TAINTED:
309328
await libkirk.events.fire("kernel_tainted", tainted_msg)
@@ -317,12 +336,6 @@ async def _run_test(self, test: Test) -> None:
317336
await libkirk.events.fire("sut_not_responding")
318337
raise KernelTimeoutError()
319338

320-
await libkirk.events.fire("test_completed", results)
321-
await self._write_kmsg(test, results)
322-
323-
self._logger.info("Test completed: %s", test.name)
324-
self._logger.debug(results)
325-
326339
async def _run_and_wait(self, tests: List[Test]) -> None:
327340
"""
328341
Run tests one after another.
@@ -380,11 +393,10 @@ async def schedule(self, jobs: List[Any]) -> None:
380393
exc_name = err.__class__.__name__
381394
self._logger.info("%s caught during tests execution", exc_name)
382395

383-
raise_exc = not self._stop
384396
async with self._running_tests_sem:
385397
pass
386398

387-
if raise_exc:
399+
if self._stop_cnt == 0:
388400
self._logger.info("Propagating %s exception", exc_name)
389401
raise err
390402

libkirk/session.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,9 @@ async def stop(self) -> None:
375375
"""
376376
Stop the current session.
377377
"""
378+
# we don't want to send session_stopped more than once
379+
already_stopped = self._stop == True
380+
378381
self._stop = True
379382
try:
380383
await self._inner_stop()
@@ -385,7 +388,9 @@ async def stop(self) -> None:
385388
async with self._exec_lock:
386389
pass
387390
finally:
388-
await libkirk.events.fire("session_stopped")
391+
if not already_stopped:
392+
await libkirk.events.fire("session_stopped")
393+
389394
self._stop = False
390395

391396
async def _schedule_once(self, suites_obj: List[Suite]) -> None:

0 commit comments

Comments
 (0)