This commit is contained in:
Levi Neuwirth 2026-05-18 12:24:35 -04:00
parent b6689a3927
commit 146583d32a
6 changed files with 49 additions and 17 deletions

View File

@ -61,7 +61,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: perf, memory, and 30s fuzz gates - name: perf, memory, and 30s fuzz gates
run: cargo test --release --test acceptance -- --ignored --nocapture run: cargo test --release --test acceptance -- --ignored --nocapture --test-threads=1
m4-perf-gates: m4-perf-gates:
name: M4 Perf Gates name: M4 Perf Gates
@ -71,7 +71,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: tree-sitter parse + highlight latency budgets - name: tree-sitter parse + highlight latency budgets
run: cargo test --release --test m4_acceptance -- --ignored --nocapture run: cargo test --release --test m4_acceptance -- --ignored --nocapture --test-threads=1
# m5-perf-gates: luajit-only by design. # m5-perf-gates: luajit-only by design.
# The keystroke-to-render gate measures protocol-path latency (Unix # The keystroke-to-render gate measures protocol-path latency (Unix

View File

@ -79,6 +79,18 @@ fn daemon_debug_enabled() -> bool {
std::env::var_os(PMACS_ATTACH_DEBUG).is_some_and(|v| !v.is_empty() && v != "0") std::env::var_os(PMACS_ATTACH_DEBUG).is_some_and(|v| !v.is_empty() && v != "0")
} }
#[cfg(any(target_os = "linux", target_os = "android"))]
fn peer_uid(stream: &UnixStream) -> Option<u32> {
nix::sys::socket::getsockopt(stream, nix::sys::socket::sockopt::PeerCredentials)
.ok()
.map(|cred| cred.uid())
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn peer_uid(_stream: &UnixStream) -> Option<u32> {
None
}
fn daemon_debug(msg: impl AsRef<str>) { fn daemon_debug(msg: impl AsRef<str>) {
if daemon_debug_enabled() { if daemon_debug_enabled() {
eprintln!("pmacs daemon debug: {}", msg.as_ref()); eprintln!("pmacs daemon debug: {}", msg.as_ref());
@ -698,15 +710,12 @@ fn per_attach_thread(
// reconnect → same color slot. If SO_PEERCRED fails (e.g., // reconnect → same color slot. If SO_PEERCRED fails (e.g.,
// non-Unix peer, kernel API unavailable), fall back to a // non-Unix peer, kernel API unavailable), fall back to a
// per-FrontendId slot (degrades to per-connection stability). // per-FrontendId slot (degrades to per-connection stability).
let color_slot = let color_slot = if let Some(uid) = peer_uid(&stream) {
match nix::sys::socket::getsockopt(&stream, nix::sys::socket::sockopt::PeerCredentials) { daemon_state.color_slot_for_uid(uid)
Ok(cred) => daemon_state.color_slot_for_uid(cred.uid()), } else {
Err(_) => {
// Fallback: use frontend_id-based slot; per-connection // Fallback: use frontend_id-based slot; per-connection
// stability only (no cross-reconnect within session). // stability only (no cross-reconnect within session).
u8::try_from(frontend_id.0 % (crate::overlay_color::PALETTE_LEN as u64)) u8::try_from(frontend_id.0 % (crate::overlay_color::PALETTE_LEN as u64)).unwrap_or(0)
.unwrap_or(0)
}
}; };
let session_state = let session_state =

View File

@ -389,7 +389,7 @@ const READER_SEND_POLL_INTERVAL: Duration = Duration::from_millis(50);
/// parser worker may still have already-read bytes in flight. This is /// parser worker may still have already-read bytes in flight. This is
/// not process termination grace; it is only the final output flush /// not process termination grace; it is only the final output flush
/// before the runtime handles are dropped. /// before the runtime handles are dropped.
const EXIT_OUTPUT_DRAIN_TIMEOUT: Duration = Duration::from_millis(250); const EXIT_OUTPUT_DRAIN_TIMEOUT: Duration = Duration::from_secs(2);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Supervisor // Supervisor

View File

@ -294,10 +294,18 @@ fn cli_attach_invokes_test_ssh_with_expected_argv() {
}); });
let lines: Vec<&str> = argv.lines().collect(); let lines: Vec<&str> = argv.lines().collect();
// build_ssh_command for `ssh:mac-studio` (no user, no instance) // build_ssh_command for `ssh:mac-studio` (no user, no instance)
// emits `[-T, host, "pmacs", "--daemon-attach"]`. // defaults to the stderr protocol channel and advertises the
// remote fd through env.
assert_eq!( assert_eq!(
lines, lines,
vec!["-T", "mac-studio", "pmacs", "--daemon-attach"], vec![
"-T",
"mac-studio",
"env",
"PMACS_ATTACH_PROTOCOL_FD=2",
"pmacs",
"--daemon-attach",
],
"argv shape: {lines:?}" "argv shape: {lines:?}"
); );
} }
@ -330,6 +338,8 @@ fn cli_attach_with_user_and_instance_name_passes_through_dash_l_and_dash_dash_so
"-l", "-l",
"alice", "alice",
"workstation", "workstation",
"env",
"PMACS_ATTACH_PROTOCOL_FD=2",
"pmacs", "pmacs",
"--daemon-attach", "--daemon-attach",
"--socket", "--socket",

View File

@ -68,6 +68,8 @@ use pmacs::protocol::{
mod common; mod common;
use common::pty::spawn_pmacs_in_pty; use common::pty::spawn_pmacs_in_pty;
const PMACS_ATTACH_SSH_PROTOCOL: &str = "PMACS_ATTACH_SSH_PROTOCOL";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Helpers // Helpers
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -145,6 +147,7 @@ fn handshake_retry_cap_fires_after_three_failed_handshakes() {
let output = Command::new(env!("CARGO_BIN_EXE_pmacs")) let output = Command::new(env!("CARGO_BIN_EXE_pmacs"))
.args(["--attach", "host"]) .args(["--attach", "host"])
.env(PMACS_TEST_SSH_BIN, &fake_ssh) .env(PMACS_TEST_SSH_BIN, &fake_ssh)
.env(PMACS_ATTACH_SSH_PROTOCOL, "stdout")
.env(PMACS_TEST_BACKOFF_SCALE_MS, "1") .env(PMACS_TEST_BACKOFF_SCALE_MS, "1")
.env("HOME", isolated_home) .env("HOME", isolated_home)
.env("XDG_CONFIG_HOME", isolated_home) .env("XDG_CONFIG_HOME", isolated_home)
@ -261,6 +264,7 @@ fn ssh_stderr_from_handshake_attempts_reaches_user() {
let output = Command::new(env!("CARGO_BIN_EXE_pmacs")) let output = Command::new(env!("CARGO_BIN_EXE_pmacs"))
.args(["--attach", "host"]) .args(["--attach", "host"])
.env(PMACS_TEST_SSH_BIN, &fake_ssh) .env(PMACS_TEST_SSH_BIN, &fake_ssh)
.env(PMACS_ATTACH_SSH_PROTOCOL, "stdout")
.env(PMACS_TEST_BACKOFF_SCALE_MS, "1") .env(PMACS_TEST_BACKOFF_SCALE_MS, "1")
.env("HOME", isolated_home) .env("HOME", isolated_home)
.env("XDG_CONFIG_HOME", isolated_home) .env("XDG_CONFIG_HOME", isolated_home)
@ -438,6 +442,7 @@ fn ssh_dies_mid_pump_then_reconnect_succeeds() {
&["--attach", "host"], &["--attach", "host"],
&[ &[
(PMACS_TEST_SSH_BIN, fx.fake_ssh.as_path()), (PMACS_TEST_SSH_BIN, fx.fake_ssh.as_path()),
(PMACS_ATTACH_SSH_PROTOCOL, Path::new("stdout")),
(PMACS_TEST_BACKOFF_SCALE_MS, Path::new("1")), (PMACS_TEST_BACKOFF_SCALE_MS, Path::new("1")),
("HOME", fx.isolated_home.as_path()), ("HOME", fx.isolated_home.as_path()),
("XDG_CONFIG_HOME", fx.isolated_home.as_path()), ("XDG_CONFIG_HOME", fx.isolated_home.as_path()),
@ -489,6 +494,7 @@ fn ctrl_c_during_reconnect_sleep_yields_clean_exit() {
&["--attach", "host"], &["--attach", "host"],
&[ &[
(PMACS_TEST_SSH_BIN, fx.fake_ssh.as_path()), (PMACS_TEST_SSH_BIN, fx.fake_ssh.as_path()),
(PMACS_ATTACH_SSH_PROTOCOL, Path::new("stdout")),
(PMACS_TEST_BACKOFF_SCALE_MS, Path::new("1000")), (PMACS_TEST_BACKOFF_SCALE_MS, Path::new("1000")),
("HOME", fx.isolated_home.as_path()), ("HOME", fx.isolated_home.as_path()),
("XDG_CONFIG_HOME", fx.isolated_home.as_path()), ("XDG_CONFIG_HOME", fx.isolated_home.as_path()),

View File

@ -32,11 +32,17 @@
use pmacs::editor::EditorState; use pmacs::editor::EditorState;
use std::fmt::Write as _; use std::fmt::Write as _;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Mutex; use std::sync::{Mutex, MutexGuard};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
static PUMP_TEST_LOCK: Mutex<()> = Mutex::new(()); static PUMP_TEST_LOCK: Mutex<()> = Mutex::new(());
fn pump_test_guard() -> MutexGuard<'static, ()> {
PUMP_TEST_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
/// Locate a shell binary for tests that require one. Returns the /// Locate a shell binary for tests that require one. Returns the
/// resolved path or `None` if the shell is neither at `PMACS_TEST_<NAME>` /// resolved path or `None` if the shell is neither at `PMACS_TEST_<NAME>`
/// nor on `PATH`. Per-test selective skipping (rather than skipping /// nor on `PATH`. Per-test selective skipping (rather than skipping
@ -70,6 +76,7 @@ fn locate_shell(name: &str) -> Option<PathBuf> {
/// Construct a fresh editor and run the given Lua chunk against it. /// Construct a fresh editor and run the given Lua chunk against it.
fn run(chunk: &str) { fn run(chunk: &str) {
let _guard = pump_test_guard();
let mut editor = EditorState::new(); let mut editor = EditorState::new();
editor editor
.lua_host .lua_host
@ -84,7 +91,7 @@ fn run(chunk: &str) {
/// `poll_until` pattern but routes through `tick_processes` so the M6.5 /// `poll_until` pattern but routes through `tick_processes` so the M6.5
/// after-tick contract is exercised end-to-end. /// after-tick contract is exercised end-to-end.
fn run_with_pump(setup_chunk: &str, predicate_chunk: &str, timeout_ms: u64) { fn run_with_pump(setup_chunk: &str, predicate_chunk: &str, timeout_ms: u64) {
let _guard = PUMP_TEST_LOCK.lock().expect("pump test lock"); let _guard = pump_test_guard();
let mut editor = EditorState::new(); let mut editor = EditorState::new();
editor editor
.lua_host .lua_host