|
5 | 5 | from tests.helpers.run_conditions import RunConditions |
6 | 6 |
|
7 | 7 | import os |
| 8 | +import sys |
8 | 9 |
|
9 | 10 | import pytest |
10 | 11 | import re |
|
14 | 15 | import threading |
15 | 16 | import typing |
16 | 17 | import uuid |
| 18 | +import subprocess |
| 19 | +import psutil |
| 20 | +import time |
| 21 | +import signal as os_signal |
17 | 22 |
|
18 | 23 | from src.exceptions import InvalidOperationException |
19 | 24 | from src.exceptions import ExecUtilException |
@@ -1137,3 +1142,197 @@ class tadWorkerData: |
1137 | 1142 |
|
1138 | 1143 | logging.info("Test is finished! Total error count is {}.".format(nErrors)) |
1139 | 1144 | 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