test(edit): PR #110 round 3 — pin raw-byte predicate posture and top-level sets guard

Coverage pins only, no code changes.

Finding 1 (low): the predicate's raw-byte posture is now pinned from
the buffer side — `(` typed immediately before a lone 0xFF inserts no
closer. Verified non-vacuous: reverting char_at to nil-on-malformed
makes the test fail (nil reads as end-of-buffer and pairs before the
junk).

Finding 2 (low): the top-level container guard is pinned alongside
the per-entry cases — `pmacs.pair.sets = 42` pairs nothing and leaves
*errors* clean.

Framing synced to revision 6.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VoiEyuPjoBhvwACf8HAnLB
This commit is contained in:
Levi Neuwirth 2026-07-12 16:20:01 +01:00
parent ceaeb81386
commit 3085195794
2 changed files with 58 additions and 3 deletions

View File

@ -47,6 +47,13 @@ context-switching command no longer attributes them to the
destination buffer); and non-table set containers degrade
language→default→empty instead of throwing from the callback.
Revision 6: PR #110 round 3 — coverage pins only, no code changes:
the predicate's raw-byte posture is pinned from the buffer side
(`(` typed before a lone `0xFF` inserts no closer; a regression to
nil-on-malformed would read junk as end-of-buffer), and the
top-level non-table `pmacs.pair.sets` container is pinned alongside
the per-entry cases (pairs nothing, clean `*errors*`).
## Ground truth (as of `7e127ab`)
- **Dispatch is keymap-first for printables**`Char('(')` resolves
@ -488,9 +495,11 @@ facility is the only way a consumed record outlives its fan-out (R4).
and skips at byte-correct cursors. Ill-formed UTF-8 closers (R5) —
truncated `"(\xC2x"`, overlong `"(\xC0\xAF"`, surrogate
`"(\xED\xA0\x80"`, beyond-U+10FFFF `"(\xF5\x80\x80\x80"` — all
pair nothing. Non-table containers (R5): a string `default` pairs
nothing without erroring; a junk language entry falls back to the
default set.
pair nothing. Non-table containers (R5/R6): a string `default` and
a non-table `pmacs.pair.sets` itself pair nothing without erroring;
a junk language entry falls back to the default set. Malformed
BUFFER bytes (R6): `(` typed immediately before a lone `0xFF`
stays unpaired — junk is word-like, not EOL-like.
- Source-buffer relevance (R5): `'` typed in Rust with a
context-switching command landing in Python stays silent; the
inverse Python→Rust route still reports "source context changed".

View File

@ -322,6 +322,52 @@ fn multibyte_pair_entries_pair_and_skip() {
assert_eq!(cursor(&s), 4);
}
#[test]
fn opener_before_malformed_buffer_bytes_does_not_pair() {
// PR #110 round 3, finding 1: the predicate's raw-byte posture,
// pinned from the BUFFER side. A byte that begins no well-formed
// sequence (0xFF) sits after the cursor; `char_at` must surface
// it as a raw byte the predicate treats as word-like — a
// regression back to nil would read it as end-of-buffer and pair
// before the junk.
let mut s = editor_with("");
exec(&s, "pmacs.window.buffer():insert(0, string.char(0xFF))");
exec(&s, "pmacs.editor.goto_byte(0)");
type_str(&mut s, "(");
let len: i64 = eval(&s, "return pmacs.window.buffer():len()");
assert_eq!(len, 2, "opener + junk byte only — a closer would make 3");
let shape_ok: bool = eval(
&s,
"local b = pmacs.window.buffer() \
return b:slice(0, 1) == \"(\" and b:slice(1, 2):byte(1) == 0xFF",
);
assert!(
shape_ok,
"the opener stands alone before the malformed byte"
);
assert!(
!status(&s).contains("auto-pair"),
"declining the predicate is silent; got: {:?}",
status(&s)
);
}
#[test]
fn non_table_sets_container_fails_closed_without_erroring() {
// PR #110 round 3, finding 2: the TOP-LEVEL container guard,
// pinned. `set_for` degrades a non-table `pmacs.pair.sets` to the
// empty set; the callback must not throw.
let mut s = editor_with("");
exec(&s, "pmacs.pair.sets = 42");
type_str(&mut s, "(");
assert_eq!(buffer_text(&s), "(", "a non-table container pairs nothing");
let log = s.lua_host.errors_buffer_text();
assert!(
!log.contains("pair"),
"the pairing callback must not error over a config typo; *errors*:\n{log}"
);
}
#[test]
fn non_table_default_set_fails_closed_without_erroring() {
// PR #110 round 2, finding 3: a config typo assigning a STRING