From 01a5225b264a5c731cc1dff89f5d1cad46e9c95d Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Tue, 23 Dec 2025 15:41:03 +0300 Subject: [PATCH 1/2] testgres.os_ops 1.0.0 is used [from master] + new tests for os_ops.kill are added (copied) --- tests/requirements.txt | 2 +- tests/test_os_ops_common.py | 199 ++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index 407279b3..0fdcae51 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -3,4 +3,4 @@ pytest pytest-xdist psycopg2 six -testgres.os_ops>=0.0.2,<1.0.0 +git+https://github.com/postgrespro/testgres.os_ops.git diff --git a/tests/test_os_ops_common.py b/tests/test_os_ops_common.py index bf2dce76..46d3129d 100644 --- a/tests/test_os_ops_common.py +++ b/tests/test_os_ops_common.py @@ -5,6 +5,7 @@ from .helpers.run_conditions import RunConditions import os +import sys import pytest import re @@ -14,6 +15,10 @@ import threading import typing import uuid +import subprocess +import psutil +import time +import signal as os_signal from src import InvalidOperationException from src import ExecUtilException @@ -1137,3 +1142,197 @@ class tadWorkerData: logging.info("Test is finished! Total error count is {}.".format(nErrors)) return + + T_KILL_SIGNAL_DESCR = typing.Tuple[ + str, + typing.Union[int, os_signal.Signals], + str + ] + + sm_kill_signal_ids: typing.List[T_KILL_SIGNAL_DESCR] = [ + ("SIGINT", os_signal.SIGINT, "2"), + # ("SIGQUIT", os_signal.SIGQUIT, "3"), # it creates coredump + ("SIGKILL", os_signal.SIGKILL, "9"), + ("SIGTERM", os_signal.SIGTERM, "15"), + ("2", 2, "2"), + # ("3", 3, "3"), # it creates coredump + ("9", 9, "9"), + ("15", 15, "15"), + ] + + @pytest.fixture( + params=sm_kill_signal_ids, + ids=["signal: {}".format(x[0]) for x in sm_kill_signal_ids], + ) + def kill_signal_id(self, request: pytest.FixtureRequest) -> T_KILL_SIGNAL_DESCR: + assert isinstance(request, pytest.FixtureRequest) + assert type(request.param) == tuple # noqa: E721 + return request.param + + def test_kill_signal( + self, + kill_signal_id: T_KILL_SIGNAL_DESCR, + ): + assert type(kill_signal_id) == tuple # noqa: E721 + assert "{}".format(kill_signal_id[1]) == kill_signal_id[2] + assert "{}".format(int(kill_signal_id[1])) == kill_signal_id[2] + + def test_kill( + self, + os_ops: OsOperations, + kill_signal_id: T_KILL_SIGNAL_DESCR, + ): + """ + Test listdir for listing directory contents. + """ + assert isinstance(os_ops, OsOperations) + assert type(kill_signal_id) == tuple # noqa: E721 + + cmd = [ + sys.executable, + "-c", + "import time; print('ENTER');time.sleep(300);print('EXIT')" + ] + + logging.info("Local test process is creating ...") + proc = subprocess.Popen( + cmd, + text=True, + ) + + assert proc is not None + assert type(proc) == subprocess.Popen # noqa: E721 + proc_pid = proc.pid + assert type(proc_pid) == int # noqa: E721 + logging.info("Test process pid is {}".format(proc_pid)) + + logging.info("Get this test process ...") + p1 = psutil.Process(proc_pid) + assert p1 is not None + del p1 + + logging.info("Kill this test process ...") + os_ops.kill(proc_pid, kill_signal_id[1]) + + logging.info("Wait for finish ...") + proc.wait() + + logging.info("Try to get this test process ...") + + attempt = 0 + while True: + if attempt == 20: + raise RuntimeError("Process did not die.") + + attempt += 1 + + if attempt > 1: + logging.info("Sleep 1 seconds...") + time.sleep(1) + + try: + psutil.Process(proc_pid) + except psutil.ZombieProcess as e: + logging.info("Exception {}: {}".format( + type(e).__name__, + str(e), + )) + except psutil.NoSuchProcess: + logging.info("OK. Process died.") + break + + logging.info("Process is alive!") + continue + + return + + def test_kill__unk_pid( + self, + os_ops: OsOperations, + kill_signal_id: T_KILL_SIGNAL_DESCR, + ): + """ + Test listdir for listing directory contents. + """ + assert isinstance(os_ops, OsOperations) + assert type(kill_signal_id) == tuple # noqa: E721 + + cmd = [ + sys.executable, + "-c", + "import sys; print(\"a\", file=sys.stdout); print(\"b\", file=sys.stderr)" + ] + + logging.info("Local test process is creating ...") + proc = subprocess.Popen( + cmd, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + assert proc is not None + assert type(proc) == subprocess.Popen # noqa: E721 + proc_pid = proc.pid + assert type(proc_pid) == int # noqa: E721 + logging.info("Test process pid is {}".format(proc_pid)) + + logging.info("Wait for finish ...") + pout, perr = proc.communicate() + logging.info("STDOUT: {}".format(pout)) + logging.info("STDERR: {}".format(pout)) + assert type(pout) == str # noqa: E721 + assert type(perr) == str # noqa: E721 + assert pout == "a\n" + assert perr == "b\n" + assert type(proc.returncode) == int # noqa: E721 + assert proc.returncode == 0 + + logging.info("Try to get this test process ...") + + attempt = 0 + while True: + if attempt == 20: + raise RuntimeError("Process did not die.") + + attempt += 1 + + if attempt > 1: + logging.info("Sleep 1 seconds...") + time.sleep(1) + + try: + psutil.Process(proc_pid) + except psutil.ZombieProcess as e: + logging.info("Exception {}: {}".format( + type(e).__name__, + str(e), + )) + except psutil.NoSuchProcess: + logging.info("OK. Process died.") + break + + logging.info("Process is alive!") + continue + + # -------------------- + with pytest.raises(expected_exception=Exception) as x: + os_ops.kill(proc_pid, kill_signal_id[1]) + + assert x is not None + assert isinstance(x.value, Exception) + assert not isinstance(x.value, AssertionError) + + logging.info("Our error is [{}]".format(str(x.value))) + logging.info("Our exception has type [{}]".format(type(x.value).__name__)) + + if type(os_ops).__name__ == "LocalOsOperations": + assert type(x.value) == ProcessLookupError # noqa: E721 + assert "No such process" in str(x.value) + elif type(os_ops).__name__ == "RemoteOsOperations": + assert type(x.value) == ExecUtilException # noqa: E721 + assert "No such process" in str(x.value) + else: + RuntimeError("Unknown os_ops type: {}".format(type(os_ops).__name__)) + + return From 1cafc9086a576bb55580b89afe3fd955f690d384 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Tue, 23 Dec 2025 22:58:20 +0300 Subject: [PATCH 2/2] A problem with git in CI is fixed (std, std-all, ubuntu_24_04) --- Dockerfile--std-all.tmpl | 1 + Dockerfile--std.tmpl | 2 ++ Dockerfile--ubuntu_24_04.tmpl | 2 ++ 3 files changed, 5 insertions(+) diff --git a/Dockerfile--std-all.tmpl b/Dockerfile--std-all.tmpl index d19f52a6..e05acd87 100644 --- a/Dockerfile--std-all.tmpl +++ b/Dockerfile--std-all.tmpl @@ -13,6 +13,7 @@ ENV PYTHON_VERSION=3 FROM base2_with_python-${PYTHON_VERSION} as final #RUN apk add --no-cache mc +RUN apk add --no-cache git # Full version of "ps" command RUN apk add --no-cache procps diff --git a/Dockerfile--std.tmpl b/Dockerfile--std.tmpl index 67aa30b4..f0bbf5b5 100644 --- a/Dockerfile--std.tmpl +++ b/Dockerfile--std.tmpl @@ -12,6 +12,8 @@ ENV PYTHON_VERSION=3 # --------------------------------------------- final FROM base2_with_python-${PYTHON_VERSION} as final +RUN apk add --no-cache git + ENV LANG=C.UTF-8 ADD . /pg/testgres diff --git a/Dockerfile--ubuntu_24_04.tmpl b/Dockerfile--ubuntu_24_04.tmpl index 7a559776..bb77b84a 100644 --- a/Dockerfile--ubuntu_24_04.tmpl +++ b/Dockerfile--ubuntu_24_04.tmpl @@ -47,6 +47,8 @@ ENV PYTHON_VERSION=3 # --------------------------------------------- final FROM base2_with_python-${PYTHON_VERSION} as final +RUN apt install -y git + ADD . /pg/testgres WORKDIR /pg/testgres RUN chown -R postgres /pg