Files
MoviePilot/tests/test_agent_code_process.py
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

95 lines
3.3 KiB
Python

"""
进程组回收兼容 macOS 僵尸状态,同时保留真实权限失败。
"""
import errno
import os
from types import SimpleNamespace
from unittest.mock import AsyncMock, Mock
import pytest
from app.adapters.system.code import process as code_process
pytestmark = pytest.mark.skipif(os.name == 'nt', reason='仅验证 POSIX 进程组信号')
@pytest.mark.asyncio
@pytest.mark.parametrize('retry_error', [None, ProcessLookupError(errno.ESRCH, 'group gone')])
async def test_macos_reaps_root_then_signals_remaining_group(monkeypatch, retry_error):
"""
根回收后仍须发送组信号,兼顾存活后代和已消失的进程组。
"""
process = SimpleNamespace(pid=12345, wait=AsyncMock(return_value=-9))
denied = PermissionError(errno.EPERM, 'zombie group')
def signal_group(_pid, _signal):
if process.wait.await_count == 0:
raise denied
if retry_error is not None:
raise retry_error
signal = Mock(side_effect=signal_group)
monkeypatch.setattr(code_process, 'sys', SimpleNamespace(platform='darwin'))
monkeypatch.setattr(code_process.os, 'killpg', signal)
await code_process.kill_tree(process)
assert signal.call_count == 2
process.wait.assert_awaited()
@pytest.mark.asyncio
async def test_macos_repeated_permission_error_is_not_swallowed(monkeypatch):
"""
即使根已退出,组内其他进程的权限失败仍必须交回调用方。
"""
process = SimpleNamespace(pid=12345, wait=AsyncMock(return_value=0))
denied = PermissionError(errno.EPERM, 'live descendant denied')
signal = Mock(side_effect=denied)
monkeypatch.setattr(code_process, 'sys', SimpleNamespace(platform='darwin'))
monkeypatch.setattr(code_process.os, 'killpg', signal)
with pytest.raises(PermissionError) as raised:
await code_process.kill_tree(process)
assert raised.value is denied
assert signal.call_count == 2
@pytest.mark.asyncio
async def test_macos_unreaped_root_keeps_original_permission_error(monkeypatch):
"""
根无法回收时不能把清理权限失败伪装成脚本执行超时。
"""
process = SimpleNamespace(pid=12345, wait=AsyncMock(side_effect=TimeoutError))
denied = PermissionError(errno.EPERM, 'live root denied')
signal = Mock(side_effect=denied)
monkeypatch.setattr(code_process, 'sys', SimpleNamespace(platform='darwin'))
monkeypatch.setattr(code_process.os, 'killpg', signal)
with pytest.raises(PermissionError) as raised:
await code_process.kill_tree(process)
assert raised.value is denied
signal.assert_called_once()
@pytest.mark.asyncio
async def test_other_platform_permission_error_is_not_retried(monkeypatch):
"""
macOS 兼容分支不改变其他 POSIX 平台的权限错误处理。
"""
process = SimpleNamespace(pid=12345, wait=AsyncMock())
denied = PermissionError(errno.EPERM, 'permission denied')
signal = Mock(side_effect=denied)
monkeypatch.setattr(code_process, 'sys', SimpleNamespace(platform='linux'))
monkeypatch.setattr(code_process.os, 'killpg', signal)
with pytest.raises(PermissionError) as raised:
await code_process.kill_tree(process)
assert raised.value is denied
signal.assert_called_once()
process.wait.assert_not_awaited()