No shims, one marker system. The full mechanical sweep:
- 165 test files converted: every @pytest.mark.<os>_only decorator and
pytestmark assignment is now platforms("<os>"). Docstring/comment
prose mentioning the trio rewritten to the platforms() vocabulary.
- conftest: _OS_MARKS, the legacy skip loop, the double-mark reject for
the trio, and the selector-tagging shim are deleted. The single
platforms() gate does all host gating; _reject_contradictory_platform_
marks replaces the old reject (one platforms() marker per test — a
module-level pytestmark stacked on a per-test marker is the historic
skipped-everywhere-green-everywhere failure and stays a hard error).
- pyproject: only the platforms marker is registered.
- list_os_marked_tests.py: rewritten for the single vocabulary — takes
a platform name (linux/macos/windows), matches quoted platforms(...)
specs including negated and any-of forms, exits nonzero on empty
selection. Its test file rewritten to match (bare identifiers must
not match; the spec must appear inside the string literal).
- tests.yml macOS lane: marker: macos feeds the lister; the pytest
selection is -m "platforms and not integration" — the marker name is
the selector, the conftest's per-test host skips are the gate.
- test_os_marker_gating.py rewritten for the new reject (the old file
tested deleted machinery).
- AGENTS.md / CONTRIBUTING.md updated to the single vocabulary.
Verified: full-tree compile; marker unit tests (36); lister tests;
macOS-lane simulation (21 files, 180 collected / 429 deselected on a
windows host); broad xdist slices through scripts/run_tests.sh
(367 + 1674 passed, zero refactor-attributable failures — the 6 red
tests in the second slice fail identically with the changes stashed).
81 lines
2.4 KiB
Python
81 lines
2.4 KiB
Python
"""Tests for native clipboard text write (hermes_cli/clipboard.py).
|
|
|
|
Mirrors the TUI's writeClipboardText fallback chain: pbcopy /
|
|
PowerShell Set-Clipboard / wl-copy / xclip / xsel, with OSC 52 left to
|
|
the caller when every backend fails.
|
|
"""
|
|
import base64
|
|
import subprocess
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from hermes_cli import clipboard as clip
|
|
|
|
|
|
def _completed(returncode=0):
|
|
return subprocess.CompletedProcess(args=[], returncode=returncode)
|
|
|
|
|
|
@pytest.mark.platforms("macos")
|
|
def test_darwin_uses_pbcopy():
|
|
with patch.object(clip.subprocess, "run", return_value=_completed()) as run:
|
|
assert clip.write_clipboard_text("hello") is True
|
|
argv = run.call_args[0][0]
|
|
assert argv == ["pbcopy"]
|
|
assert run.call_args[1]["input"] == b"hello"
|
|
|
|
|
|
@pytest.mark.platforms("linux")
|
|
def test_linux_falls_through_backends_until_success():
|
|
calls = []
|
|
|
|
def fake_run(argv, **kwargs):
|
|
calls.append(argv[0])
|
|
# xclip fails, xsel succeeds
|
|
return _completed(returncode=0 if argv[0] == "xsel" else 1)
|
|
|
|
with patch.object(clip, "_is_wsl", return_value=False), \
|
|
patch.dict(clip.os.environ, {}, clear=False), \
|
|
patch.object(clip.os.environ, "get", lambda k, d=None: None), \
|
|
patch.object(clip.subprocess, "run", side_effect=fake_run):
|
|
assert clip.write_clipboard_text("x") is True
|
|
assert calls == ["xclip", "xsel"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestOsc52MultiplexerWrapping:
|
|
"""CLI _write_osc52_clipboard must wrap for tmux/screen passthrough
|
|
(mirrors ui-tui/src/lib/osc52.ts wrapForMultiplexer)."""
|
|
|
|
def _capture_seq(self, env):
|
|
import io
|
|
from unittest.mock import patch as _patch
|
|
from cli import HermesCLI
|
|
|
|
cli_obj = HermesCLI.__new__(HermesCLI)
|
|
cli_obj._app = None
|
|
buf = io.StringIO()
|
|
with _patch.dict(clip.os.environ, env, clear=False), \
|
|
_patch("cli.sys.stdout", buf):
|
|
for var in ("TMUX", "STY"):
|
|
if var not in env:
|
|
clip.os.environ.pop(var, None)
|
|
cli_obj._write_osc52_clipboard("hello")
|
|
return buf.getvalue()
|
|
|
|
def test_tmux_wraps_in_dcs_passthrough(self, monkeypatch):
|
|
monkeypatch.setenv("TMUX", "/tmp/tmux-123/default,1,0")
|
|
monkeypatch.delenv("STY", raising=False)
|
|
seq = self._capture_seq({"TMUX": "/tmp/tmux-123/default,1,0"})
|
|
assert seq.startswith("\x1bPtmux;")
|
|
assert "]52;c;" in seq
|
|
assert seq.endswith("\x1b\\")
|
|
|
|
|