fix(scripts): skip the portal probe on niri in inject-probe

The injector never offers the RemoteDesktop portal on niri, but inject-probe
ran the portal probe unguarded and reported portal_available: true there.
The probe now reuses the injector's _is_niri_session guard: on niri it skips
the portal and reports portal_skip_reason "niri" with portal_available false.
Off niri the probe runs and reports its answer as before.

Fixes #46
This commit is contained in:
2026-11-06 23:50:46 +05:30
parent a4a831c3c6
commit 3776c4de6e
3 changed files with 114 additions and 14 deletions
+6 -4
View File
@@ -63,10 +63,12 @@ with the installed build's interpreter.
version-probe result and timing, then a session-establishment attempt
(fast failure versus hang matters more than the version answer), plus
the resolved first rung and a no-emission `wtype ""` spawn cost as a
lower bound. End-to-end key-to-screen latency is deliberately not
taken here: emitting keystrokes into a live desktop from a background
run would disturb the session. It belongs to the hardware checklist
with a dedicated target window.
lower bound. On niri the portal probe is skipped, as the injector skips
it, and the report says so with `portal_skip_reason: "niri"` and
`portal_available: false`. End-to-end key-to-screen latency is
deliberately not taken here: emitting keystrokes into a live desktop
from a background run would disturb the session. It belongs to the
hardware checklist with a dedicated target window.
- **Thresholds for the model ladder:** derive free-memory cutoffs from
the maximum observed per-rung footprint (transcribe peak, not just
load) plus explicit headroom for the compositor and other GPU
+18 -10
View File
@@ -433,8 +433,11 @@ def cmd_inject_probe(args: argparse.Namespace) -> int:
Never emits keystrokes: end-to-end key-to-screen latency would type
into the live desktop, so it stays a hardware-checklist item (#32).
On niri the portal is skipped with the injector's own niri guard: the
injector never offers the portal there, so the report must not claim it.
"""
from vocalinux.text_injection.text_injector import TextInjector
from vocalinux.text_injection.text_injector import TextInjector, _is_niri_session
probe_text = "the quick brown fox jumps over the lazy dog 0123456789"
started = time.monotonic()
@@ -462,16 +465,20 @@ def cmd_inject_probe(args: argparse.Namespace) -> int:
env=host_env(),
)
wtype_roundtrips.append(time.monotonic() - begin)
try:
from vocalinux.text_injection.remote_desktop_portal import RemoteDesktopPortal
portal_available = False
portal_seconds = None
portal_skip_reason = "niri" if _is_niri_session() else None
if portal_skip_reason is None:
try:
from vocalinux.text_injection.remote_desktop_portal import RemoteDesktopPortal
portal_started = time.monotonic()
portal_available = RemoteDesktopPortal().probe()
portal_seconds = time.monotonic() - portal_started
except Exception as exc: # noqa: BLE001 - probe must never fail the pass
portal_available = False
portal_seconds = None
print(f"portal probe raised: {exc}", file=sys.stderr)
portal_started = time.monotonic()
portal_available = RemoteDesktopPortal().probe()
portal_seconds = time.monotonic() - portal_started
except Exception as exc: # noqa: BLE001 - probe must never fail the pass
portal_available = False
portal_seconds = None
print(f"portal probe raised: {exc}", file=sys.stderr)
print(
json.dumps(
{
@@ -481,6 +488,7 @@ def cmd_inject_probe(args: argparse.Namespace) -> int:
"wtype_empty_roundtrip_stats": describe(wtype_roundtrips),
"portal_available": portal_available,
"portal_probe_seconds": portal_seconds,
"portal_skip_reason": portal_skip_reason,
},
indent=2,
sort_keys=True,
+90
View File
@@ -7,7 +7,10 @@ helpers tested here. Tests run offline with fixture text only and never
touch audio hardware, the GPU, or the network.
"""
import argparse
import importlib.util
import json
import shutil
from pathlib import Path
import pytest
@@ -134,3 +137,90 @@ DBUS_TOGGLE_LOG = """\
def test_parse_wake_latencies_pairs_dbus_toggle_starts_only() -> None:
"""Only IDLE toggles open capture; a stop toggle never borrows the next run."""
assert PROTOCOL.parse_wake_latencies(DBUS_TOGGLE_LOG) == pytest.approx([0.146, 0.649])
#: Session variables the injector's niri guard reads, plus Wayland; cleared so
#: the host's own session (this machine runs niri) cannot leak into a test.
_SESSION_ENV = (
"NIRI_SOCKET",
"XDG_CURRENT_DESKTOP",
"XDG_SESSION_DESKTOP",
"DESKTOP_SESSION",
"WAYLAND_DISPLAY",
)
class _FakeInjector:
"""Stands in for TextInjector: a Wayland session that selected wtype."""
wayland_mode = True
wayland_tool = "wtype"
def _run_inject_probe(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
session_env: dict[str, str],
portal_answers: bool,
) -> tuple[dict, list[bool]]:
"""Run inject-probe under a fake session; return its report and portal probe calls."""
from vocalinux.text_injection import remote_desktop_portal, text_injector
probes: list[bool] = []
class _SpyPortal:
def probe(self) -> bool:
probes.append(True)
return portal_answers
for name in _SESSION_ENV:
monkeypatch.delenv(name, raising=False)
for name, value in session_env.items():
monkeypatch.setenv(name, value)
monkeypatch.setattr(text_injector, "TextInjector", _FakeInjector)
monkeypatch.setattr(remote_desktop_portal, "RemoteDesktopPortal", _SpyPortal)
monkeypatch.setattr(shutil, "which", lambda _name: None)
assert PROTOCOL.cmd_inject_probe(argparse.Namespace()) == 0
return json.loads(capsys.readouterr().out), probes
@pytest.mark.parametrize(
"session_env",
[
{"XDG_CURRENT_DESKTOP": "niri"},
{"NIRI_SOCKET": "/run/user/1000/niri.wayland-1.1.sock"},
],
)
def test_inject_probe_never_probes_portal_on_niri(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
session_env: dict[str, str],
) -> None:
"""The injector never offers the portal on niri, so the probe must not claim it.
Both niri signals are covered: the probe reuses the injector's guard, so a
session the injector treats as niri is reported the same way here.
"""
report, probes = _run_inject_probe(
monkeypatch, capsys, session_env=session_env, portal_answers=True
)
assert probes == []
assert report["portal_available"] is False
assert report["portal_probe_seconds"] is None
assert report["portal_skip_reason"] == "niri"
def test_inject_probe_reports_portal_answer_off_niri(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
"""Off niri the portal is probed and its answer is reported unchanged."""
report, probes = _run_inject_probe(
monkeypatch,
capsys,
session_env={"XDG_CURRENT_DESKTOP": "GNOME"},
portal_answers=True,
)
assert probes == [True]
assert report["portal_available"] is True
assert report["portal_skip_reason"] is None