Skip to content

Commit a2c6293

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 a2c6293

File tree

5 files changed

+49
-33
lines changed

5 files changed

+49
-33
lines changed

libkirk/ltp.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import logging
1111
import os
1212
import re
13+
import signal
1314
from typing import (
1415
Any,
1516
Dict,

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: 23 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,12 @@ async def _run_test(self, test: Test) -> None:
294300
"exec_time": exec_time,
295301
}
296302

303+
# we won't consider killed tests, unless they have been
304+
# killed by kirk by forcibly stop
305+
if test_data["returncode"] == -signal.SIGKILL:
306+
self._logger.info("Test killed: %s", test.name)
307+
return
308+
297309
results = await self._framework.read_result(
298310
test,
299311
test_data["stdout"],
@@ -304,6 +316,12 @@ async def _run_test(self, test: Test) -> None:
304316
self._logger.debug("results=%s", results)
305317
self._results.append(results)
306318

319+
await libkirk.events.fire("test_completed", results)
320+
await self._write_kmsg(test, results)
321+
322+
self._logger.info("Test completed: %s", test.name)
323+
self._logger.debug(results)
324+
307325
# raise kernel errors at the end so we can collect test results
308326
if status == self.KERNEL_TAINTED:
309327
await libkirk.events.fire("kernel_tainted", tainted_msg)
@@ -317,12 +335,6 @@ async def _run_test(self, test: Test) -> None:
317335
await libkirk.events.fire("sut_not_responding")
318336
raise KernelTimeoutError()
319337

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-
326338
async def _run_and_wait(self, tests: List[Test]) -> None:
327339
"""
328340
Run tests one after another.
@@ -380,11 +392,10 @@ async def schedule(self, jobs: List[Any]) -> None:
380392
exc_name = err.__class__.__name__
381393
self._logger.info("%s caught during tests execution", exc_name)
382394

383-
raise_exc = not self._stop
384395
async with self._running_tests_sem:
385396
pass
386397

387-
if raise_exc:
398+
if self._stop_cnt == 0:
388399
self._logger.info("Propagating %s exception", exc_name)
389400
raise err
390401

libkirk/session.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ def __init__(
9090
self._sut = sut
9191
self._exec_timeout = exec_timeout
9292
self._force_parallel = force_parallel
93-
self._stop = False
93+
self._stop_cnt = 0
94+
self._stop_lock = asyncio.Lock()
9495
self._exec_lock = asyncio.Lock()
9596
self._run_lock = asyncio.Lock()
9697
self._results = []
@@ -356,7 +357,7 @@ async def _exec_command(self, command: str) -> None:
356357
except asyncio.TimeoutError:
357358
exc = KirkException(f"Command timeout: {repr(command)}")
358359
except KirkException as err:
359-
if not self._stop:
360+
if self._stop_cnt == 0:
360361
exc = err
361362

362363
if exc:
@@ -369,13 +370,16 @@ async def _inner_stop(self) -> None:
369370
if self._scheduler:
370371
await self._scheduler.stop()
371372

372-
await self._stop_sut()
373+
if self._stop_cnt < 1:
374+
await self._stop_sut()
373375

374376
async def stop(self) -> None:
375377
"""
376378
Stop the current session.
377379
"""
378-
self._stop = True
380+
async with self._stop_lock:
381+
self._stop_cnt += 1
382+
379383
try:
380384
await self._inner_stop()
381385

@@ -385,8 +389,12 @@ async def stop(self) -> None:
385389
async with self._exec_lock:
386390
pass
387391
finally:
388-
await libkirk.events.fire("session_stopped")
389-
self._stop = False
392+
if self._stop_cnt == 1:
393+
await libkirk.events.fire("session_stopped")
394+
elif self._stop_cnt > 1:
395+
await libkirk.events.fire("session_killed")
396+
397+
self._stop_cnt = 0
390398

391399
async def _schedule_once(self, suites_obj: List[Suite]) -> None:
392400
"""
@@ -403,7 +411,7 @@ async def _schedule_infinite(self, suites_obj: List[Suite]) -> None:
403411
suites_list.extend(suites_obj)
404412

405413
count = 1
406-
while not self._stop:
414+
while self._stop_cnt == 0:
407415
await self._schedule_once(suites_obj)
408416
if self._scheduler.stopped:
409417
break
@@ -516,7 +524,7 @@ async def run(
516524

517525
await self._run_scheduler(suites_obj, runtime)
518526
except KirkException as err:
519-
if not self._stop:
527+
if self._stop_cnt == 0:
520528
self._logger.exception(err)
521529
await libkirk.events.fire("session_error", str(err))
522530
raise err

libkirk/ui.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def __init__(self, no_colors: bool = False) -> None:
4646
libkirk.events.register("session_restore", self.session_restore)
4747
libkirk.events.register("session_started", self.session_started)
4848
libkirk.events.register("session_stopped", self.session_stopped)
49+
libkirk.events.register("session_killed", self.session_killed)
4950
libkirk.events.register("sut_start", self.sut_start)
5051
libkirk.events.register("sut_stop", self.sut_stop)
5152
libkirk.events.register("sut_restart", self.sut_restart)
@@ -128,6 +129,9 @@ async def session_started(self, tmpdir: str) -> None:
128129
async def session_stopped(self) -> None:
129130
await self._print("Session stopped")
130131

132+
async def session_killed(self) -> None:
133+
await self._print("Session killed")
134+
131135
async def sut_start(self, sut: str) -> None:
132136
await self._print(f"Connecting to SUT: {sut}")
133137

0 commit comments

Comments
 (0)