Skip to content

Commit 81200bc

Browse files
OsOperation::kill is updated (#2)
LocalOsOperation::kill - pid must be int - signal must be int or signal.Signals - argument "expected_error" is not supported anymore (unification with RemoteOsOperation) - it uses os.kill function instead "kill" command from OS. NOTE: It brokes applications where expect_error is passed (testgres 1.12.0, for example). OsOperation::kill and RemoteOsOperation - pid must be int - signal must be int or signal.Signals New tests - test_kill_signal - test_kill - test_kill__unk_pid
1 parent ce82ff6 commit 81200bc

File tree

4 files changed

+216
-7
lines changed

4 files changed

+216
-7
lines changed

src/local_ops.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import typing
1515
import threading
1616
import copy
17+
import signal as os_signal
1718

1819
from .exceptions import ExecUtilException
1920
from .exceptions import InvalidOperationException
@@ -568,10 +569,11 @@ def remove_file(self, filename):
568569
return os.remove(filename)
569570

570571
# Processes control
571-
def kill(self, pid, signal, expect_error=False):
572+
def kill(self, pid: int, signal: typing.Union[int, os_signal.Signals]):
572573
# Kill the process
573-
cmd = "kill -{} {}".format(signal, pid)
574-
return self.exec_command(cmd, expect_error=expect_error)
574+
assert type(pid) == int # noqa: E721
575+
assert type(signal) == int or type(signal) == os_signal.Signals # noqa: E721 E501
576+
os.kill(pid, signal)
575577

576578
def get_pid(self):
577579
# Get current process id

src/os_ops.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

33
import locale
4+
import typing
5+
import signal as os_signal
46

57

68
class ConnectionParams:
@@ -126,8 +128,10 @@ def remove_file(self, filename):
126128
raise NotImplementedError()
127129

128130
# Processes control
129-
def kill(self, pid, signal):
131+
def kill(self, pid: int, signal: typing.Union[int, os_signal.Signals]):
130132
# Kill the process
133+
assert type(pid) == int # noqa: E721
134+
assert type(signal) == int or type(signal) == os_signal.Signals # noqa: E721 E501
131135
raise NotImplementedError()
132136

133137
def get_pid(self):

src/remote_ops.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import typing
1212
import copy
1313
import re
14+
import signal as os_signal
1415

1516
from .exceptions import ExecUtilException
1617
from .exceptions import InvalidOperationException
@@ -658,10 +659,13 @@ def remove_file(self, filename):
658659
return self.exec_command(cmd)
659660

660661
# Processes control
661-
def kill(self, pid, signal):
662+
def kill(self, pid: int, signal: typing.Union[int, os_signal.Signals]):
662663
# Kill the process
663-
cmd = "kill -{} {}".format(signal, pid)
664-
return self.exec_command(cmd)
664+
assert type(pid) == int # noqa: E721
665+
assert type(signal) == int or type(signal) == os_signal.Signals # noqa: E721 E501
666+
assert int(signal) == signal
667+
cmd = "kill -{} {}".format(int(signal), pid)
668+
return self.exec_command(cmd, encoding=get_default_encoding())
665669

666670
def get_pid(self):
667671
# Get current process id

tests/test_os_ops_common.py

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from tests.helpers.run_conditions import RunConditions
66

77
import os
8+
import sys
89

910
import pytest
1011
import re
@@ -14,6 +15,10 @@
1415
import threading
1516
import typing
1617
import uuid
18+
import subprocess
19+
import psutil
20+
import time
21+
import signal as os_signal
1722

1823
from src.exceptions import InvalidOperationException
1924
from src.exceptions import ExecUtilException
@@ -1137,3 +1142,197 @@ class tadWorkerData:
11371142

11381143
logging.info("Test is finished! Total error count is {}.".format(nErrors))
11391144
return
1145+
1146+
T_KILL_SIGNAL_DESCR = typing.Tuple[
1147+
str,
1148+
typing.Union[int, os_signal.Signals],
1149+
str
1150+
]
1151+
1152+
sm_kill_signal_ids: typing.List[T_KILL_SIGNAL_DESCR] = [
1153+
("SIGINT", os_signal.SIGINT, "2"),
1154+
# ("SIGQUIT", os_signal.SIGQUIT, "3"), # it creates coredump
1155+
("SIGKILL", os_signal.SIGKILL, "9"),
1156+
("SIGTERM", os_signal.SIGTERM, "15"),
1157+
("2", 2, "2"),
1158+
# ("3", 3, "3"), # it creates coredump
1159+
("9", 9, "9"),
1160+
("15", 15, "15"),
1161+
]
1162+
1163+
@pytest.fixture(
1164+
params=sm_kill_signal_ids,
1165+
ids=["signal: {}".format(x[0]) for x in sm_kill_signal_ids],
1166+
)
1167+
def kill_signal_id(self, request: pytest.FixtureRequest) -> T_KILL_SIGNAL_DESCR:
1168+
assert isinstance(request, pytest.FixtureRequest)
1169+
assert type(request.param) == tuple # noqa: E721
1170+
return request.param
1171+
1172+
def test_kill_signal(
1173+
self,
1174+
kill_signal_id: T_KILL_SIGNAL_DESCR,
1175+
):
1176+
assert type(kill_signal_id) == tuple # noqa: E721
1177+
assert "{}".format(kill_signal_id[1]) == kill_signal_id[2]
1178+
assert "{}".format(int(kill_signal_id[1])) == kill_signal_id[2]
1179+
1180+
def test_kill(
1181+
self,
1182+
os_ops: OsOperations,
1183+
kill_signal_id: T_KILL_SIGNAL_DESCR,
1184+
):
1185+
"""
1186+
Test listdir for listing directory contents.
1187+
"""
1188+
assert isinstance(os_ops, OsOperations)
1189+
assert type(kill_signal_id) == tuple # noqa: E721
1190+
1191+
cmd = [
1192+
sys.executable,
1193+
"-c",
1194+
"import time; print('ENTER');time.sleep(300);print('EXIT')"
1195+
]
1196+
1197+
logging.info("Local test process is creating ...")
1198+
proc = subprocess.Popen(
1199+
cmd,
1200+
text=True,
1201+
)
1202+
1203+
assert proc is not None
1204+
assert type(proc) == subprocess.Popen # noqa: E721
1205+
proc_pid = proc.pid
1206+
assert type(proc_pid) == int # noqa: E721
1207+
logging.info("Test process pid is {}".format(proc_pid))
1208+
1209+
logging.info("Get this test process ...")
1210+
p1 = psutil.Process(proc_pid)
1211+
assert p1 is not None
1212+
del p1
1213+
1214+
logging.info("Kill this test process ...")
1215+
os_ops.kill(proc_pid, kill_signal_id[1])
1216+
1217+
logging.info("Wait for finish ...")
1218+
proc.wait()
1219+
1220+
logging.info("Try to get this test process ...")
1221+
1222+
attempt = 0
1223+
while True:
1224+
if attempt == 20:
1225+
raise RuntimeError("Process did not die.")
1226+
1227+
attempt += 1
1228+
1229+
if attempt > 1:
1230+
logging.info("Sleep 1 seconds...")
1231+
time.sleep(1)
1232+
1233+
try:
1234+
psutil.Process(proc_pid)
1235+
except psutil.ZombieProcess as e:
1236+
logging.info("Exception {}: {}".format(
1237+
type(e).__name__,
1238+
str(e),
1239+
))
1240+
except psutil.NoSuchProcess:
1241+
logging.info("OK. Process died.")
1242+
break
1243+
1244+
logging.info("Process is alive!")
1245+
continue
1246+
1247+
return
1248+
1249+
def test_kill__unk_pid(
1250+
self,
1251+
os_ops: OsOperations,
1252+
kill_signal_id: T_KILL_SIGNAL_DESCR,
1253+
):
1254+
"""
1255+
Test listdir for listing directory contents.
1256+
"""
1257+
assert isinstance(os_ops, OsOperations)
1258+
assert type(kill_signal_id) == tuple # noqa: E721
1259+
1260+
cmd = [
1261+
sys.executable,
1262+
"-c",
1263+
"import sys; print(\"a\", file=sys.stdout); print(\"b\", file=sys.stderr)"
1264+
]
1265+
1266+
logging.info("Local test process is creating ...")
1267+
proc = subprocess.Popen(
1268+
cmd,
1269+
text=True,
1270+
stdout=subprocess.PIPE,
1271+
stderr=subprocess.PIPE,
1272+
)
1273+
1274+
assert proc is not None
1275+
assert type(proc) == subprocess.Popen # noqa: E721
1276+
proc_pid = proc.pid
1277+
assert type(proc_pid) == int # noqa: E721
1278+
logging.info("Test process pid is {}".format(proc_pid))
1279+
1280+
logging.info("Wait for finish ...")
1281+
pout, perr = proc.communicate()
1282+
logging.info("STDOUT: {}".format(pout))
1283+
logging.info("STDERR: {}".format(pout))
1284+
assert type(pout) == str # noqa: E721
1285+
assert type(perr) == str # noqa: E721
1286+
assert pout == "a\n"
1287+
assert perr == "b\n"
1288+
assert type(proc.returncode) == int # noqa: E721
1289+
assert proc.returncode == 0
1290+
1291+
logging.info("Try to get this test process ...")
1292+
1293+
attempt = 0
1294+
while True:
1295+
if attempt == 20:
1296+
raise RuntimeError("Process did not die.")
1297+
1298+
attempt += 1
1299+
1300+
if attempt > 1:
1301+
logging.info("Sleep 1 seconds...")
1302+
time.sleep(1)
1303+
1304+
try:
1305+
psutil.Process(proc_pid)
1306+
except psutil.ZombieProcess as e:
1307+
logging.info("Exception {}: {}".format(
1308+
type(e).__name__,
1309+
str(e),
1310+
))
1311+
except psutil.NoSuchProcess:
1312+
logging.info("OK. Process died.")
1313+
break
1314+
1315+
logging.info("Process is alive!")
1316+
continue
1317+
1318+
# --------------------
1319+
with pytest.raises(expected_exception=Exception) as x:
1320+
os_ops.kill(proc_pid, kill_signal_id[1])
1321+
1322+
assert x is not None
1323+
assert isinstance(x.value, Exception)
1324+
assert not isinstance(x.value, AssertionError)
1325+
1326+
logging.info("Our error is [{}]".format(str(x.value)))
1327+
logging.info("Our exception has type [{}]".format(type(x.value).__name__))
1328+
1329+
if type(os_ops).__name__ == "LocalOsOperations":
1330+
assert type(x.value) == ProcessLookupError # noqa: E721
1331+
assert "No such process" in str(x.value)
1332+
elif type(os_ops).__name__ == "RemoteOsOperations":
1333+
assert type(x.value) == ExecUtilException # noqa: E721
1334+
assert "No such process" in str(x.value)
1335+
else:
1336+
RuntimeError("Unknown os_ops type: {}".format(type(os_ops).__name__))
1337+
1338+
return

0 commit comments

Comments
 (0)