From e20e5b593782272050d52435809cbd14270e1106 Mon Sep 17 00:00:00 2001 From: tboy1337 Date: Sat, 30 Aug 2025 10:57:08 +0100 Subject: [PATCH 1/4] Refactor PostgreSQLExecutor to support Windows compatibility for process management - Introduced _get_base_command method to dynamically set the base command for PostgreSQL based on the operating system. - Added _windows_terminate_process method to handle process termination on Windows, ensuring graceful shutdown before force-killing. - Updated stop method to utilize the new Windows-specific termination logic. --- pytest_postgresql/executor.py | 68 +++++++-- tests/test_windows_compatibility.py | 213 ++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+), 9 deletions(-) create mode 100644 tests/test_windows_compatibility.py diff --git a/pytest_postgresql/executor.py b/pytest_postgresql/executor.py index bff67afe..7929ee1e 100644 --- a/pytest_postgresql/executor.py +++ b/pytest_postgresql/executor.py @@ -24,6 +24,8 @@ import subprocess import tempfile import time +import os +import signal from typing import Any, Optional, TypeVar from mirakuru import TCPExecutor @@ -48,13 +50,28 @@ class PostgreSQLExecutor(TCPExecutor): `_ """ - BASE_PROC_START_COMMAND = ( - '{executable} start -D "{datadir}" ' - "-o \"-F -p {port} -c log_destination='stderr' " - "-c logging_collector=off " - "-c unix_socket_directories='{unixsocketdir}' {postgres_options}\" " - '-l "{logfile}" {startparams}' - ) + def _get_base_command(self) -> str: + """Get the base PostgreSQL command, Windows-compatible.""" + if platform.system() == "Windows": + # Windows doesn't handle single quotes well in subprocess calls + return ( + '{executable} start -D "{datadir}" ' + "-o \"-F -p {port} -c log_destination=stderr " + "-c logging_collector=off " + "-c unix_socket_directories={unixsocketdir} {postgres_options}\" " + '-l "{logfile}" {startparams}' + ) + else: + # Unix/Linux systems work with single quotes + return ( + '{executable} start -D "{datadir}" ' + "-o \"-F -p {port} -c log_destination='stderr' " + "-c logging_collector=off " + "-c unix_socket_directories='{unixsocketdir}' {postgres_options}\" " + '-l "{logfile}" {startparams}' + ) + + BASE_PROC_START_COMMAND = "" # Will be set dynamically VERSION_RE = re.compile(r".* (?P\d+(?:\.\d+)?)") MIN_SUPPORTED_VERSION = parse("10") @@ -108,7 +125,7 @@ def __init__( self.logfile = logfile self.startparams = startparams self.postgres_options = postgres_options - command = self.BASE_PROC_START_COMMAND.format( + command = self._get_base_command().format( executable=self.executable, datadir=self.datadir, port=port, @@ -219,6 +236,30 @@ def running(self) -> bool: status_code = subprocess.getstatusoutput(f'{self.executable} status -D "{self.datadir}"')[0] return status_code == 0 + def _windows_terminate_process(self, sig: Optional[int] = None) -> None: + """Terminate process on Windows.""" + if self.process is None: + return + + try: + if platform.system() == "Windows": + # On Windows, try to terminate gracefully first + self.process.terminate() + # Give it a chance to terminate gracefully + try: + self.process.wait(timeout=5) + except subprocess.TimeoutExpired: + # If it doesn't terminate gracefully, force kill + self.process.kill() + self.process.wait() + else: + # On Unix systems, use the signal + actual_sig = sig or signal.SIGTERM + os.killpg(self.process.pid, actual_sig) + except (OSError, AttributeError): + # Process might already be dead or other issues + pass + def stop(self: T, sig: Optional[int] = None, exp_sig: Optional[int] = None) -> T: """Issue a stop request to executable.""" subprocess.check_output( @@ -226,10 +267,19 @@ def stop(self: T, sig: Optional[int] = None, exp_sig: Optional[int] = None) -> T shell=True, ) try: - super().stop(sig, exp_sig) + if platform.system() == "Windows": + self._windows_terminate_process(sig) + else: + super().stop(sig, exp_sig) except ProcessFinishedWithError: # Finished, leftovers ought to be cleaned afterwards anyway pass + except AttributeError as e: + # Handle case where os.killpg doesn't exist (shouldn't happen now) + if "killpg" in str(e): + self._windows_terminate_process(sig) + else: + raise return self def __del__(self) -> None: diff --git a/tests/test_windows_compatibility.py b/tests/test_windows_compatibility.py new file mode 100644 index 00000000..99bfdd61 --- /dev/null +++ b/tests/test_windows_compatibility.py @@ -0,0 +1,213 @@ +"""Test Windows compatibility fixes for pytest-postgresql.""" + +import platform +import pytest +from unittest.mock import Mock, patch, MagicMock +import subprocess +import signal + +from pytest_postgresql.executor import PostgreSQLExecutor + + +class TestWindowsCompatibility: + """Test Windows-specific functionality.""" + + def test_get_base_command_windows(self): + """Test that Windows base command doesn't use single quotes.""" + executor = PostgreSQLExecutor( + executable="/path/to/pg_ctl", + host="localhost", + port=5432, + datadir="/tmp/data", + unixsocketdir="/tmp/socket", + logfile="/tmp/log", + startparams="-w", + dbname="test", + ) + + with patch('platform.system', return_value='Windows'): + command = executor._get_base_command() + # Windows command should not have single quotes around stderr + assert "log_destination=stderr" in command + assert "log_destination='stderr'" not in command + assert "unix_socket_directories={unixsocketdir}" in command + assert "unix_socket_directories='{unixsocketdir}'" not in command + + def test_get_base_command_unix(self): + """Test that Unix base command uses single quotes.""" + executor = PostgreSQLExecutor( + executable="/path/to/pg_ctl", + host="localhost", + port=5432, + datadir="/tmp/data", + unixsocketdir="/tmp/socket", + logfile="/tmp/log", + startparams="-w", + dbname="test", + ) + + with patch('platform.system', return_value='Linux'): + command = executor._get_base_command() + # Unix command should have single quotes around stderr + assert "log_destination='stderr'" in command + assert "unix_socket_directories='{unixsocketdir}'" in command + + def test_windows_terminate_process(self): + """Test Windows process termination.""" + executor = PostgreSQLExecutor( + executable="/path/to/pg_ctl", + host="localhost", + port=5432, + datadir="/tmp/data", + unixsocketdir="/tmp/socket", + logfile="/tmp/log", + startparams="-w", + dbname="test", + ) + + # Mock process + mock_process = MagicMock() + executor.process = mock_process + + with patch('platform.system', return_value='Windows'): + executor._windows_terminate_process() + + # Should call terminate first + mock_process.terminate.assert_called_once() + mock_process.wait.assert_called() + + def test_windows_terminate_process_force_kill(self): + """Test Windows process termination with force kill on timeout.""" + executor = PostgreSQLExecutor( + executable="/path/to/pg_ctl", + host="localhost", + port=5432, + datadir="/tmp/data", + unixsocketdir="/tmp/socket", + logfile="/tmp/log", + startparams="-w", + dbname="test", + ) + + # Mock process that times out + mock_process = MagicMock() + mock_process.wait.side_effect = [subprocess.TimeoutExpired(cmd="test", timeout=5), None] + executor.process = mock_process + + with patch('platform.system', return_value='Windows'): + executor._windows_terminate_process() + + # Should call terminate, wait (timeout), then kill, then wait again + mock_process.terminate.assert_called_once() + mock_process.kill.assert_called_once() + assert mock_process.wait.call_count == 2 + + def test_stop_method_windows(self): + """Test stop method on Windows.""" + executor = PostgreSQLExecutor( + executable="/path/to/pg_ctl", + host="localhost", + port=5432, + datadir="/tmp/data", + unixsocketdir="/tmp/socket", + logfile="/tmp/log", + startparams="-w", + dbname="test", + ) + + # Mock subprocess and process + with patch('subprocess.check_output') as mock_subprocess, \ + patch('platform.system', return_value='Windows'), \ + patch.object(executor, '_windows_terminate_process') as mock_terminate: + + result = executor.stop() + + # Should call pg_ctl stop and Windows terminate + mock_subprocess.assert_called_once() + mock_terminate.assert_called_once_with(None) + assert result is executor + + def test_stop_method_unix(self): + """Test stop method on Unix systems.""" + executor = PostgreSQLExecutor( + executable="/path/to/pg_ctl", + host="localhost", + port=5432, + datadir="/tmp/data", + unixsocketdir="/tmp/socket", + logfile="/tmp/log", + startparams="-w", + dbname="test", + ) + + # Mock subprocess and super().stop + with patch('subprocess.check_output') as mock_subprocess, \ + patch('platform.system', return_value='Linux'), \ + patch('pytest_postgresql.executor.TCPExecutor.stop') as mock_super_stop: + + mock_super_stop.return_value = executor + result = executor.stop() + + # Should call pg_ctl stop and parent class stop + mock_subprocess.assert_called_once() + mock_super_stop.assert_called_once_with(None, None) + assert result is executor + + def test_stop_method_fallback_on_killpg_error(self): + """Test stop method falls back to Windows termination on killpg AttributeError.""" + executor = PostgreSQLExecutor( + executable="/path/to/pg_ctl", + host="localhost", + port=5432, + datadir="/tmp/data", + unixsocketdir="/tmp/socket", + logfile="/tmp/log", + startparams="-w", + dbname="test", + ) + + # Mock subprocess and super().stop to raise AttributeError + with patch('subprocess.check_output') as mock_subprocess, \ + patch('platform.system', return_value='Linux'), \ + patch('pytest_postgresql.executor.TCPExecutor.stop', + side_effect=AttributeError("module 'os' has no attribute 'killpg'")), \ + patch.object(executor, '_windows_terminate_process') as mock_terminate: + + result = executor.stop() + + # Should call pg_ctl stop, fail on super().stop, then use Windows terminate + mock_subprocess.assert_called_once() + mock_terminate.assert_called_once_with(None) + assert result is executor + + def test_command_formatting_windows(self): + """Test that command is properly formatted for Windows.""" + with patch('platform.system', return_value='Windows'): + executor = PostgreSQLExecutor( + executable="C:/Program Files/PostgreSQL/bin/pg_ctl.exe", + host="localhost", + port=5555, + datadir="C:/temp/data", + unixsocketdir="C:/temp/socket", + logfile="C:/temp/log.txt", + startparams="-w -s", + dbname="testdb", + postgres_options="-c shared_preload_libraries=test" + ) + + # The command should be properly formatted without quotes around stderr + expected_parts = [ + "C:/Program Files/PostgreSQL/bin/pg_ctl.exe start", + '-D "C:/temp/data"', + "-o \"-F -p 5555 -c log_destination=stderr", + "-c logging_collector=off", + "-c unix_socket_directories=C:/temp/socket", + "-c shared_preload_libraries=test\"", + '-l "C:/temp/log.txt"', + "-w -s" + ] + + # Check if all expected parts are in the command + command = executor.command + for part in expected_parts: + assert part in command, f"Expected '{part}' in command: {command}" From 6ebc7eb08a530407e7e275f51f85c663fd7f5d06 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 10:01:18 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pytest_postgresql/executor.py | 10 ++-- tests/test_windows_compatibility.py | 91 +++++++++++++++-------------- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/pytest_postgresql/executor.py b/pytest_postgresql/executor.py index 7929ee1e..256259d9 100644 --- a/pytest_postgresql/executor.py +++ b/pytest_postgresql/executor.py @@ -17,15 +17,15 @@ # along with pytest-postgresql. If not, see . """PostgreSQL executor crafter around pg_ctl.""" +import os import os.path import platform import re import shutil +import signal import subprocess import tempfile import time -import os -import signal from typing import Any, Optional, TypeVar from mirakuru import TCPExecutor @@ -56,9 +56,9 @@ def _get_base_command(self) -> str: # Windows doesn't handle single quotes well in subprocess calls return ( '{executable} start -D "{datadir}" ' - "-o \"-F -p {port} -c log_destination=stderr " + '-o "-F -p {port} -c log_destination=stderr ' "-c logging_collector=off " - "-c unix_socket_directories={unixsocketdir} {postgres_options}\" " + '-c unix_socket_directories={unixsocketdir} {postgres_options}" ' '-l "{logfile}" {startparams}' ) else: @@ -240,7 +240,7 @@ def _windows_terminate_process(self, sig: Optional[int] = None) -> None: """Terminate process on Windows.""" if self.process is None: return - + try: if platform.system() == "Windows": # On Windows, try to terminate gracefully first diff --git a/tests/test_windows_compatibility.py b/tests/test_windows_compatibility.py index 99bfdd61..322aedfe 100644 --- a/tests/test_windows_compatibility.py +++ b/tests/test_windows_compatibility.py @@ -1,10 +1,7 @@ """Test Windows compatibility fixes for pytest-postgresql.""" -import platform -import pytest -from unittest.mock import Mock, patch, MagicMock import subprocess -import signal +from unittest.mock import MagicMock, patch from pytest_postgresql.executor import PostgreSQLExecutor @@ -24,8 +21,8 @@ def test_get_base_command_windows(self): startparams="-w", dbname="test", ) - - with patch('platform.system', return_value='Windows'): + + with patch("platform.system", return_value="Windows"): command = executor._get_base_command() # Windows command should not have single quotes around stderr assert "log_destination=stderr" in command @@ -45,8 +42,8 @@ def test_get_base_command_unix(self): startparams="-w", dbname="test", ) - - with patch('platform.system', return_value='Linux'): + + with patch("platform.system", return_value="Linux"): command = executor._get_base_command() # Unix command should have single quotes around stderr assert "log_destination='stderr'" in command @@ -64,14 +61,14 @@ def test_windows_terminate_process(self): startparams="-w", dbname="test", ) - + # Mock process mock_process = MagicMock() executor.process = mock_process - - with patch('platform.system', return_value='Windows'): + + with patch("platform.system", return_value="Windows"): executor._windows_terminate_process() - + # Should call terminate first mock_process.terminate.assert_called_once() mock_process.wait.assert_called() @@ -88,15 +85,15 @@ def test_windows_terminate_process_force_kill(self): startparams="-w", dbname="test", ) - + # Mock process that times out mock_process = MagicMock() mock_process.wait.side_effect = [subprocess.TimeoutExpired(cmd="test", timeout=5), None] executor.process = mock_process - - with patch('platform.system', return_value='Windows'): + + with patch("platform.system", return_value="Windows"): executor._windows_terminate_process() - + # Should call terminate, wait (timeout), then kill, then wait again mock_process.terminate.assert_called_once() mock_process.kill.assert_called_once() @@ -114,14 +111,16 @@ def test_stop_method_windows(self): startparams="-w", dbname="test", ) - + # Mock subprocess and process - with patch('subprocess.check_output') as mock_subprocess, \ - patch('platform.system', return_value='Windows'), \ - patch.object(executor, '_windows_terminate_process') as mock_terminate: - + with ( + patch("subprocess.check_output") as mock_subprocess, + patch("platform.system", return_value="Windows"), + patch.object(executor, "_windows_terminate_process") as mock_terminate, + ): + result = executor.stop() - + # Should call pg_ctl stop and Windows terminate mock_subprocess.assert_called_once() mock_terminate.assert_called_once_with(None) @@ -139,15 +138,17 @@ def test_stop_method_unix(self): startparams="-w", dbname="test", ) - + # Mock subprocess and super().stop - with patch('subprocess.check_output') as mock_subprocess, \ - patch('platform.system', return_value='Linux'), \ - patch('pytest_postgresql.executor.TCPExecutor.stop') as mock_super_stop: - + with ( + patch("subprocess.check_output") as mock_subprocess, + patch("platform.system", return_value="Linux"), + patch("pytest_postgresql.executor.TCPExecutor.stop") as mock_super_stop, + ): + mock_super_stop.return_value = executor result = executor.stop() - + # Should call pg_ctl stop and parent class stop mock_subprocess.assert_called_once() mock_super_stop.assert_called_once_with(None, None) @@ -165,16 +166,20 @@ def test_stop_method_fallback_on_killpg_error(self): startparams="-w", dbname="test", ) - + # Mock subprocess and super().stop to raise AttributeError - with patch('subprocess.check_output') as mock_subprocess, \ - patch('platform.system', return_value='Linux'), \ - patch('pytest_postgresql.executor.TCPExecutor.stop', - side_effect=AttributeError("module 'os' has no attribute 'killpg'")), \ - patch.object(executor, '_windows_terminate_process') as mock_terminate: - + with ( + patch("subprocess.check_output") as mock_subprocess, + patch("platform.system", return_value="Linux"), + patch( + "pytest_postgresql.executor.TCPExecutor.stop", + side_effect=AttributeError("module 'os' has no attribute 'killpg'"), + ), + patch.object(executor, "_windows_terminate_process") as mock_terminate, + ): + result = executor.stop() - + # Should call pg_ctl stop, fail on super().stop, then use Windows terminate mock_subprocess.assert_called_once() mock_terminate.assert_called_once_with(None) @@ -182,7 +187,7 @@ def test_stop_method_fallback_on_killpg_error(self): def test_command_formatting_windows(self): """Test that command is properly formatted for Windows.""" - with patch('platform.system', return_value='Windows'): + with patch("platform.system", return_value="Windows"): executor = PostgreSQLExecutor( executable="C:/Program Files/PostgreSQL/bin/pg_ctl.exe", host="localhost", @@ -192,21 +197,21 @@ def test_command_formatting_windows(self): logfile="C:/temp/log.txt", startparams="-w -s", dbname="testdb", - postgres_options="-c shared_preload_libraries=test" + postgres_options="-c shared_preload_libraries=test", ) - + # The command should be properly formatted without quotes around stderr expected_parts = [ "C:/Program Files/PostgreSQL/bin/pg_ctl.exe start", '-D "C:/temp/data"', - "-o \"-F -p 5555 -c log_destination=stderr", + '-o "-F -p 5555 -c log_destination=stderr', "-c logging_collector=off", "-c unix_socket_directories=C:/temp/socket", - "-c shared_preload_libraries=test\"", + '-c shared_preload_libraries=test"', '-l "C:/temp/log.txt"', - "-w -s" + "-w -s", ] - + # Check if all expected parts are in the command command = executor.command for part in expected_parts: From ba90190231c5a38740f333bcd0fd515f38c81b99 Mon Sep 17 00:00:00 2001 From: tboy1337 Date: Thu, 4 Sep 2025 17:21:35 +0100 Subject: [PATCH 3/4] Refactor PostgreSQLExecutor for cross-platform command compatibility - Updated _get_base_command method to provide a unified command format for both Windows and Unix systems, eliminating the use of single quotes around configuration values. - Simplified process termination logic by removing platform checks from the _windows_terminate_process method, ensuring consistent behavior across operating systems. - Enhanced tests to verify that the base command format is consistent across platforms. --- pytest_postgresql/executor.py | 66 ++++++++++++++--------------- tests/test_windows_compatibility.py | 61 +++++++++++--------------- 2 files changed, 58 insertions(+), 69 deletions(-) diff --git a/pytest_postgresql/executor.py b/pytest_postgresql/executor.py index 256259d9..4a12ae0e 100644 --- a/pytest_postgresql/executor.py +++ b/pytest_postgresql/executor.py @@ -51,25 +51,17 @@ class PostgreSQLExecutor(TCPExecutor): """ def _get_base_command(self) -> str: - """Get the base PostgreSQL command, Windows-compatible.""" - if platform.system() == "Windows": - # Windows doesn't handle single quotes well in subprocess calls - return ( - '{executable} start -D "{datadir}" ' - '-o "-F -p {port} -c log_destination=stderr ' - "-c logging_collector=off " - '-c unix_socket_directories={unixsocketdir} {postgres_options}" ' - '-l "{logfile}" {startparams}' - ) - else: - # Unix/Linux systems work with single quotes - return ( - '{executable} start -D "{datadir}" ' - "-o \"-F -p {port} -c log_destination='stderr' " - "-c logging_collector=off " - "-c unix_socket_directories='{unixsocketdir}' {postgres_options}\" " - '-l "{logfile}" {startparams}' - ) + """Get the base PostgreSQL command, cross-platform compatible.""" + # Use unified format without single quotes around values + # This format works on both Windows and Unix systems since PostgreSQL + # configuration values without spaces don't require quotes + return ( + '{executable} start -D "{datadir}" ' + '-o "-F -p {port} -c log_destination=stderr ' + "-c logging_collector=off " + '-c unix_socket_directories={unixsocketdir} {postgres_options}" ' + '-l "{logfile}" {startparams}' + ) BASE_PROC_START_COMMAND = "" # Will be set dynamically @@ -242,20 +234,28 @@ def _windows_terminate_process(self, sig: Optional[int] = None) -> None: return try: - if platform.system() == "Windows": - # On Windows, try to terminate gracefully first - self.process.terminate() - # Give it a chance to terminate gracefully - try: - self.process.wait(timeout=5) - except subprocess.TimeoutExpired: - # If it doesn't terminate gracefully, force kill - self.process.kill() - self.process.wait() - else: - # On Unix systems, use the signal - actual_sig = sig or signal.SIGTERM - os.killpg(self.process.pid, actual_sig) + # On Windows, try to terminate gracefully first + self.process.terminate() + # Give it a chance to terminate gracefully + try: + self.process.wait(timeout=5) + except subprocess.TimeoutExpired: + # If it doesn't terminate gracefully, force kill + self.process.kill() + self.process.wait() + except (OSError, AttributeError): + # Process might already be dead or other issues + pass + + def _unix_terminate_process(self, sig: Optional[int] = None) -> None: + """Terminate process on Unix systems.""" + if self.process is None: + return + + try: + # On Unix systems, use the signal + actual_sig = sig or signal.SIGTERM + os.killpg(self.process.pid, actual_sig) except (OSError, AttributeError): # Process might already be dead or other issues pass diff --git a/tests/test_windows_compatibility.py b/tests/test_windows_compatibility.py index 322aedfe..3e2c5ba6 100644 --- a/tests/test_windows_compatibility.py +++ b/tests/test_windows_compatibility.py @@ -9,8 +9,8 @@ class TestWindowsCompatibility: """Test Windows-specific functionality.""" - def test_get_base_command_windows(self): - """Test that Windows base command doesn't use single quotes.""" + def test_get_base_command_unified(self): + """Test that base command is unified across platforms.""" executor = PostgreSQLExecutor( executable="/path/to/pg_ctl", host="localhost", @@ -22,32 +22,21 @@ def test_get_base_command_windows(self): dbname="test", ) + # Test that command format is consistent across platforms with patch("platform.system", return_value="Windows"): - command = executor._get_base_command() - # Windows command should not have single quotes around stderr - assert "log_destination=stderr" in command - assert "log_destination='stderr'" not in command - assert "unix_socket_directories={unixsocketdir}" in command - assert "unix_socket_directories='{unixsocketdir}'" not in command - - def test_get_base_command_unix(self): - """Test that Unix base command uses single quotes.""" - executor = PostgreSQLExecutor( - executable="/path/to/pg_ctl", - host="localhost", - port=5432, - datadir="/tmp/data", - unixsocketdir="/tmp/socket", - logfile="/tmp/log", - startparams="-w", - dbname="test", - ) + windows_command = executor._get_base_command() with patch("platform.system", return_value="Linux"): - command = executor._get_base_command() - # Unix command should have single quotes around stderr - assert "log_destination='stderr'" in command - assert "unix_socket_directories='{unixsocketdir}'" in command + unix_command = executor._get_base_command() + + # Both should be the same now + assert windows_command == unix_command + + # Both should use the simplified format without single quotes + assert "log_destination=stderr" in windows_command + assert "log_destination='stderr'" not in windows_command + assert "unix_socket_directories={unixsocketdir}" in windows_command + assert "unix_socket_directories='{unixsocketdir}'" not in windows_command def test_windows_terminate_process(self): """Test Windows process termination.""" @@ -66,12 +55,12 @@ def test_windows_terminate_process(self): mock_process = MagicMock() executor.process = mock_process - with patch("platform.system", return_value="Windows"): - executor._windows_terminate_process() + # No need to mock platform.system() since the method doesn't check it anymore + executor._windows_terminate_process() - # Should call terminate first - mock_process.terminate.assert_called_once() - mock_process.wait.assert_called() + # Should call terminate first + mock_process.terminate.assert_called_once() + mock_process.wait.assert_called() def test_windows_terminate_process_force_kill(self): """Test Windows process termination with force kill on timeout.""" @@ -91,13 +80,13 @@ def test_windows_terminate_process_force_kill(self): mock_process.wait.side_effect = [subprocess.TimeoutExpired(cmd="test", timeout=5), None] executor.process = mock_process - with patch("platform.system", return_value="Windows"): - executor._windows_terminate_process() + # No need to mock platform.system() since the method doesn't check it anymore + executor._windows_terminate_process() - # Should call terminate, wait (timeout), then kill, then wait again - mock_process.terminate.assert_called_once() - mock_process.kill.assert_called_once() - assert mock_process.wait.call_count == 2 + # Should call terminate, wait (timeout), then kill, then wait again + mock_process.terminate.assert_called_once() + mock_process.kill.assert_called_once() + assert mock_process.wait.call_count == 2 def test_stop_method_windows(self): """Test stop method on Windows.""" From 88617721debf38e2751e8dd6bb210278bc63871d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:21:48 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_windows_compatibility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_windows_compatibility.py b/tests/test_windows_compatibility.py index 3e2c5ba6..e29adcb5 100644 --- a/tests/test_windows_compatibility.py +++ b/tests/test_windows_compatibility.py @@ -31,7 +31,7 @@ def test_get_base_command_unified(self): # Both should be the same now assert windows_command == unix_command - + # Both should use the simplified format without single quotes assert "log_destination=stderr" in windows_command assert "log_destination='stderr'" not in windows_command