Keep the "psutil answers without a fork" and "ps stays the fallback when psutil is denied" cases; the dead-PID and agreement cases are change-detectors.
71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
"""``_read_process_cmdline`` asks the cheapest source that can answer, and still answers.
|
|
|
|
The gateway-identity check reads a live PID's command line, and `list_profiles` runs it per profile
|
|
with a live gateway — on every 5s roster poll, per connection. On macOS there is no ``/proc``, so
|
|
this used to fork ``ps`` every time (measured 4.2ms) when psutil answers the same string in 0.02ms.
|
|
psutil cannot always answer (macOS raises ``AccessDenied`` across users), so ``ps`` must remain the
|
|
fallback, not be replaced.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from gateway import status
|
|
|
|
|
|
@pytest.fixture
|
|
def no_proc(monkeypatch):
|
|
"""Force the non-Linux shape: /proc unreadable, so the source choice is psutil vs ps."""
|
|
real_read_bytes = status.Path.read_bytes
|
|
|
|
def _read_bytes(self, *a, **kw):
|
|
if str(self).startswith("/proc/"):
|
|
raise OSError("no /proc")
|
|
return real_read_bytes(self, *a, **kw)
|
|
|
|
monkeypatch.setattr(status.Path, "read_bytes", _read_bytes)
|
|
|
|
|
|
def _no_fork(monkeypatch):
|
|
"""Fail loudly if anything forks a subprocess."""
|
|
def _boom(*a, **kw):
|
|
raise AssertionError(f"forked a subprocess: {a[0] if a else kw}")
|
|
|
|
monkeypatch.setattr(status.subprocess, "run", _boom)
|
|
|
|
|
|
def test_psutil_answers_without_forking_ps(no_proc, monkeypatch):
|
|
_no_fork(monkeypatch)
|
|
|
|
cmdline = status._read_process_cmdline(os.getpid())
|
|
|
|
assert cmdline and sys.executable.split("/")[-1] in cmdline
|
|
|
|
|
|
def test_ps_still_answers_when_psutil_cannot(no_proc, monkeypatch):
|
|
"""macOS raises AccessDenied across users; ps still reports those, so it stays the fallback."""
|
|
import psutil
|
|
|
|
def _denied(_pid):
|
|
raise psutil.AccessDenied(_pid)
|
|
|
|
monkeypatch.setattr(psutil, "Process", _denied)
|
|
forked: list = []
|
|
real_run = status.subprocess.run
|
|
|
|
def _watching(cmd, *a, **kw):
|
|
forked.append(cmd)
|
|
return real_run(cmd, *a, **kw)
|
|
|
|
monkeypatch.setattr(status.subprocess, "run", _watching)
|
|
|
|
cmdline = status._read_process_cmdline(os.getpid())
|
|
|
|
assert cmdline, "the ps fallback must still answer when psutil refuses"
|
|
assert forked and forked[0][:2] == ["ps", "-p"]
|