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).
56 lines
2.1 KiB
Python
56 lines
2.1 KiB
Python
"""Sibling regression test for #79178: background-PTY stdin must round-trip
|
|
surrogateescape content instead of crashing on the strict UTF-8 encode."""
|
|
import shlex
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from tools.process_registry import ProcessRegistry
|
|
|
|
|
|
@pytest.mark.platforms("linux")
|
|
def test_write_stdin_pty_surrogateescape_roundtrip(tmp_path):
|
|
registry = ProcessRegistry()
|
|
out = tmp_path / "out.bin"
|
|
script = tmp_path / "read_stdin.py"
|
|
# readline(): a PTY never delivers EOF, so read one line (canonical mode
|
|
# delivers it after the newline we send).
|
|
script.write_text(
|
|
f"import sys\nopen({str(out)!r}, 'wb').write(sys.stdin.buffer.readline())\n"
|
|
)
|
|
session = registry.spawn_local(
|
|
f"python3 {shlex.quote(str(script))}",
|
|
cwd=str(tmp_path),
|
|
use_pty=True,
|
|
)
|
|
if session._pty is None:
|
|
registry.kill_process(session.id)
|
|
pytest.skip("ptyprocess not available; PTY path not exercised")
|
|
try:
|
|
result = registry.write_stdin(
|
|
session.id, b"\xff".decode("utf-8", "surrogateescape") + "\n"
|
|
)
|
|
assert result["status"] == "ok", result
|
|
# Wait for the CONTENT, and not for the file to exist. The child runs
|
|
# open(out,'wb').write(...). open() creates the file empty, and the
|
|
# bytes arrive only after the PTY delivers the line. The previous wait
|
|
# stopped at out.exists(), which the empty file already satisfies, so
|
|
# the read returned b'' when the parent won that gap.
|
|
#
|
|
# On a 144-worker runner the gap is wide enough to lose every time.
|
|
# This test failed both attempts in CI, and not one time only. It also
|
|
# loses 6 times in 25 runs on an idle 16-core machine.
|
|
deadline = time.monotonic() + 30
|
|
got = b""
|
|
while time.monotonic() < deadline:
|
|
try:
|
|
got = out.read_bytes()
|
|
except FileNotFoundError:
|
|
got = b""
|
|
if got == b"\xff\n":
|
|
break
|
|
time.sleep(0.05)
|
|
assert got == b"\xff\n"
|
|
finally:
|
|
registry.kill_process(session.id)
|