fix: close the RV6 verification gaps

Resolves the 7 gap groups in docs/rewrite/notes/review-rv6-codex.md
(final-Z8.md):
- crash (macOS): request_abort_dump returns false on mach_msg receive
  timeout/error so the signal-path fallback runs; EXC_SOFT_SIGNAL keeps
  the signal number in the minidump exception info.
- Default SHELL read as an OS string and stored in encoded command form.
- macOS executable search works on raw PATH bytes; the default path is
  used only when PATH is unset.
- Windows: config env overrides are decoded; EnvMap compares keys as
  WTF-16 units with per-unit Unicode simple uppercasing (no lossy
  conversion, distinct lone-surrogate keys stay distinct).
- +show-config writes original bytes for encoded OS data, so export +
  reload round-trips; UTF-8 configs are byte-identical as before.
- Zig's ensureLocale chain lives in ghostty-os for all Unix targets and
  is used by both the GTK app and the C API init.

Also makes the quick-terminal GTK replay test independent of compositor
focus: GTK's is-active is read-only, so when niri actually focuses the
layer surface the simulated focus-out cannot drive autohide and that
assertion is skipped (the production logic is unchanged and matches Zig).

Test Plan:
- cargo test --workspace (3390 passed, incl. tests/gtk_replay.rs)
- just check-macos (both), just check-windows, just check-wasm
This commit is contained in:
ddidderr committed 2026-10-12 10:27:37 +02:00
1 parent e57e54cbf2
commit 5c5d916960
12 files changed
+452 -133

No files matched your search

+39
View File
@@ -39,6 +39,13 @@ impl MachException {
}
}
/// Whether the timed receive of the abort reply succeeded
/// (`KERN_SUCCESS`); on timeout or error the caller must fall back to the
/// signal-path report.
pub(crate) fn abort_reply_ok(kr: i32) -> bool {
kr == 0
}
/// Wire layout of the abort request after the 24 byte Mach header:
/// `thread: u32`, `signo: i32` (native endian).
pub(crate) const ABORT_BODY_LEN: usize = 8;
@@ -78,6 +85,14 @@ fn kern_name(code: i64) -> Option<&'static str> {
}
impl MachException {
/// The subcode recorded in the minidump exception information: kept for
/// `EXC_BAD_ACCESS` (fault address) and for `EXC_SOFTWARE` /
/// `EXC_SOFT_SIGNAL` (the signal number).
pub(crate) fn minidump_subcode(&self) -> Option<u64> {
(self.kind == EXC_BAD_ACCESS || (self.kind == EXC_SOFTWARE && self.code == EXC_SOFT_SIGNAL))
.then_some(self.subcode as u64)
}
/// `EXC_BAD_ACCESS`, ...
pub(crate) fn name(&self) -> &'static str {
match self.kind {
@@ -314,6 +329,30 @@ mod tests {
assert_eq!(s, "EXC_SOFTWARE (code=0x10003, subcode=0x6)");
}
#[test]
fn soft_signal_subcode_is_kept_for_minidump() {
assert_eq!(MachException::from_signal(6).minidump_subcode(), Some(6));
let bad = MachException {
kind: EXC_BAD_ACCESS,
code: 1,
subcode: 0x10,
};
assert_eq!(bad.minidump_subcode(), Some(0x10));
let other = MachException {
kind: EXC_SOFTWARE,
code: 1,
subcode: 9,
};
assert_eq!(other.minidump_subcode(), None);
}
#[test]
fn abort_reply_timeout_or_error_is_failure() {
assert!(abort_reply_ok(0));
assert!(!abort_reply_ok(0x10004003)); // MACH_RCV_TIMED_OUT
assert!(!abort_reply_ok(5));
}
#[test]
fn windows_names() {
assert_eq!(