test(isolation): migrate the corpus off the ambient roots
The mechanical half, riding on the census in the previous commit. * 342 in-process construction sites in 65 files now take `new_with_roots` / `open_with_roots` with `iso::roots()`. The isolated base is a pure function of `CARGO_TARGET_TMPDIR` — no counter, no `OnceLock` — so two copies of the module in one binary agree instead of racing, and the tree lives somewhere `cargo clean` owns rather than leaking into `/tmp` once per run. It is shared deliberately: materialization is content-gated and idempotent, so a per-test directory would repeat it ~330 times per run for a byte-identical result. * `journey_acceptance` keeps the ambient `EditorState::open`, because proving the production entry point has a caller is the whole of what that ratchet is for. Rev 2's "isolated by the environment its binary is launched with" was not a mechanism — cargo launches each test binary with the caller's environment, and a binary cannot re-point its own roots before its tests run. Each test is now a thin parent that re-execs this binary for its own name with controlled roots, and the child runs the body against production's call. Two pins guard it: the child asserts all four roots resolve inside the controlled base, and the suite asserts against its own source that it has not quietly taken the seam. The parent also asserts the child ran `1 passed` — a stale `--exact` filter would otherwise hollow the whole thing out silently. * The shared spawners take all five storage variables. `spawn_daemon_process_with_env` set `HOME` and `XDG_CONFIG_HOME` only; `HOME` is a FALLBACK, so it isolates a root only while the matching `XDG_*` is unset — the harness's apparent adequacy was a property of one developer's environment. The PTY spawner backfills whichever of the five its caller did not pin. The 10 direct `Command::new` daemon and attach spawns get the same treatment. Three suites had `mod common;` behind `#[cfg(feature = "crdt")]`; `common::iso` is needed in every build, so those are ungated. Files that already pull in `common` reach `iso` through a `use` rather than a second `#[path]` declaration — loading one file as two modules is `clippy::duplicate_mod`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lv428Fth9LRtffwJSsqH7T
This commit is contained in:
parent
fcaf0b36fa
commit
fb14dc9ec3
|
|
@ -74,7 +74,7 @@ fn status(s: &EditorState) -> String {
|
|||
|
||||
/// Fresh editor whose active scratch buffer holds `body`, cursor at 0.
|
||||
fn editor_with(body: &str) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
if !body.is_empty() {
|
||||
exec(&s, &format!("pmacs.window.buffer():insert(0, {body:?})"));
|
||||
}
|
||||
|
|
@ -503,3 +503,10 @@ fn isearch_ret_accepts_instead_of_inserting() {
|
|||
"RET during isearch accepts; no newline is inserted"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ fn fresh_state_dir() -> PathBuf {
|
|||
}
|
||||
|
||||
fn editor(state_dir: &std::path::Path) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -114,7 +114,7 @@ fn status(s: &EditorState) -> String {
|
|||
/// Fresh scratch-buffer editor whose buffer holds `body`, cursor at 0.
|
||||
/// No state dir / no files: scratch pairing uses the `default` set.
|
||||
fn editor_with(body: &str) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
if !body.is_empty() {
|
||||
exec(&s, &format!("pmacs.window.buffer():insert(0, {body:?})"));
|
||||
}
|
||||
|
|
@ -1378,3 +1378,10 @@ fn relocated_closer_first_did_change_carries_the_complete_effective_text() {
|
|||
text in the first didChange — never an opener-only intermediate"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ fn fresh_state_dir() -> PathBuf {
|
|||
}
|
||||
|
||||
fn editor(state_dir: &std::path::Path) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -861,3 +861,10 @@ fn before_quit_sweeps_synchronously_without_vetoing() {
|
|||
);
|
||||
std::fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const COLS: u32 = 60;
|
|||
const AREA_ROWS: u32 = ROWS - 1;
|
||||
|
||||
fn editor() -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
// Geometry is authoritative state, and a grid frontend's real frame
|
||||
// size IS its declaration. Every test that does not render declares
|
||||
|
|
@ -2571,7 +2571,7 @@ fn panel_hidden_never_describes_a_panel_that_no_longer_exists() {
|
|||
|
||||
#[test]
|
||||
fn unknown_geometry_is_not_twenty_four_by_eighty() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
let fid = FrontendId(77);
|
||||
attach_frontend(&s, fid, false);
|
||||
|
|
@ -2619,3 +2619,10 @@ fn cell_coord_helper_is_used() {
|
|||
// Keeps the CellCoord import honest for grid assertions above.
|
||||
assert_eq!(CellCoord::new(1, 2).row, 1);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const ROWS: u32 = 24;
|
|||
const COLS: u32 = 60;
|
||||
|
||||
fn editor() -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
s.sync_frame_geometry(FrontendId::LOCAL, CellSize::new(ROWS, COLS));
|
||||
s
|
||||
|
|
@ -896,3 +896,10 @@ fn a_provider_closing_the_document_split_still_clears_the_statusline() {
|
|||
the phase-1 document identity, even when a callback closed that window; got {msgs:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ impl Session {
|
|||
/// A semantic, panel-capable frontend with one document window and
|
||||
/// no geometry declared yet.
|
||||
fn new() -> Self {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, "pmacs.lsp.config = {}");
|
||||
let document = {
|
||||
let mut core = state.core.borrow_mut();
|
||||
|
|
@ -659,7 +659,7 @@ fn a2b1_a_rejected_declaration_reconciles_nothing() {
|
|||
fn a2b1_grid_allocator_exhaustion_clears_the_declaration_and_hides() {
|
||||
// The grid/LOCAL allocator, which mints its own epochs. `LOCAL` is
|
||||
// panel-capable, so this is the production path for a TUI.
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, "pmacs.lsp.config = {}");
|
||||
state.sync_frame_geometry(FrontendId::LOCAL, CellSize::new(ROWS, COLS));
|
||||
exec(
|
||||
|
|
@ -1340,3 +1340,10 @@ fn sweep_a_panel_wider_than_the_terminal_cap_still_presents_its_terminal() {
|
|||
);
|
||||
exec(&session.state, "pmacs.terminal.terminate(TERM_BUF)");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ fn fresh_state_dir() -> PathBuf {
|
|||
}
|
||||
|
||||
fn editor(state_dir: &std::path::Path) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -366,3 +366,10 @@ fn toggle_between_kills_breaks_the_kill_chain() {
|
|||
"C-k, M-;, C-k yields two ring entries (chain broken)"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -159,9 +159,31 @@ pub fn spawn_daemon_process_with_env(socket_path: &Path, env_vars: &[(&str, &str
|
|||
cmd.args(["--daemon", "--socket"])
|
||||
.arg(socket_path)
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::from(stderr));
|
||||
// All FIVE storage variables, not just `XDG_CONFIG_HOME`
|
||||
// (`docs/test-ambient-config-isolation-framing.md` §1.6a).
|
||||
//
|
||||
// `HOME` above is only a *fallback*: it isolates a root only while
|
||||
// the corresponding `XDG_*` variable is unset. On a developer
|
||||
// machine that exports `XDG_DATA_HOME`, a daemon given `HOME` alone
|
||||
// still materializes bundled packages into the real data root — so
|
||||
// the harness's apparent adequacy was a property of one machine's
|
||||
// environment, not of the harness. And `PMACS_STATE_HOME` outranks
|
||||
// `XDG_STATE_HOME`, so the four XDG variables alone leave a
|
||||
// higher-precedence state override live.
|
||||
//
|
||||
// Flat layout (every root at the socket's tempdir) so
|
||||
// `spawn_with_config`'s `<tempdir>/pmacs/init.lua` keeps loading
|
||||
// through the real `load_user_config` path.
|
||||
let roots = pmacs::bootstrap::BootstrapRoots::ambient()
|
||||
.with_config_root(isolated_home.to_path_buf())
|
||||
.with_data_root(isolated_home.to_path_buf())
|
||||
.with_state_root(isolated_home.to_path_buf())
|
||||
.with_cache_root(isolated_home.to_path_buf());
|
||||
for (key, value) in roots.child_env() {
|
||||
cmd.env(key, value);
|
||||
}
|
||||
for (key, value) in env_vars {
|
||||
cmd.env(key, value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,21 @@ pub fn spawn_pmacs_in_pty(args: &[&str], envs: &[(&str, &Path)], rows: u16, cols
|
|||
for arg in args {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
// Backfill any of the five storage variables the caller did not set
|
||||
// (`docs/test-ambient-config-isolation-framing.md` §1.6a). Callers
|
||||
// pin the roots their assertions read (a per-test `XDG_CONFIG_HOME`,
|
||||
// a `PMACS_STATE_HOME` they inspect); the rest would otherwise be
|
||||
// inherited, and the child would materialize bundled packages into
|
||||
// the developer's real data root. Caller entries win: this only
|
||||
// fills holes.
|
||||
let fallback = pmacs::bootstrap::BootstrapRoots::isolated_under(&super::iso::base());
|
||||
for (key, value) in fallback.child_env() {
|
||||
if envs.iter().any(|(k, _)| *k == key) {
|
||||
continue;
|
||||
}
|
||||
std::fs::create_dir_all(&value).expect("create isolated root for PTY child");
|
||||
cmd.env(key, value.as_os_str());
|
||||
}
|
||||
for (k, v) in envs {
|
||||
let mut value = OsString::new();
|
||||
value.push(v);
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ fn errors_buffer(s: &EditorState) -> String {
|
|||
/// Fresh editor with LSP spawning disabled (language detection still
|
||||
/// works; the after-load hook must not exec real servers).
|
||||
fn editor() -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
s
|
||||
}
|
||||
|
|
@ -2853,3 +2853,10 @@ fn j1b1_context_reports_the_cwd_a_run_would_use() {
|
|||
);
|
||||
assert_eq!(kind, "rust", "the kind is detected from the given cwd");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ fn probe(s: &EditorState) -> (String, bool, i64) {
|
|||
/// the prefix is replaced by the candidate in one step.
|
||||
#[test]
|
||||
fn typing_opens_popup_and_tab_accepts() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello_world ");
|
||||
let (_, visible, _) = probe(&s);
|
||||
assert!(
|
||||
|
|
@ -95,7 +95,7 @@ fn typing_opens_popup_and_tab_accepts() {
|
|||
/// candidate wins. Uses a custom provider for a deterministic order.
|
||||
#[test]
|
||||
fn ctrl_n_navigates_then_ret_accepts_second_candidate() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host
|
||||
.lua()
|
||||
.load(
|
||||
|
|
@ -133,7 +133,7 @@ fn ctrl_n_navigates_then_ret_accepts_second_candidate() {
|
|||
/// key self-inserts normally (the shadow is partial, not modal).
|
||||
#[test]
|
||||
fn esc_dismisses_and_typing_falls_through() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello_world he");
|
||||
let (_, visible, _) = probe(&s);
|
||||
assert!(visible);
|
||||
|
|
@ -154,7 +154,7 @@ fn esc_dismisses_and_typing_falls_through() {
|
|||
/// anchor.
|
||||
#[test]
|
||||
fn motion_before_anchor_closes_popup() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello_world he");
|
||||
let (_, visible, _) = probe(&s);
|
||||
assert!(visible);
|
||||
|
|
@ -169,7 +169,7 @@ fn motion_before_anchor_closes_popup() {
|
|||
/// the Q#C9 single-char signature rejects paste-shaped deltas.
|
||||
#[test]
|
||||
fn yank_shaped_edit_does_not_auto_open() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello_world hello");
|
||||
// Select the trailing word and cut it (C-w): the popup that was
|
||||
// open over `hello` closes as its word dies.
|
||||
|
|
@ -198,7 +198,7 @@ fn yank_shaped_edit_does_not_auto_open() {
|
|||
/// auto-open prefix threshold.
|
||||
#[test]
|
||||
fn at_point_command_opens_below_threshold() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello_world h");
|
||||
let (_, visible, _) = probe(&s);
|
||||
assert!(!visible, "a 1-char prefix stays below the auto-open bar");
|
||||
|
|
@ -223,7 +223,7 @@ fn at_point_command_opens_below_threshold() {
|
|||
/// `C-g` abort) reaches the dispatcher instead of the popup shadow.
|
||||
#[test]
|
||||
fn pending_prefix_dismisses_popup_and_keeps_dispatcher_control() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello_world he");
|
||||
let (_, visible, _) = probe(&s);
|
||||
assert!(visible);
|
||||
|
|
@ -258,7 +258,7 @@ fn pending_prefix_dismisses_popup_and_keeps_dispatcher_control() {
|
|||
/// index sweep meant the server was never asked).
|
||||
#[test]
|
||||
fn empty_sync_sweep_still_leaves_a_pending_session() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// A buffer whose only word is the one being typed: dabbrev is
|
||||
// structurally empty, no LSP attached. The popup cannot open...
|
||||
type_str(&mut s, "qz");
|
||||
|
|
@ -282,7 +282,7 @@ fn empty_sync_sweep_still_leaves_a_pending_session() {
|
|||
/// because the real LSP store is Rust-side.
|
||||
#[test]
|
||||
fn collect_scopes_lsp_candidates_to_ctx_uri() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let (scoped, unscoped): (u64, u64) = s
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -322,3 +322,10 @@ fn collect_scopes_lsp_candidates_to_ctx_uri() {
|
|||
assert_eq!(scoped, 1, "ctx.uri reaches Lua providers (9th arg)");
|
||||
assert_eq!(unscoped, 2, "no uri → all documents");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ fn fresh_state_dir() -> PathBuf {
|
|||
}
|
||||
|
||||
fn editor(state_dir: &std::path::Path) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -507,3 +507,10 @@ fn describe_setting_shows_a_buffer_local_override_when_one_exists() {
|
|||
"an existing buffer-local override must be reported, got {text:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ fn probe(s: &EditorState) -> (String, bool, i64) {
|
|||
|
||||
#[test]
|
||||
fn backspace_deletes_the_shift_selected_region() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello");
|
||||
|
||||
// Shift+Left three times: region [2, 5), cursor at 2.
|
||||
|
|
@ -85,7 +85,7 @@ fn backspace_deletes_the_shift_selected_region() {
|
|||
/// chorded deletion keys to this same dispatch path.
|
||||
#[test]
|
||||
fn ctrl_backspace_deletes_the_previous_word() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "alpha beta");
|
||||
|
||||
s.dispatch_key(
|
||||
|
|
@ -111,7 +111,7 @@ fn ctrl_backspace_deletes_the_previous_word() {
|
|||
|
||||
#[test]
|
||||
fn typing_replaces_the_shift_selected_region() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello");
|
||||
|
||||
// Select "llo" (region [2, 5), cursor at 2), then type 'X':
|
||||
|
|
@ -149,7 +149,7 @@ fn typing_replaces_the_shift_selected_region() {
|
|||
|
||||
#[test]
|
||||
fn type_over_is_a_single_undo_step() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello");
|
||||
|
||||
// Select "llo" (region [2, 5)) and type 'X' → "heX".
|
||||
|
|
@ -177,7 +177,7 @@ fn type_over_is_a_single_undo_step() {
|
|||
|
||||
#[test]
|
||||
fn delete_forward_deletes_the_shift_selected_region() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "world");
|
||||
|
||||
// Shift+Home-equivalent: extend left over the whole word.
|
||||
|
|
@ -200,3 +200,10 @@ fn delete_forward_deletes_the_shift_selected_region() {
|
|||
assert_eq!(text, "b", "no region ⇒ plain forward delete at cursor");
|
||||
assert_eq!(cursor, 0);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ fn fresh_state_dir() -> PathBuf {
|
|||
}
|
||||
|
||||
fn editor(state_dir: &std::path::Path) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -428,3 +428,10 @@ fn startup_gate_respects_file_arg_and_arming() {
|
|||
);
|
||||
std::fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ fn eval<T: mlua::FromLuaMulti>(s: &EditorState, src: &str) -> T {
|
|||
/// frame size *is* its geometry declaration, and the panel tests need
|
||||
/// one before any side window can be placed).
|
||||
fn editor() -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
s.sync_frame_geometry(FrontendId::LOCAL, CellSize::new(ROWS, COLS));
|
||||
s
|
||||
|
|
@ -1997,3 +1997,10 @@ fn dired_the_fold_refusal_names_the_read_only_lock() {
|
|||
"and not the sentence that is no longer true; got {st:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ fn status(s: &EditorState) -> String {
|
|||
/// typing RET would route through edit.newline-and-indent and clone
|
||||
/// leading whitespace), cursor at 0.
|
||||
fn editor_with(text: &str) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&s,
|
||||
&format!(
|
||||
|
|
@ -1155,7 +1155,7 @@ fn temp_path(name: &str) -> std::path::PathBuf {
|
|||
fn trim_on_save_defaults_off_and_writes_untouched_bytes() {
|
||||
let path = temp_path("off.txt");
|
||||
std::fs::write(&path, "x \ny\t\n").unwrap();
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let on: bool = eval(&s, "return pmacs.editops.trim_on_save()");
|
||||
assert!(!on, "default off");
|
||||
exec(
|
||||
|
|
@ -1175,7 +1175,7 @@ fn trim_on_save_defaults_off_and_writes_untouched_bytes() {
|
|||
fn trim_on_save_trims_the_written_bytes_before_later_callbacks() {
|
||||
let path = temp_path("on.txt");
|
||||
std::fs::write(&path, "x \ny\t\n").unwrap();
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.editops.trim_on_save(true)");
|
||||
// A callback registered AFTER editops' (load-time) hook observes
|
||||
// the fan-out order saveplace sees: post-trim text.
|
||||
|
|
@ -1215,7 +1215,7 @@ fn trim_on_save_trims_the_written_bytes_before_later_callbacks() {
|
|||
fn trim_on_save_failure_never_vetoes_the_save() {
|
||||
let path = temp_path("veto-immune.txt");
|
||||
std::fs::write(&path, "x \n").unwrap();
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.editops.trim_on_save(true)");
|
||||
exec(
|
||||
&s,
|
||||
|
|
@ -1246,7 +1246,7 @@ fn trim_on_save_failure_never_vetoes_the_save() {
|
|||
fn trim_on_save_unexpected_error_reports_and_still_saves() {
|
||||
let path = temp_path("unexpected.txt");
|
||||
std::fs::write(&path, "x \n").unwrap();
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.editops.trim_on_save(true)");
|
||||
// Capture the pmacs.error log (the m9_6 stub pattern — the
|
||||
// `if pmacs.error` branch is a no-op without it).
|
||||
|
|
@ -1293,7 +1293,7 @@ fn trim_on_save_unexpected_error_reports_and_still_saves() {
|
|||
fn another_callbacks_veto_is_not_masked_by_trim() {
|
||||
let path = temp_path("veto.txt");
|
||||
std::fs::write(&path, "x \n").unwrap();
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.editops.trim_on_save(true)");
|
||||
exec(
|
||||
&s,
|
||||
|
|
@ -1318,3 +1318,10 @@ fn another_callbacks_veto_is_not_masked_by_trim() {
|
|||
);
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ fn status(s: &EditorState) -> String {
|
|||
fn editor_in(dir: &std::path::Path) -> EditorState {
|
||||
let anchor = dir.join("anchor.txt");
|
||||
std::fs::write(&anchor, b"anchor\n").expect("write anchor");
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
let anchor_str = anchor.display().to_string();
|
||||
exec(
|
||||
|
|
@ -279,7 +279,7 @@ fn find_file_accepting_a_directory_reports_instead_of_raising() {
|
|||
/// stable, real candidate there.
|
||||
#[test]
|
||||
fn find_file_without_a_backing_path_roots_at_the_process_cwd() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.reopen_init_phase_for_testing();
|
||||
assert!(
|
||||
active_path(&s).is_none(),
|
||||
|
|
@ -352,7 +352,7 @@ fn find_file_expands_a_leading_tilde() {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.reopen_init_phase_for_testing();
|
||||
open_prompt(&mut s);
|
||||
// Contains a '/', so the typed text reaches on_accept verbatim.
|
||||
|
|
@ -371,3 +371,10 @@ fn find_file_expands_a_leading_tilde() {
|
|||
"the expansion must use $HOME; got {path} with HOME={home}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ fn insert_into(s: &EditorState, id: BufferId, text: &str) {
|
|||
|
||||
#[test]
|
||||
fn command_path_self_insert_unfolds_at_point() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let id = active_id(&s);
|
||||
insert_into(&s, id, "line0\nline1\nline2\nline3\n");
|
||||
// Fold the interior of lines 1..2: [end of line0, end of line2].
|
||||
|
|
@ -330,7 +330,7 @@ fn self_insert_at_head_line_end_does_not_unfold() {
|
|||
// `(start, end]` containment: a self-insert exactly at the end of the
|
||||
// head line (== range.start) is outside the fold — it must NOT unfold,
|
||||
// and the translator shifts the fold right so the char lands visible.
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let id = active_id(&s);
|
||||
insert_into(&s, id, "line0\nline1\nline2\nline3\n");
|
||||
let store = {
|
||||
|
|
@ -389,7 +389,7 @@ fn install_rust_parse(s: &EditorState, id: BufferId) {
|
|||
|
||||
#[test]
|
||||
fn folding_moves_point_to_head_line() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let id = active_id(&s);
|
||||
let src = "fn foo() {\n let x = 1;\n let y = 2;\n}\n";
|
||||
insert_into(&s, id, src);
|
||||
|
|
@ -413,7 +413,7 @@ fn folding_moves_point_to_head_line() {
|
|||
|
||||
#[test]
|
||||
fn data_api_validation() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// A plain document buffer with four lines.
|
||||
exec(
|
||||
&s,
|
||||
|
|
@ -465,7 +465,7 @@ fn data_api_validation() {
|
|||
|
||||
#[test]
|
||||
fn forget_drops_store_and_detaches_view() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let id = active_id(&s);
|
||||
insert_into(&s, id, "line0\nline1\nline2\n");
|
||||
let store = {
|
||||
|
|
@ -500,7 +500,7 @@ fn killing_a_buffer_through_the_real_path_purges_its_fold_store() {
|
|||
// assertion reads through the DEAD id on purpose: BufferIds are never
|
||||
// reused, so a stale id cannot alias a later buffer. Mirrors
|
||||
// config_registry's `killing_a_buffer_through_the_real_path_...`.
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&s,
|
||||
"b = pmacs.buffer.from_bytes('kill.rs', 'aaa\\nbbb\\nccc\\nddd\\n')",
|
||||
|
|
@ -530,7 +530,7 @@ fn close_all_command_moves_point_to_enclosing_head() {
|
|||
// Q#FD3 through the command surface: `fold.close-all` is interactive,
|
||||
// so when it collapses a top-level fold around the invoking point, the
|
||||
// point moves to that fold's head line (Finding 3, round 1).
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let id = active_id(&s);
|
||||
let src = "fn first() {\n a();\n b();\n}\nfn second() {\n c();\n d();\n}\n";
|
||||
insert_into(&s, id, src);
|
||||
|
|
@ -553,7 +553,7 @@ fn close_all_command_moves_point_to_enclosing_head() {
|
|||
fn stale_parse_tree_refuses_fold() {
|
||||
// Q#FD10: an edit after the parse leaves `pending_edit_count() > 0`,
|
||||
// so a fold command refuses (the settled coordinates are stale).
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let id = active_id(&s);
|
||||
let src = "fn foo() {\n let x = 1;\n let y = 2;\n}\n";
|
||||
insert_into(&s, id, src);
|
||||
|
|
@ -571,7 +571,7 @@ fn stale_parse_tree_refuses_fold() {
|
|||
fn read_only_buffer_is_rejected() {
|
||||
// Q#FD11's "normal document buffer" guard: terminals are read-only, so
|
||||
// a read-only buffer is not foldable.
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&s,
|
||||
"b = pmacs.buffer.from_bytes('ro.rs', 'aaa\\nbbb\\nccc\\n')",
|
||||
|
|
@ -592,7 +592,7 @@ fn read_only_buffer_is_rejected() {
|
|||
|
||||
#[test]
|
||||
fn unfold_normalizes_an_arbitrary_range_to_a_stored_fold() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&s,
|
||||
"b = pmacs.buffer.from_bytes('doc.rs', 'aaa\\nbbb\\nccc\\nddd\\n')",
|
||||
|
|
@ -608,3 +608,10 @@ fn unfold_normalizes_an_arbitrary_range_to_a_stored_fold() {
|
|||
let n: i64 = eval(&s, "return #pmacs.fold.folds(b)");
|
||||
assert_eq!(n, 0);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ fn end_of(line: usize) -> u64 {
|
|||
}
|
||||
|
||||
fn editor() -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
s
|
||||
}
|
||||
|
|
@ -1772,3 +1772,10 @@ fn wheel_over_an_inactive_unfolded_pane_uses_that_windows_map() {
|
|||
"the folded pane did not scroll"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ use pmacs::editor::EditorState;
|
|||
use pmacs::protocol::{ByteRange, FrontendId, InstanceMessage};
|
||||
use pmacs::semantic_render::SemanticRenderState;
|
||||
|
||||
#[cfg(feature = "crdt")]
|
||||
// Ungated: `common::iso` (isolated bootstrap roots) is needed in every
|
||||
// build, not only the CRDT one.
|
||||
mod common;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -43,7 +44,7 @@ fn eval<T: mlua::FromLuaMulti>(s: &EditorState, src: &str) -> T {
|
|||
|
||||
/// Fresh editor with LSP spawning disabled.
|
||||
fn editor() -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
s
|
||||
}
|
||||
|
|
@ -490,3 +491,11 @@ fn preference_set_before_attach_ships_on_the_first_frame() {
|
|||
"the first frame ships the pre-attach preference"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root. Re-exported rather than re-declared
|
||||
// with `#[path]` — this file already pulls in `common`, and loading one
|
||||
// source file as two modules is `clippy::duplicate_mod`.
|
||||
use common::iso;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@ fn non_crdt_root_rejects_gpu_before_socket_io_discovery_or_spawn() {
|
|||
.arg("--gpu")
|
||||
.env(TEST_GPU_OVERRIDE, &fake_gpu)
|
||||
.env("PMACS_TEST_MARKER", &marker)
|
||||
.env("XDG_CONFIG_HOME", temp.path())
|
||||
.env("XDG_DATA_HOME", temp.path())
|
||||
.env("XDG_STATE_HOME", temp.path())
|
||||
.env("PMACS_STATE_HOME", temp.path())
|
||||
.env("XDG_CACHE_HOME", temp.path())
|
||||
.env("XDG_RUNTIME_DIR", &runtime)
|
||||
.output()
|
||||
.expect("run non-CRDT pmacs --gpu");
|
||||
|
|
@ -61,6 +66,11 @@ fn non_crdt_root_rejects_gpu_before_socket_io_discovery_or_spawn() {
|
|||
.arg(&occupied_socket)
|
||||
.env(TEST_GPU_OVERRIDE, &fake_gpu)
|
||||
.env("PMACS_TEST_MARKER", &marker)
|
||||
.env("XDG_CONFIG_HOME", temp.path())
|
||||
.env("XDG_DATA_HOME", temp.path())
|
||||
.env("XDG_STATE_HOME", temp.path())
|
||||
.env("PMACS_STATE_HOME", temp.path())
|
||||
.env("XDG_CACHE_HOME", temp.path())
|
||||
.output()
|
||||
.expect("run non-CRDT pmacs --gpu against occupied socket");
|
||||
assert!(!occupied.status.success());
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ fn pump_async<F: Fn(&EditorState) -> bool>(state: &mut EditorState, predicate: F
|
|||
/// resolver would leave the Lua write-through + snapshot bridge unproven.
|
||||
#[test]
|
||||
fn lua_alias_override_resolves_on_async_parse() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
|
||||
// Add a bespoke fence alias from Lua (write-through to the registry).
|
||||
state
|
||||
|
|
@ -98,7 +98,7 @@ fn lua_alias_override_resolves_on_async_parse() {
|
|||
/// alias discriminates the fix: with the empty map it would not resolve.
|
||||
#[test]
|
||||
fn sync_parse_now_resolves_alias() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let src = b"# Doc\n\n```py\nx = 1\n```\n";
|
||||
let buf_id = state
|
||||
.lua_host
|
||||
|
|
@ -136,7 +136,7 @@ fn sync_parse_now_resolves_alias() {
|
|||
/// the file drops below the cap and exceeds it again.
|
||||
#[test]
|
||||
fn injection_cap_surfaced_once_and_rearms_via_lua() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// Capture pmacs.error messages into a Lua global.
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -305,3 +305,10 @@ fn many_paragraph_settle_under_budget_with_tail_covered() {
|
|||
"the final paragraph still receives an inline layer (no tail loss)"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -209,6 +209,85 @@ fn launch(path: &Path) -> EditorState {
|
|||
s
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ambient-roots isolation
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// This suite is the one place that must NOT take the
|
||||
// `EditorState::new_with_roots` / `open_with_roots` seam
|
||||
// (`docs/test-ambient-config-isolation-framing.md` §1.7). Its whole job
|
||||
// is to prove the production entry point is wired: an `open` arm with no
|
||||
// production caller passes every direct-call test, so the ratchet drives
|
||||
// the same `EditorState::open` that `pmacs FILE` does — parameterless,
|
||||
// ambient, exactly as production calls it.
|
||||
//
|
||||
// That leaves nothing in the process able to isolate it. Cargo launches
|
||||
// each integration-test binary with the caller's environment, and a
|
||||
// binary cannot re-point its own roots before its tests run:
|
||||
// `std::env::set_var` is `unsafe` and pmacs is
|
||||
// `#![forbid(unsafe_code)]`. "Isolated by the environment it is launched
|
||||
// with" is not a mechanism — it is a hope that whoever typed `cargo
|
||||
// test` wrapped it, which is the external workaround this lane exists to
|
||||
// delete.
|
||||
//
|
||||
// So each test is a thin **parent** that re-execs this same binary for
|
||||
// its own test name, with controlled roots in the child's environment.
|
||||
// The child sees the marker, runs the real body, and calls the ambient
|
||||
// entry point — production's call, against a controlled tree.
|
||||
|
||||
/// Marker the parent sets on the child. Its presence, not its value, is
|
||||
/// the signal.
|
||||
const ISOLATED_CHILD: &str = "PMACS_JOURNEY_ISOLATED_CHILD";
|
||||
|
||||
/// Re-exec this test binary for `name` under controlled bootstrap
|
||||
/// storage roots.
|
||||
///
|
||||
/// Returns `true` in the **parent** — the child has already run and been
|
||||
/// asserted, so the caller must return without doing anything itself —
|
||||
/// and `false` in the **child**, whose job is to run the body.
|
||||
#[must_use]
|
||||
fn reexec_isolated(name: &str) -> bool {
|
||||
if std::env::var_os(ISOLATED_CHILD).is_some() {
|
||||
return false;
|
||||
}
|
||||
// Under `target/`, not `/tmp`: nothing unlinks this (libtest has no
|
||||
// teardown hook), so it belongs somewhere `cargo clean` owns.
|
||||
let base = Path::new(env!("CARGO_TARGET_TMPDIR"))
|
||||
.join("journey-isolation")
|
||||
.join(name);
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
let roots = pmacs::bootstrap::BootstrapRoots::isolated_under(&base);
|
||||
let child_env = roots.child_env();
|
||||
for (_, dir) in &child_env {
|
||||
std::fs::create_dir_all(dir).expect("create controlled root");
|
||||
}
|
||||
let exe = std::env::current_exe().expect("current test binary");
|
||||
let output = std::process::Command::new(exe)
|
||||
.args(["--exact", name, "--nocapture", "--test-threads=1"])
|
||||
.env(ISOLATED_CHILD, "1")
|
||||
.envs(child_env)
|
||||
.output()
|
||||
.unwrap_or_else(|e| panic!("re-exec `{name}`: {e}"));
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"isolated child for `{name}` failed ({})\n--- child stdout ---\n{stdout}\
|
||||
\n--- child stderr ---\n{stderr}",
|
||||
output.status
|
||||
);
|
||||
// A filter that matches nothing exits 0. Without this the parent
|
||||
// would pass while running no test at all — the failure mode a
|
||||
// renamed test produces, and the one that would quietly hollow out
|
||||
// the whole ratchet.
|
||||
assert!(
|
||||
stdout.contains("1 passed"),
|
||||
"isolated child for `{name}` ran no test — the `--exact` filter went \
|
||||
stale against the function name\n--- child stdout ---\n{stdout}"
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 2 — launch unconfigured
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -216,6 +295,9 @@ fn launch(path: &Path) -> EditorState {
|
|||
/// **N** — the editor starts with no configuration and no arguments.
|
||||
#[test]
|
||||
fn journey_step2_launches_unconfigured_into_scratch() {
|
||||
if reexec_isolated("journey_step2_launches_unconfigured_into_scratch") {
|
||||
return;
|
||||
}
|
||||
let s = EditorState::new();
|
||||
assert_eq!(active_name(&s), "*scratch*");
|
||||
assert!(
|
||||
|
|
@ -236,6 +318,9 @@ fn journey_step2_launches_unconfigured_into_scratch() {
|
|||
/// `Err(EISDIR)` and `main` exited 1.
|
||||
#[test]
|
||||
fn journey_step3_opening_a_directory_lists_it() {
|
||||
if reexec_isolated("journey_step3_opening_a_directory_lists_it") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let s = launch(td.path());
|
||||
|
||||
|
|
@ -259,6 +344,9 @@ fn journey_step3_opening_a_directory_lists_it() {
|
|||
/// the assertion above while `pmacs .` still printed a diagnostic.
|
||||
#[test]
|
||||
fn journey_step3_directory_startup_reports_no_error() {
|
||||
if reexec_isolated("journey_step3_directory_startup_reports_no_error") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let s = launch(td.path());
|
||||
assert!(
|
||||
|
|
@ -274,6 +362,9 @@ fn journey_step3_directory_startup_reports_no_error() {
|
|||
#[test]
|
||||
fn journey_step3_unreadable_directory_reports_without_failing_startup() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
if reexec_isolated("journey_step3_unreadable_directory_reports_without_failing_startup") {
|
||||
return;
|
||||
}
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let locked = td.path().join("locked");
|
||||
std::fs::create_dir(&locked).expect("mkdir");
|
||||
|
|
@ -301,6 +392,9 @@ fn journey_step3_unreadable_directory_reports_without_failing_startup() {
|
|||
/// because no buffer is created and `set_buffer_path` never runs.
|
||||
#[test]
|
||||
fn journey_directory_resolver_receives_a_canonical_path() {
|
||||
if reexec_isolated("journey_directory_resolver_receives_a_canonical_path") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -331,6 +425,9 @@ fn journey_directory_resolver_receives_a_canonical_path() {
|
|||
/// fallback is a clearable slot rather than a builtin hook subscription.
|
||||
#[test]
|
||||
fn journey_unclaimed_directory_starts_successfully_with_a_status() {
|
||||
if reexec_isolated("journey_unclaimed_directory_starts_successfully_with_a_status") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -360,6 +457,9 @@ fn journey_unclaimed_directory_starts_successfully_with_a_status() {
|
|||
/// a claim suppresses the fallback.
|
||||
#[test]
|
||||
fn journey_resolver_chain_is_first_claimant_wins() {
|
||||
if reexec_isolated("journey_resolver_chain_is_first_claimant_wins") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -395,6 +495,9 @@ fn journey_resolver_chain_is_first_claimant_wins() {
|
|||
/// `proceed == false`.
|
||||
#[test]
|
||||
fn journey_a_raising_resolver_suppresses_the_fallback_and_reports() {
|
||||
if reexec_isolated("journey_a_raising_resolver_suppresses_the_fallback_and_reports") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -458,6 +561,9 @@ const COMPETITOR: FrontendId = FrontendId(7);
|
|||
/// ambient display: the file then appears in the competitor's window.
|
||||
#[test]
|
||||
fn commit_to_delivers_to_the_requesting_frontend_not_the_ambient_one() {
|
||||
if reexec_isolated("commit_to_delivers_to_the_requesting_frontend_not_the_ambient_one") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -519,6 +625,9 @@ fn commit_to_delivers_to_the_requesting_frontend_not_the_ambient_one() {
|
|||
/// `acting_frontend`, or by reordering it after the interactive origin.
|
||||
#[test]
|
||||
fn commit_to_outranks_an_interactive_origin() {
|
||||
if reexec_isolated("commit_to_outranks_an_interactive_origin") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -586,6 +695,9 @@ fn commit_to_outranks_an_interactive_origin() {
|
|||
/// `display`, or by reading `prev` from the ambient window.
|
||||
#[test]
|
||||
fn a_background_open_uses_the_captured_window_not_the_selected_one() {
|
||||
if reexec_isolated("a_background_open_uses_the_captured_window_not_the_selected_one") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -652,6 +764,9 @@ fn a_background_open_uses_the_captured_window_not_the_selected_one() {
|
|||
/// competitor and the assertion fails from the other direction).
|
||||
#[test]
|
||||
fn commit_to_scopes_and_restores_on_a_normal_return() {
|
||||
if reexec_isolated("commit_to_scopes_and_restores_on_a_normal_return") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -701,6 +816,9 @@ fn commit_to_scopes_and_restores_on_a_normal_return() {
|
|||
/// through the scope, or by restoring on the success path only.
|
||||
#[test]
|
||||
fn commit_to_restores_when_the_callback_raises() {
|
||||
if reexec_isolated("commit_to_restores_when_the_callback_raises") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -744,6 +862,9 @@ fn commit_to_restores_when_the_callback_raises() {
|
|||
/// `<no raise>`.
|
||||
#[test]
|
||||
fn commit_to_refuses_an_await_and_restores() {
|
||||
if reexec_isolated("commit_to_refuses_an_await_and_restores") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -795,6 +916,9 @@ fn commit_to_refuses_an_await_and_restores() {
|
|||
/// userdata after invoking the callback.
|
||||
#[test]
|
||||
fn commit_to_refuses_a_forged_destination() {
|
||||
if reexec_isolated("commit_to_refuses_a_forged_destination") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -831,6 +955,9 @@ fn commit_to_refuses_a_forged_destination() {
|
|||
/// a shared mutable table.
|
||||
#[test]
|
||||
fn a_declining_listener_cannot_redirect_the_destination() {
|
||||
if reexec_isolated("a_declining_listener_cannot_redirect_the_destination") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -907,6 +1034,9 @@ fn a_declining_listener_cannot_redirect_the_destination() {
|
|||
/// distinguish "validates" from "validates in time".
|
||||
#[test]
|
||||
fn preservation_a_failed_precondition_never_reaches_the_callback() {
|
||||
if reexec_isolated("preservation_a_failed_precondition_never_reaches_the_callback") {
|
||||
return;
|
||||
}
|
||||
// (label, Lua that breaks the precondition, expected reason fragment)
|
||||
let cases: [(&str, &str, &str); 4] = [
|
||||
(
|
||||
|
|
@ -994,6 +1124,9 @@ fn preservation_a_failed_precondition_never_reaches_the_callback() {
|
|||
/// (window-only validation). The dired buffer then replaces the user's.
|
||||
#[test]
|
||||
fn preservation_a_stale_destination_loses_to_the_users_newer_buffer() {
|
||||
if reexec_isolated("preservation_a_stale_destination_loses_to_the_users_newer_buffer") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -1062,6 +1195,11 @@ fn preservation_a_stale_destination_loses_to_the_users_newer_buffer() {
|
|||
/// `*competitor*`.
|
||||
#[test]
|
||||
fn preservation_dired_captures_prev_from_the_destination_not_the_ambient_frontend() {
|
||||
if reexec_isolated(
|
||||
"preservation_dired_captures_prev_from_the_destination_not_the_ambient_frontend",
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -1117,6 +1255,9 @@ fn preservation_dired_captures_prev_from_the_destination_not_the_ambient_fronten
|
|||
/// the read-only contract rather than pin the journey.
|
||||
#[test]
|
||||
fn journey_step5_editing_a_file_reached_through_the_directory() {
|
||||
if reexec_isolated("journey_step5_editing_a_file_reached_through_the_directory") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = launch(td.path());
|
||||
assert!(active_name(&s).starts_with("*dired:"));
|
||||
|
|
@ -1169,6 +1310,9 @@ fn journey_step5_editing_a_file_reached_through_the_directory() {
|
|||
/// preserve is which window shows the file, and that is what this pins.
|
||||
#[test]
|
||||
fn preservation_opening_a_file_shows_it_in_the_active_window() {
|
||||
if reexec_isolated("preservation_opening_a_file_shows_it_in_the_active_window") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let target = td.path().join("alpha.txt");
|
||||
let s = EditorState::open(target.clone()).expect("open");
|
||||
|
|
@ -1204,6 +1348,9 @@ fn preservation_opening_a_file_shows_it_in_the_active_window() {
|
|||
/// failure mode is a hard error on a perfectly ordinary gesture.
|
||||
#[test]
|
||||
fn preservation_a_missing_path_becomes_a_new_file_buffer() {
|
||||
if reexec_isolated("preservation_a_missing_path_becomes_a_new_file_buffer") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let fresh = td.path().join("not-yet.txt");
|
||||
let s = EditorState::open(fresh.clone()).expect("a missing path is not an error");
|
||||
|
|
@ -1223,6 +1370,9 @@ fn preservation_a_missing_path_becomes_a_new_file_buffer() {
|
|||
#[test]
|
||||
fn preservation_an_unreadable_file_reports_with_its_path() {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
if reexec_isolated("preservation_an_unreadable_file_reports_with_its_path") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let locked = td.path().join("locked.txt");
|
||||
std::fs::write(&locked, b"secret\n").expect("write");
|
||||
|
|
@ -1270,6 +1420,9 @@ fn preservation_an_unreadable_file_reports_with_its_path() {
|
|||
/// real accept path; this one pins the primitive and the window state.
|
||||
#[test]
|
||||
fn preservation_display_file_still_refuses_a_directory() {
|
||||
if reexec_isolated("preservation_display_file_still_refuses_a_directory") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::open(td.path().join("alpha.txt")).expect("open");
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -1440,6 +1593,9 @@ fn walk_to_open_file(dir: &Path, name: &str) -> EditorState {
|
|||
/// `COHERENCE.md` says is missing.
|
||||
#[test]
|
||||
fn journey_step9_the_compile_chord_opens_the_prompt() {
|
||||
if reexec_isolated("journey_step9_the_compile_chord_opens_the_prompt") {
|
||||
return;
|
||||
}
|
||||
let td = cargo_project();
|
||||
let mut s = walk_to_open_file(td.path(), "Cargo.toml");
|
||||
press_compile_chord(&mut s);
|
||||
|
|
@ -1453,6 +1609,9 @@ fn journey_step9_the_compile_chord_opens_the_prompt() {
|
|||
/// **N** — the prompt is prefilled from the detected project kind.
|
||||
#[test]
|
||||
fn journey_step9_the_prompt_is_prefilled_for_a_cargo_project() {
|
||||
if reexec_isolated("journey_step9_the_prompt_is_prefilled_for_a_cargo_project") {
|
||||
return;
|
||||
}
|
||||
let td = cargo_project();
|
||||
let mut s = walk_to_open_file(td.path(), "Cargo.toml");
|
||||
press_compile_chord(&mut s);
|
||||
|
|
@ -1472,6 +1631,9 @@ fn journey_step9_the_prompt_is_prefilled_for_a_cargo_project() {
|
|||
/// which is what re-resolving at accept time looks like.
|
||||
#[test]
|
||||
fn journey_step9_the_prompt_runs_in_the_directory_it_captured() {
|
||||
if reexec_isolated("journey_step9_the_prompt_runs_in_the_directory_it_captured") {
|
||||
return;
|
||||
}
|
||||
let a = cargo_project();
|
||||
let b = project();
|
||||
let mut s = walk_to_open_file(a.path(), "Cargo.toml");
|
||||
|
|
@ -1521,6 +1683,9 @@ fn journey_step9_the_prompt_runs_in_the_directory_it_captured() {
|
|||
/// real process.
|
||||
#[test]
|
||||
fn journey_step9_the_offered_command_builds_the_project() {
|
||||
if reexec_isolated("journey_step9_the_offered_command_builds_the_project") {
|
||||
return;
|
||||
}
|
||||
if !binary_available("cargo") {
|
||||
assert!(
|
||||
std::env::var_os("PMACS_REQUIRE_CARGO_BUILD").is_none(),
|
||||
|
|
@ -1574,6 +1739,9 @@ fn journey_step9_the_offered_command_builds_the_project() {
|
|||
/// from it yields `node` again and this pin stays green.
|
||||
#[test]
|
||||
fn journey_step9_a_nested_project_gets_its_own_kind_not_the_outer_one() {
|
||||
if reexec_isolated("journey_step9_a_nested_project_gets_its_own_kind_not_the_outer_one") {
|
||||
return;
|
||||
}
|
||||
let outer = cargo_project();
|
||||
let sub = outer.path().join("sub");
|
||||
std::fs::create_dir_all(&sub).expect("mkdir sub");
|
||||
|
|
@ -1610,6 +1778,9 @@ fn journey_step9_a_nested_project_gets_its_own_kind_not_the_outer_one() {
|
|||
/// runner's cwd and pinning it would pin the environment.
|
||||
#[test]
|
||||
fn journey_step9_the_compile_context_is_total_even_with_no_file_open() {
|
||||
if reexec_isolated("journey_step9_the_compile_context_is_total_even_with_no_file_open") {
|
||||
return;
|
||||
}
|
||||
let td = cargo_project();
|
||||
let s = launch(td.path());
|
||||
assert!(
|
||||
|
|
@ -1638,6 +1809,9 @@ fn journey_step9_the_compile_context_is_total_even_with_no_file_open() {
|
|||
/// is reordering the precedence chain to put `defaults[kind]` first.
|
||||
#[test]
|
||||
fn journey_step9_preservation_the_last_command_outranks_the_default() {
|
||||
if reexec_isolated("journey_step9_preservation_the_last_command_outranks_the_default") {
|
||||
return;
|
||||
}
|
||||
let td = cargo_project();
|
||||
let mut s = walk_to_open_file(td.path(), "Cargo.toml");
|
||||
exec(&s, "pmacs.compile.run('true')");
|
||||
|
|
@ -1660,6 +1834,9 @@ fn journey_step9_preservation_the_last_command_outranks_the_default() {
|
|||
/// over one of these sequences.
|
||||
#[test]
|
||||
fn journey_step9_preservation_the_existing_compile_bindings_survive() {
|
||||
if reexec_isolated("journey_step9_preservation_the_existing_compile_bindings_survive") {
|
||||
return;
|
||||
}
|
||||
let td = cargo_project();
|
||||
let s = walk_to_open_file(td.path(), "Cargo.toml");
|
||||
for (sequence, command) in [
|
||||
|
|
@ -1705,6 +1882,9 @@ fn binary_available(name: &str) -> bool {
|
|||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn journey_step9_the_compile_directory_is_detection_canonical() {
|
||||
if reexec_isolated("journey_step9_the_compile_directory_is_detection_canonical") {
|
||||
return;
|
||||
}
|
||||
let parent = tempfile::tempdir().expect("tempdir");
|
||||
let real = parent.path().join("real");
|
||||
std::fs::create_dir_all(real.join("src")).expect("mkdir real");
|
||||
|
|
@ -1804,6 +1984,9 @@ fn start_local() -> EditorState {
|
|||
/// `prepare_startup` — the mutation every by-hand pin would survive.
|
||||
#[test]
|
||||
fn journey_step4_a_no_target_launch_greets_in_scratch() {
|
||||
if reexec_isolated("journey_step4_a_no_target_launch_greets_in_scratch") {
|
||||
return;
|
||||
}
|
||||
let s = start_local();
|
||||
|
||||
// Preconditions asserted, not assumed (framing §3.2b): a developer
|
||||
|
|
@ -1835,6 +2018,9 @@ fn journey_step4_a_no_target_launch_greets_in_scratch() {
|
|||
/// is two chords and nothing in the rendered text marks the boundary.
|
||||
#[test]
|
||||
fn journey_step4_every_advertised_key_is_bound() {
|
||||
if reexec_isolated("journey_step4_every_advertised_key_is_bound") {
|
||||
return;
|
||||
}
|
||||
let s = start_local();
|
||||
let entries = welcome_entries(&s);
|
||||
assert!(
|
||||
|
|
@ -1858,6 +2044,9 @@ fn journey_step4_every_advertised_key_is_bound() {
|
|||
/// Pin 2 alone would pass if rendering silently dropped one.
|
||||
#[test]
|
||||
fn journey_step4_the_rendered_welcome_contains_every_entry() {
|
||||
if reexec_isolated("journey_step4_the_rendered_welcome_contains_every_entry") {
|
||||
return;
|
||||
}
|
||||
let s = start_local();
|
||||
let text = active_text(&s);
|
||||
for (keys, label) in welcome_entries(&s) {
|
||||
|
|
@ -1884,6 +2073,9 @@ fn journey_step4_the_rendered_welcome_contains_every_entry() {
|
|||
/// so nothing about the accepted value survives afterwards.
|
||||
#[test]
|
||||
fn journey_step4_m_x_help_renders_the_cheat_sheet() {
|
||||
if reexec_isolated("journey_step4_m_x_help_renders_the_cheat_sheet") {
|
||||
return;
|
||||
}
|
||||
let mut s = start_local();
|
||||
|
||||
s.dispatch_key(
|
||||
|
|
@ -1922,6 +2114,9 @@ fn journey_step4_m_x_help_renders_the_cheat_sheet() {
|
|||
/// would lift read-only, discard history, and fail the insert.
|
||||
#[test]
|
||||
fn journey_step4_preservation_the_greeted_scratch_is_editable_and_clean() {
|
||||
if reexec_isolated("journey_step4_preservation_the_greeted_scratch_is_editable_and_clean") {
|
||||
return;
|
||||
}
|
||||
let mut s = start_local();
|
||||
assert!(
|
||||
eval::<bool>(
|
||||
|
|
@ -1942,6 +2137,9 @@ fn journey_step4_preservation_the_greeted_scratch_is_editable_and_clean() {
|
|||
/// **P** — a file target does not greet.
|
||||
#[test]
|
||||
fn journey_step4_preservation_a_file_target_does_not_greet() {
|
||||
if reexec_isolated("journey_step4_preservation_a_file_target_does_not_greet") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let path = td.path().join("alpha.txt");
|
||||
let s = match pmacs::editor::prepare_startup(Some(path.clone())).expect("startup") {
|
||||
|
|
@ -1965,6 +2163,9 @@ fn journey_step4_preservation_a_file_target_does_not_greet() {
|
|||
/// still exists to be wrongly greeted.
|
||||
#[test]
|
||||
fn journey_step4_preservation_a_directory_target_does_not_greet() {
|
||||
if reexec_isolated("journey_step4_preservation_a_directory_target_does_not_greet") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s =
|
||||
match pmacs::editor::prepare_startup(Some(td.path().to_path_buf())).expect("startup") {
|
||||
|
|
@ -1979,6 +2180,9 @@ fn journey_step4_preservation_a_directory_target_does_not_greet() {
|
|||
/// **P** — a non-empty `*scratch*` is never overwritten.
|
||||
#[test]
|
||||
fn journey_step4_preservation_existing_scratch_content_survives() {
|
||||
if reexec_isolated("journey_step4_preservation_existing_scratch_content_survives") {
|
||||
return;
|
||||
}
|
||||
let mut s = EditorState::new();
|
||||
exec(&s, "pmacs.window.buffer():insert(0, 'user content')");
|
||||
s.finalize_local_launch(false);
|
||||
|
|
@ -1993,6 +2197,9 @@ fn journey_step4_preservation_existing_scratch_content_survives() {
|
|||
/// desktop restore having put something else in front.
|
||||
#[test]
|
||||
fn journey_step4_preservation_a_backgrounded_scratch_is_not_greeted() {
|
||||
if reexec_isolated("journey_step4_preservation_a_backgrounded_scratch_is_not_greeted") {
|
||||
return;
|
||||
}
|
||||
let td = project();
|
||||
let mut s = EditorState::new();
|
||||
exec(
|
||||
|
|
@ -2019,6 +2226,9 @@ fn journey_step4_preservation_a_backgrounded_scratch_is_not_greeted() {
|
|||
/// reached in production.
|
||||
#[test]
|
||||
fn journey_step4_preservation_constructors_never_greet() {
|
||||
if reexec_isolated("journey_step4_preservation_constructors_never_greet") {
|
||||
return;
|
||||
}
|
||||
let bare = EditorState::new();
|
||||
assert_eq!(scratch_text(&bare), "", "EditorState::new must not greet");
|
||||
|
||||
|
|
@ -2078,6 +2288,9 @@ fn lsp_segment(s: &EditorState) -> Option<String> {
|
|||
/// vacuous.
|
||||
#[test]
|
||||
fn journey_step6_a_missing_language_server_is_reported_not_swallowed() {
|
||||
if reexec_isolated("journey_step6_a_missing_language_server_is_reported_not_swallowed") {
|
||||
return;
|
||||
}
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
std::fs::write(td.path().join("Cargo.toml"), b"[package]\nname=\"x\"\n").expect("write toml");
|
||||
std::fs::write(td.path().join("main.rs"), b"fn main() {}\n").expect("write rs");
|
||||
|
|
@ -2138,6 +2351,9 @@ fn journey_step6_a_missing_language_server_is_reported_not_swallowed() {
|
|||
#[test]
|
||||
fn journey_step4_the_welcome_paints_as_multiple_rows_on_the_first_frame() {
|
||||
use pmacs::cell::{Cell, CellGrid, CellSize, Glyph};
|
||||
if reexec_isolated("journey_step4_the_welcome_paints_as_multiple_rows_on_the_first_frame") {
|
||||
return;
|
||||
}
|
||||
|
||||
let s = start_local();
|
||||
let (rows, cols) = (12u32, 100u32);
|
||||
|
|
@ -2192,3 +2408,86 @@ fn journey_step4_the_welcome_paints_as_multiple_rows_on_the_first_frame() {
|
|||
row_text(0)
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The isolation mechanism itself
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// **N** — the re-exec of §1.10 actually redirects the child's roots.
|
||||
///
|
||||
/// Every other test in this file is *protected* by the mechanism; none
|
||||
/// of them fails if it silently stops working, because they assert about
|
||||
/// editor behaviour and a developer's `init.lua` usually leaves that
|
||||
/// alone. This one asserts the mechanism's own output: the roots the
|
||||
/// child resolves, from inside the child.
|
||||
///
|
||||
/// Falsified by dropping `.envs(child_env)` from `reexec_isolated` — the
|
||||
/// child then resolves the developer's real `~/.config/pmacs` and
|
||||
/// `~/.local/share`, and all four assertions below fail.
|
||||
#[test]
|
||||
fn journey_isolated_child_resolves_the_controlled_roots() {
|
||||
if reexec_isolated("journey_isolated_child_resolves_the_controlled_roots") {
|
||||
return;
|
||||
}
|
||||
let base = Path::new(env!("CARGO_TARGET_TMPDIR"))
|
||||
.join("journey-isolation")
|
||||
.join("journey_isolated_child_resolves_the_controlled_roots");
|
||||
let under = |p: Option<std::path::PathBuf>, what: &str| {
|
||||
let p = p.unwrap_or_else(|| panic!("{what} must resolve in the child"));
|
||||
assert!(
|
||||
p.starts_with(&base),
|
||||
"{what} resolved to {} — outside the controlled base {}",
|
||||
p.display(),
|
||||
base.display()
|
||||
);
|
||||
};
|
||||
under(pmacs::config::user_config_dir(), "the config dir");
|
||||
under(
|
||||
Some(pmacs::builtin_packages::bundled_runtime_dir()),
|
||||
"the bundled-package dir",
|
||||
);
|
||||
// `PMACS_STATE_HOME` is the fifth variable: it outranks
|
||||
// `XDG_STATE_HOME`, so a child given only the four XDG ones inherits
|
||||
// whatever the launching shell exported.
|
||||
under(pmacs::state::user_state_dir(), "the state dir");
|
||||
under(pmacs::minibuffer::user_history_dir(), "the history dir");
|
||||
}
|
||||
|
||||
/// **N** — the ambient entry point is still what this suite drives.
|
||||
///
|
||||
/// The isolation seam has a `new_with_roots` / `open_with_roots` sibling
|
||||
/// that every other suite in the tree now takes. Taking it *here* would
|
||||
/// be a silent regression of what the ratchet exists to prove — a
|
||||
/// production entry point with no caller — and would leave every
|
||||
/// assertion in this file green. Asserted against this file's own source
|
||||
/// rather than left as an obvious-by-inspection property.
|
||||
#[test]
|
||||
fn journey_drives_the_ambient_production_entry_point() {
|
||||
let src = std::fs::read_to_string(
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests")
|
||||
.join("journey_acceptance.rs"),
|
||||
)
|
||||
.expect("read this suite's own source");
|
||||
// Every needle is assembled rather than written whole: a literal
|
||||
// spelled out here would appear in this file and match itself, which
|
||||
// turns the positive check vacuous and the negative ones into
|
||||
// guaranteed failures.
|
||||
let ambient_open = concat!("EditorState::open(", "path.to_path_buf())");
|
||||
let seam_open = concat!("EditorState::open", "_with_roots(");
|
||||
let seam_new = concat!("EditorState::new", "_with_roots(");
|
||||
assert!(
|
||||
src.contains(ambient_open),
|
||||
"journey must call the parameterless production `open`"
|
||||
);
|
||||
assert!(
|
||||
!src.contains(seam_open),
|
||||
"journey must not take the isolation seam — its isolation comes \
|
||||
from the environment its child is launched with"
|
||||
);
|
||||
assert!(
|
||||
!src.contains(seam_new),
|
||||
"journey must not take the isolation seam — its isolation comes \
|
||||
from the environment its child is launched with"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ fn status(s: &EditorState) -> String {
|
|||
|
||||
/// Fresh editor whose scratch buffer holds `text`, cursor at 0.
|
||||
fn editor_with(text: &str) -> EditorState {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, text);
|
||||
exec(&s, "pmacs.editor.goto_byte(0)");
|
||||
s
|
||||
|
|
@ -855,3 +855,10 @@ fn frontend_detached_drops_per_frontend_state() {
|
|||
);
|
||||
assert!(gone, "detach dropped B's killring state");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ impl Fixture {
|
|||
/// resolver. That combination is the point: the root rule under test is
|
||||
/// production code, only the command is a stand-in.
|
||||
fn editor(fx: &Fixture) -> EditorState {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, "pmacs.lsp.config = {}");
|
||||
exec(
|
||||
&state,
|
||||
|
|
@ -1880,3 +1880,10 @@ fn r6_no_swap_retires_only_the_failed_root() {
|
|||
swap occurred"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ fn fresh_state_dir() -> PathBuf {
|
|||
}
|
||||
|
||||
fn editor(state_dir: &std::path::Path) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -243,7 +243,7 @@ fn acc10b_the_prime_suffix_does_not_pair_in_lean() {
|
|||
/// WRITE-ONLY proxy (the canonical map lives Rust-side), so an
|
||||
/// alias-table read would prove nothing about what the parser does.
|
||||
fn markdown_layer_languages(src: &[u8]) -> Vec<String> {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let buf_id = state
|
||||
.lua_host
|
||||
.registry()
|
||||
|
|
@ -323,7 +323,7 @@ fn acc12_opening_lean_spawns_no_process_without_a_server_config() {
|
|||
|
||||
// Constructing an editor touches no process, even though the Lean
|
||||
// config now exists and names `lake`.
|
||||
let pristine = EditorState::new();
|
||||
let pristine = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let at_init: i64 = eval(&pristine, "return #pmacs.process.list()");
|
||||
assert_eq!(
|
||||
at_init, 0,
|
||||
|
|
@ -352,3 +352,10 @@ fn acc12_opening_lean_spawns_no_process_without_a_server_config() {
|
|||
"with no server configured, opening a Lean buffer spawns nothing"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ fn lean_editor() -> (EditorState, PathBuf) {
|
|||
let dir = fresh_dir();
|
||||
let f = dir.join("a.lean");
|
||||
std::fs::write(&f, "").unwrap();
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
let fd = f.display().to_string();
|
||||
exec(&s, &format!("pmacs.buffer.find_or_open({fd:?})"));
|
||||
|
|
@ -543,7 +543,7 @@ fn no_abbreviation_state_is_opened_outside_a_lean_buffer() {
|
|||
let dir = fresh_dir();
|
||||
let f = dir.join("a.rs");
|
||||
std::fs::write(&f, "").unwrap();
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
let fd = f.display().to_string();
|
||||
exec(&s, &format!("pmacs.buffer.find_or_open({fd:?})"));
|
||||
|
|
@ -811,7 +811,7 @@ fn the_expansion_reaches_the_first_did_change() {
|
|||
|
||||
let f = dir.join("a.lean");
|
||||
std::fs::write(&f, "").unwrap();
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host.lua().set_app_data(StateDir(dir.clone()));
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -1018,3 +1018,10 @@ fn detaching_a_frontend_purges_only_its_own_pending_state() {
|
|||
"B's detachment purged B's entries and left A's record valid"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ fn probe(s: &EditorState) -> (String, String, i64, Option<String>) {
|
|||
|
||||
#[test]
|
||||
fn open_seats_cursor_and_ret_visits_the_row() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
let (name, text, line, _) = probe(&s);
|
||||
assert_eq!(name, "*test-panel*");
|
||||
|
|
@ -227,7 +227,7 @@ fn open_seats_cursor_and_ret_visits_the_row() {
|
|||
|
||||
#[test]
|
||||
fn header_row_is_not_visitable() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
press(&mut s, KeyCode::Char('p')); // up onto the header
|
||||
press(&mut s, KeyCode::Enter);
|
||||
|
|
@ -237,7 +237,7 @@ fn header_row_is_not_visitable() {
|
|||
|
||||
#[test]
|
||||
fn q_restores_the_previous_buffer() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
press(&mut s, KeyCode::Char('q'));
|
||||
let (name, _, _, _) = probe(&s);
|
||||
|
|
@ -246,7 +246,7 @@ fn q_restores_the_previous_buffer() {
|
|||
|
||||
#[test]
|
||||
fn panel_rejects_typing() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
let (_, before, _, _) = probe(&s);
|
||||
press(&mut s, KeyCode::Char('z')); // unbound printable → self-insert → intercept rejects
|
||||
|
|
@ -258,7 +258,7 @@ fn panel_rejects_typing() {
|
|||
fn dispatch_idle_is_false_while_a_panel_is_focused() {
|
||||
// Q#P6: while the panel is the active buffer, semantic frontends
|
||||
// must round-trip every key (RET = visit, not an optimistic \n).
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
assert!(s.dispatch_idle(), "scratch buffer: idle");
|
||||
open_test_panel(&mut s);
|
||||
assert!(!s.dispatch_idle(), "panel focused: keys must round-trip");
|
||||
|
|
@ -268,7 +268,7 @@ fn dispatch_idle_is_false_while_a_panel_is_focused() {
|
|||
|
||||
#[test]
|
||||
fn refresh_reruns_the_source_and_reseats() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
press(&mut s, KeyCode::Char('g'));
|
||||
let (_, text, line, _) = probe(&s);
|
||||
|
|
@ -304,7 +304,7 @@ const PANEL_TEXT: &str = "3 items RET visit q quit\nalpha\nbeta\ngamma";
|
|||
/// consulting the intercept chain.
|
||||
#[test]
|
||||
fn s1_1_the_undo_chord_cannot_empty_a_listview_panel() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
assert_eq!(active_text(&s), PANEL_TEXT, "precondition: rendered");
|
||||
|
||||
|
|
@ -327,7 +327,7 @@ fn s1_1_the_undo_chord_cannot_empty_a_listview_panel() {
|
|||
/// *Bite:* same empty result on the pre-image.
|
||||
#[test]
|
||||
fn s1_2_m_x_buffer_undo_cannot_empty_a_listview_panel() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
|
||||
m_x(&mut s, "buffer.undo");
|
||||
|
|
@ -355,7 +355,7 @@ fn s1_2_m_x_buffer_undo_cannot_empty_a_listview_panel() {
|
|||
/// raising.
|
||||
#[test]
|
||||
fn s1_4_the_owners_refresh_still_works_after_the_lock() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
let panel = id_of(&s, "*test-panel*");
|
||||
assert!(
|
||||
|
|
@ -388,7 +388,7 @@ fn s1_4_the_owners_refresh_still_works_after_the_lock() {
|
|||
/// therefore passes the rope half and fails the lifted half.
|
||||
#[test]
|
||||
fn s1_5_the_rope_lock_and_named_intercept_refuse_in_order() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
let panel = id_of(&s, "*test-panel*");
|
||||
let before = active_text(&s);
|
||||
|
|
@ -453,7 +453,7 @@ fn s1_5_the_rope_lock_and_named_intercept_refuse_in_order() {
|
|||
/// pinned through `dispatch_idle_for` rather than through `read_only`.
|
||||
#[test]
|
||||
fn s1_6_round_trip_input_survives_the_adoption() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
|
||||
// (a) the premise.
|
||||
|
|
@ -498,7 +498,7 @@ fn s1_6_round_trip_input_survives_the_adoption() {
|
|||
/// the old line index live and this paint assertion bites.
|
||||
#[test]
|
||||
fn s1_7_a_shrinking_refresh_reaches_the_window() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_test_panel(&mut s);
|
||||
let painted = paint_active_window(&s, 6, 24);
|
||||
assert_eq!(
|
||||
|
|
@ -540,7 +540,7 @@ fn s1_7_a_shrinking_refresh_reaches_the_window() {
|
|||
/// builtin/runtime/listview.lua` falsifies it.
|
||||
#[test]
|
||||
fn s1_9_a_foreign_buffer_with_the_panels_name_is_never_adopted() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&s,
|
||||
"FOREIGN = pmacs.buffer.create('*test-panel*')\n\
|
||||
|
|
@ -585,7 +585,7 @@ fn s1_9_a_foreign_buffer_with_the_panels_name_is_never_adopted() {
|
|||
/// created.
|
||||
#[test]
|
||||
fn s1_10_the_disambiguation_limit_raises_rather_than_adopting() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&s,
|
||||
"MINE = pmacs.buffer.create('*test-panel*')\n\
|
||||
|
|
@ -624,7 +624,7 @@ fn s1_10_the_disambiguation_limit_raises_rather_than_adopting() {
|
|||
/// command produced, never on "it did not raise".
|
||||
#[test]
|
||||
fn s1_11_a_disambiguated_panel_still_answers_ret_g_and_q() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&s,
|
||||
"FOREIGN = pmacs.buffer.create('*test-panel*')\n\
|
||||
|
|
@ -678,7 +678,7 @@ fn s1_11_a_disambiguated_panel_still_answers_ret_g_and_q() {
|
|||
/// the disambiguation and `q` lands back in `*test-panel*<2>`.
|
||||
#[test]
|
||||
fn s1_12_the_q_target_capture_is_not_inverted_across_two_panels() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&s,
|
||||
"FOREIGN = pmacs.buffer.create('*test-panel*')\n\
|
||||
|
|
@ -755,3 +755,10 @@ fn s1_14_no_bypass_write_or_name_keyed_identity_remains() {
|
|||
"every `panels[` subscript must be an append; found {subscripts:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ fn fake_lsp_path() -> String {
|
|||
/// A fresh editor with the shipped language configs cleared, so the only
|
||||
/// server any test can spawn is the fake one it configures itself.
|
||||
fn editor() -> EditorState {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, "pmacs.lsp.config = {}");
|
||||
state
|
||||
}
|
||||
|
|
@ -722,3 +722,10 @@ fn acc34a_canonicalize_declines_a_non_utf8_resolution() {
|
|||
return a U+FFFD-substituted path that exists nowhere"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ fn fake_lsp_path() -> String {
|
|||
/// A fresh editor with the shipped language configs cleared, so the only
|
||||
/// server any test can spawn is the fake one it configures itself.
|
||||
fn editor() -> EditorState {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, "pmacs.lsp.config = {}");
|
||||
state
|
||||
}
|
||||
|
|
@ -702,3 +702,10 @@ fn a_resolver_returning_nil_declines_silently() {
|
|||
file_uri(&fx.dir("proj"))
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ fn open(state: &EditorState, path: &Path) {
|
|||
}
|
||||
|
||||
fn editor_for(dir: &Path) -> EditorState {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// Clamp detection so a stray marker above the tempdir cannot leak in.
|
||||
exec(
|
||||
&state,
|
||||
|
|
@ -565,3 +565,10 @@ fn j1b2_preservation_a_spawnable_server_still_attaches() {
|
|||
);
|
||||
assert_eq!(failure_count(&state), 0);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ fn assert_disjoint_within(ranges: &[ByteRange], vp: ByteRange) {
|
|||
|
||||
#[test]
|
||||
fn incremental_reconstruction_equals_fresh_full_projection() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let buffer_id = active_buffer(&state);
|
||||
let vp1 = ByteRange { start: 0, end: 64 };
|
||||
|
||||
|
|
@ -327,3 +327,11 @@ fn daemon_routes_semantic_family_to_semantic_session_only() {
|
|||
"grid session must NOT receive the semantic family"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root. Re-exported rather than re-declared
|
||||
// with `#[path]` — this file already pulls in `common`, and loading one
|
||||
// source file as two modules is `clippy::duplicate_mod`.
|
||||
use common::iso;
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ fn m4_1_parse_tree_introspectable_via_lua() {
|
|||
use pmacs::editor::EditorState;
|
||||
use pmacs::lua_bindings::BufferIdLua;
|
||||
|
||||
let editor = EditorState::new();
|
||||
let editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// Insert a buffer with known shape; bind its handle into Lua as
|
||||
// `BUF` so the script can reference it by name.
|
||||
let buf_id = editor
|
||||
|
|
@ -359,7 +359,8 @@ fn m4_2_opening_a_rust_file_produces_a_parse_tree() {
|
|||
let path = dir.path().join("hello.rs");
|
||||
std::fs::write(&path, b"fn main() { let x = 1 + 2; }\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::open(path).expect("open .rs");
|
||||
let mut state =
|
||||
pmacs::editor::EditorState::open_with_roots(path, &crate::iso::roots()).expect("open .rs");
|
||||
pump_async(&mut state, |s| current_tree_language(s).is_some());
|
||||
assert_eq!(current_tree_language(&state).as_deref(), Some("rust"));
|
||||
|
||||
|
|
@ -389,7 +390,8 @@ fn m4_2_opening_a_lua_file_produces_a_parse_tree() {
|
|||
let path = dir.path().join("hello.lua");
|
||||
std::fs::write(&path, b"local x = 1\nreturn x + 2\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::open(path).expect("open .lua");
|
||||
let mut state =
|
||||
pmacs::editor::EditorState::open_with_roots(path, &crate::iso::roots()).expect("open .lua");
|
||||
pump_async(&mut state, |s| current_tree_language(s).is_some());
|
||||
assert_eq!(current_tree_language(&state).as_deref(), Some("lua"));
|
||||
|
||||
|
|
@ -420,7 +422,8 @@ fn m4_2_register_extension_attaches_for_a_runtime_added_extension() {
|
|||
let path = dir.path().join("hello.myrust");
|
||||
std::fs::write(&path, b"fn main() {}\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::open(path).expect("open .myrust");
|
||||
let mut state = pmacs::editor::EditorState::open_with_roots(path, &crate::iso::roots())
|
||||
.expect("open .myrust");
|
||||
// The auto-attach hook fires *during* open, but at that point our
|
||||
// custom extension isn't registered yet --- so the first hook
|
||||
// invocation is a no-op. Register the extension and trigger the
|
||||
|
|
@ -516,7 +519,8 @@ fn render_active_window_to_grid(
|
|||
/// either the parse settles (so highlights can attach) or the
|
||||
/// timeout deadline hits.
|
||||
fn open_and_wait_for_parse(path: std::path::PathBuf) -> pmacs::editor::EditorState {
|
||||
let mut state = pmacs::editor::EditorState::open(path).expect("open file");
|
||||
let mut state =
|
||||
pmacs::editor::EditorState::open_with_roots(path, &crate::iso::roots()).expect("open file");
|
||||
pump_async(&mut state, |s| current_tree_language(s).is_some());
|
||||
state
|
||||
}
|
||||
|
|
@ -1155,7 +1159,7 @@ fn m4_4_pty_mode_child_observes_a_tty() {
|
|||
/// to Lua" spec invariant for the process case.
|
||||
#[test]
|
||||
fn m4_4_lua_surface_drives_lifecycle() {
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let id_raw: i64 = state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -1611,7 +1615,7 @@ fn m4_5_protocol_violation_surfaces_as_structured_error() {
|
|||
fn m4_5_lua_surface_drives_lsp_lifecycle() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
let lua = state.lua_host.lua();
|
||||
let sid_raw: u64 = lua
|
||||
|
|
@ -1846,7 +1850,7 @@ fn m4_6_diagnostic_source_field_is_preserved() {
|
|||
fn m4_6_lua_surface_reads_diagnostics() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
let lua = state.lua_host.lua();
|
||||
let sid_raw: u64 = lua
|
||||
|
|
@ -1958,7 +1962,7 @@ fn m4_6_lua_surface_reads_diagnostics() {
|
|||
fn m4_6_diag_navigate_commands_and_bindings_are_registered() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
|
||||
let commands: Vec<String> = lua
|
||||
|
|
@ -2028,7 +2032,7 @@ fn m4_6_diag_navigate_commands_and_bindings_are_registered() {
|
|||
fn m4_6_diag_attach_view_pushes_diagnostic_overlay() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let attached: bool = state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -2260,7 +2264,7 @@ fn m4_7_signature_help_during_function_call() {
|
|||
fn m4_7_lua_surface_drives_completion_hover_signature() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
let lua = state.lua_host.lua();
|
||||
let sid_raw: u64 = lua
|
||||
|
|
@ -2581,7 +2585,7 @@ fn m4_8_status_buffer_text_reflects_state_and_capabilities() {
|
|||
fn m4_8_lua_surface_exposes_status() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
let lua = state.lua_host.lua();
|
||||
let sid_raw: u64 = lua
|
||||
|
|
@ -2761,7 +2765,7 @@ fn m4_9_project_switch_is_first_class() {
|
|||
touch_file(&d1.path().join("Cargo.toml"));
|
||||
touch_file(&d2.path().join(".luarc.json"));
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
let d1_path = d1.path().display().to_string();
|
||||
let d2_path = d2.path().display().to_string();
|
||||
|
|
@ -2817,7 +2821,7 @@ fn m4_9_lsp_runs_per_project_not_per_buffer() {
|
|||
touch_file(&d1.path().join("Cargo.toml"));
|
||||
touch_file(&d2.path().join("Cargo.toml"));
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let d1_path = d1.path().display().to_string();
|
||||
let d2_path = d2.path().display().to_string();
|
||||
|
||||
|
|
@ -2929,7 +2933,7 @@ fn m4_10_index_persists_across_sessions() {
|
|||
|
||||
// Session A: index a synthetic source file, save to disk.
|
||||
{
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
let saved_path: String = lua
|
||||
.load(format!(
|
||||
|
|
@ -2957,7 +2961,7 @@ fn m4_10_index_persists_across_sessions() {
|
|||
// searches still find what session A indexed --- without ever
|
||||
// calling upsert_file again.
|
||||
let started = std::time::Instant::now();
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
let (file_count, symbol_count, hit_count, hit_name): (u64, u64, u64, String) = lua
|
||||
.load(format!(
|
||||
|
|
@ -3063,7 +3067,7 @@ fn m4_10_lua_surface_drives_index() {
|
|||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let root = dir.path().display().to_string();
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
|
||||
let (files_before, syms_before, hit_count, after_invalidate, generation_after): (
|
||||
|
|
@ -3111,7 +3115,7 @@ fn m4_10_lua_surface_ingests_lsp_workspace_symbol() {
|
|||
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let root = dir.path().display().to_string();
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
// The URI we feed has to be under the project root so the
|
||||
// resulting absolute path is deterministic.
|
||||
|
|
@ -3160,7 +3164,7 @@ fn m4_10_lua_surface_ingests_lsp_workspace_symbol() {
|
|||
fn m4_11_multiple_sources_combine_without_duplicates() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
|
||||
let (parse_count, parse_source, total_count): (u64, String, u64) = lua
|
||||
|
|
@ -3235,7 +3239,7 @@ fn m4_11_multiple_sources_combine_without_duplicates() {
|
|||
fn m4_11_source_priority_configurable() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
|
||||
let (winner_first, winner_second): (String, String) = lua
|
||||
|
|
@ -3289,7 +3293,7 @@ fn m4_11_source_priority_configurable() {
|
|||
fn m4_11_custom_sources_from_lua() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
|
||||
let (custom_count_active, custom_label, custom_source, count_disabled, count_unregistered): (
|
||||
|
|
@ -3373,7 +3377,7 @@ fn m4_11_custom_sources_from_lua() {
|
|||
fn m4_11_snippets_surface_through_completion() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
let (label, kind, insert_text, source): (String, String, String, String) = lua
|
||||
.load(
|
||||
|
|
@ -3529,7 +3533,7 @@ fn m4_12_formatting_response_lands_in_store() {
|
|||
fn m4_12_lua_surface_drives_definition_and_formatting() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
|
||||
// Spawn a server through the Lua surface.
|
||||
|
|
@ -3667,7 +3671,7 @@ fn m4_12_cross_file_go_to_definition_and_jump_back() {
|
|||
let b_disp = b_path.display().to_string();
|
||||
let b_uri = format!("file://{b_disp}");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
|
||||
// Point the default `rust` server at the fake, in `defenv` mode,
|
||||
|
|
@ -3787,7 +3791,7 @@ fn m4_13_rename_applies_cross_file_workspace_edit() {
|
|||
let b_disp = b_path.display().to_string();
|
||||
let b_uri = format!("file://{b_disp}");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
|
||||
state
|
||||
|
|
@ -3915,7 +3919,7 @@ fn m4_14_code_action_command_drives_apply_edit() {
|
|||
std::fs::write(&a_path, b"abcfooxyz\n___zzz\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
|
||||
state
|
||||
|
|
@ -4028,7 +4032,7 @@ fn m4_15_workspace_edit_resource_ops_apply_in_order() {
|
|||
let created = dir.path().join("created.rs");
|
||||
let b2 = dir.path().join("b2.rs");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
|
||||
state
|
||||
|
|
@ -4131,7 +4135,7 @@ fn m4_15_workspace_edit_resource_ops_apply_in_order() {
|
|||
/// kinds, and `paddingRight`.
|
||||
#[test]
|
||||
fn m4_16_lua_surface_drives_inlay_hints() {
|
||||
let mut s = pmacs::editor::EditorState::new();
|
||||
let mut s = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut s, Some("inlaybounds"));
|
||||
|
||||
let uri = "file:///tmp/m4_16_inlay.rs";
|
||||
|
|
@ -4190,7 +4194,7 @@ fn m4_16_lua_surface_drives_inlay_hints() {
|
|||
/// the `token_type` index resolves to a name.
|
||||
#[test]
|
||||
fn m4_17_lua_surface_drives_semantic_tokens() {
|
||||
let mut s = pmacs::editor::EditorState::new();
|
||||
let mut s = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut s, None);
|
||||
|
||||
let uri = "file:///tmp/m4_17_sem.rs";
|
||||
|
|
@ -4261,7 +4265,7 @@ fn m4_18_inlay_hint_refresh_repulls_via_server_request() {
|
|||
std::fs::write(&a_path, b"fn a() {}\n\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4314,7 +4318,7 @@ fn m4_18b_inlay_hints_auto_pull_after_initialize() {
|
|||
std::fs::write(&a_path, b"fn a() {}\n\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4365,7 +4369,7 @@ fn m4_19_semantic_tokens_refresh_repulls_via_server_request() {
|
|||
std::fs::write(&a_path, b"fn a() {}\n\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4422,7 +4426,7 @@ fn arc1c_semantic_tokens_auto_pull_on_attach() {
|
|||
std::fs::write(&a_path, b"fn a() {}\n\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4466,7 +4470,7 @@ fn arc1c_semantic_tokens_repull_after_edit_flush() {
|
|||
std::fs::write(&a_path, b"fn a() {}\n\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4529,7 +4533,7 @@ fn arc1d_signature_help_auto_triggers_on_trigger_char() {
|
|||
std::fs::write(&a_path, b"\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4589,7 +4593,7 @@ fn arc1d_signature_help_does_not_trigger_on_ordinary_typing() {
|
|||
std::fs::write(&a_path, b"\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4648,7 +4652,7 @@ fn arc1c_range_only_server_is_served_range_requests() {
|
|||
std::fs::write(&a_path, b"fn a() {}\n\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4703,7 +4707,7 @@ fn arc1c_range_only_utf16_server_gets_converted_bounds() {
|
|||
std::fs::write(&a_path, "fn a() {}\nlet x = \u{e9}\u{e9};".as_bytes()).expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4752,7 +4756,7 @@ fn arc1d_signature_help_triggers_on_non_ascii_trigger_char() {
|
|||
std::fs::write(&a_path, b"\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4810,7 +4814,7 @@ fn arc1d_signature_help_ignores_non_typed_edits() {
|
|||
std::fs::write(&a_path, b"\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4881,7 +4885,7 @@ fn arc1c_full_only_server_repulls_via_full_not_delta() {
|
|||
std::fs::write(&a_path, b"fn a() {}\n\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -4954,7 +4958,7 @@ fn arc1c_full_only_server_repulls_via_full_not_delta() {
|
|||
/// fake returns one token.
|
||||
#[test]
|
||||
fn m4_20_semantic_tokens_range() {
|
||||
let mut s = pmacs::editor::EditorState::new();
|
||||
let mut s = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut s, None);
|
||||
|
||||
let uri = "file:///tmp/m4_20_sem.rs";
|
||||
|
|
@ -4998,7 +5002,7 @@ fn m4_20_semantic_tokens_range() {
|
|||
/// `resultId` "rid-2".
|
||||
#[test]
|
||||
fn m4_21_semantic_tokens_full_then_delta() {
|
||||
let mut s = pmacs::editor::EditorState::new();
|
||||
let mut s = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut s, None);
|
||||
|
||||
let uri = "file:///tmp/m4_21_sem.rs";
|
||||
|
|
@ -5078,7 +5082,7 @@ fn m4_22_rename_prepare_gates_and_prefills() {
|
|||
std::fs::write(&a_path, b"abcfooxyz\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -5179,7 +5183,7 @@ fn m4_23_rename_prepare_refusal_aborts() {
|
|||
std::fs::write(&a_path, b"abcfooxyz\n").expect("write a");
|
||||
let a_disp = a_path.display().to_string();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -5272,7 +5276,7 @@ fn m4_24_workspace_did_change_watched_files() {
|
|||
let received = base.join(".received");
|
||||
let foo_uri = format!("file://{}", base.join("foo.txt").display());
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -5351,7 +5355,7 @@ fn m4_24_workspace_did_change_watched_files() {
|
|||
fn m4_25_tier1_language_server_configs_and_filetypes() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let probe: mlua::Table = s
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -5455,7 +5459,7 @@ fn m4_26_auto_attach_roots_server_at_opened_files_project() {
|
|||
let go_file_disp = go_file.display().to_string();
|
||||
let fake = fake_lsp_path();
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// Clamp the marker walk to the tempdir so a stray ancestor marker
|
||||
// (a developer's /tmp/.git, say) can't masquerade as the root.
|
||||
// Point the default `go` server at the fake in `rooturi` mode.
|
||||
|
|
@ -5567,7 +5571,7 @@ fn m4_27_real_gopls_analyzes_module_via_auto_attach() {
|
|||
let go_file_disp = go_file.display().to_string();
|
||||
let uri = format!("file://{go_file_disp}");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// gopls handshake + workspace load is slow on a cold cache (30s).
|
||||
real_server_open_and_init(&mut state, "go", &gopls, &root_disp, &go_file_disp);
|
||||
|
||||
|
|
@ -5664,7 +5668,7 @@ fn m4_28_real_clangd_diagnostics_and_semantic_tokens_via_auto_attach() {
|
|||
let cpp_disp = cpp.display().to_string();
|
||||
let uri = format!("file://{cpp_disp}");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
real_server_open_and_init(&mut state, "cpp", &clangd, &root_disp, &cpp_disp);
|
||||
|
||||
// Fire a semantic-tokens request; diagnostics flow unsolicited
|
||||
|
|
@ -5767,7 +5771,7 @@ fn m4_29_real_rust_analyzer_inlay_hints_via_auto_attach() {
|
|||
let file_disp = file.display().to_string();
|
||||
let uri = format!("file://{file_disp}");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
real_server_open_and_init(&mut state, "rust", &rust_analyzer, &root_disp, &file_disp);
|
||||
|
||||
// rust-analyzer only answers `textDocument/inlayHint` after it has
|
||||
|
|
@ -5815,7 +5819,7 @@ fn m4_29_real_rust_analyzer_inlay_hints_via_auto_attach() {
|
|||
fn m4_12_default_bundle_wires_commands_and_keymaps() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let probe: mlua::Table = s
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -5870,7 +5874,7 @@ fn m4_12_default_bundle_wires_commands_and_keymaps() {
|
|||
fn m4_12_default_bundle_wires_cuda() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let probe: mlua::Table = s
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -5926,7 +5930,7 @@ fn m4_12_default_bundle_wires_cuda() {
|
|||
fn m4_12_default_bundle_wires_bash() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let probe: mlua::Table = s
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -5970,7 +5974,7 @@ fn m4_12_default_bundle_wires_bash() {
|
|||
#[test]
|
||||
fn m4_shebang_resolver_maps_interpreters() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let resolve = |first_line: &str| -> Option<String> {
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -6030,7 +6034,7 @@ fn m4_shebang_resolver_maps_interpreters() {
|
|||
#[test]
|
||||
fn m4_shebang_extensionless_script_resolves_bash() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let hook = dir.path().join("pre-commit"); // no extension
|
||||
std::fs::write(&hook, b"#!/bin/sh\nset -e\necho building\n").expect("write");
|
||||
|
|
@ -6071,7 +6075,7 @@ fn m4_shebang_extensionless_script_resolves_bash() {
|
|||
#[test]
|
||||
fn m4_shebang_does_not_override_extension() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let f = dir.path().join("tool.py");
|
||||
std::fs::write(&f, b"#!/bin/sh\nprint('hi')\n").expect("write");
|
||||
|
|
@ -6116,7 +6120,7 @@ fn m4_shebang_does_not_override_extension() {
|
|||
#[test]
|
||||
fn m4_shebang_extensionless_grammarless_language_is_silent() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let f = dir.path().join("generate"); // no extension
|
||||
// `ruby` is deliberately grammarless (and serverless) — a language the
|
||||
|
|
@ -6166,7 +6170,7 @@ fn m4_shebang_extensionless_grammarless_language_is_silent() {
|
|||
#[test]
|
||||
fn m4_shebang_edit_keeps_pinned_grammar() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let hook = dir.path().join("deploy"); // no extension
|
||||
std::fs::write(&hook, b"#!/bin/sh\necho one\n").expect("write");
|
||||
|
|
@ -6261,7 +6265,7 @@ fn m4_shebang_edit_keeps_pinned_grammar() {
|
|||
#[test]
|
||||
fn m4_modeline_overrides_extension_end_to_end() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let file = dir.path().join("misleading.py");
|
||||
std::fs::write(&file, b"-- -*- mode: lua -*-\nprint('ok')\n").expect("write");
|
||||
|
|
@ -6299,7 +6303,7 @@ fn m4_modeline_overrides_extension_end_to_end() {
|
|||
#[test]
|
||||
fn m4_modeline_parser_matches_supported_emacs_and_vim_forms() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let resolve = |text: &str| -> Option<String> {
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -6344,7 +6348,7 @@ fn m4_modeline_parser_matches_supported_emacs_and_vim_forms() {
|
|||
#[test]
|
||||
fn m4_modeline_parser_enforces_boundaries_aliases_and_conflicts() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let resolve = |text: &str| -> Option<String> {
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -6428,7 +6432,7 @@ fn m4_modeline_parser_enforces_boundaries_aliases_and_conflicts() {
|
|||
#[test]
|
||||
fn m4_modeline_unknown_mode_is_quiet_and_parser_free() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let file = dir.path().join("notes.txt");
|
||||
std::fs::write(&file, b"# vim:ft=prose:\nhello\n").expect("write");
|
||||
|
|
@ -6464,7 +6468,7 @@ fn m4_modeline_unknown_mode_is_quiet_and_parser_free() {
|
|||
#[test]
|
||||
fn m4_modeline_language_is_pinned_until_reopen() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let file = dir.path().join("mutable.txt");
|
||||
let other = dir.path().join("other.txt");
|
||||
|
|
@ -6558,7 +6562,7 @@ fn m4_modeline_language_is_pinned_until_reopen() {
|
|||
#[test]
|
||||
fn m4_modeline_shared_resolver_preserves_pathless_lsp_guard() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let (syntax, lsp): (Option<String>, Option<String>) = s
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -6580,7 +6584,7 @@ fn m4_modeline_shared_resolver_preserves_pathless_lsp_guard() {
|
|||
#[test]
|
||||
fn m4_filename_map_resolves_special_files() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let probe: mlua::Table = s
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -6650,7 +6654,7 @@ fn m4_filename_map_resolves_special_files() {
|
|||
#[test]
|
||||
fn m4_filename_extensionless_dockerfile_highlights() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let f = dir.path().join("Dockerfile"); // no extension
|
||||
std::fs::write(&f, b"FROM alpine:3\nRUN apk add curl\n").expect("write");
|
||||
|
|
@ -6690,7 +6694,7 @@ fn m4_filename_extensionless_dockerfile_highlights() {
|
|||
#[test]
|
||||
fn m4_gap_grammars_align_with_lsp_configs() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
for (path, id) in [
|
||||
("app.py", "python"),
|
||||
("srv.go", "go"),
|
||||
|
|
@ -6733,7 +6737,7 @@ fn m4_gap_grammars_align_with_lsp_configs() {
|
|||
#[test]
|
||||
fn m4_json_yaml_lsp_configs_pin_command_and_sections() {
|
||||
use pmacs::editor::EditorState;
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = s.lua_host.lua();
|
||||
|
||||
// json: the `@t1ckbase/vscode-langservers-extracted@2.0.2` binary
|
||||
|
|
@ -6809,7 +6813,7 @@ fn m4_json_yaml_lsp_configs_pin_command_and_sections() {
|
|||
#[test]
|
||||
fn m4_5_initial_config_pushed_via_did_change_configuration() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let sink = dir.path().join("config.jsonl");
|
||||
|
|
@ -6895,7 +6899,7 @@ fn m4_real_json_provider_receives_config_and_reports_diagnostics() {
|
|||
let file_disp = file.display().to_string();
|
||||
let uri = format!("file://{file_disp}");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -6964,7 +6968,7 @@ fn m4_real_yaml_provider_pulls_config_and_reports_diagnostics() {
|
|||
let file_disp = file.display().to_string();
|
||||
let uri = format!("file://{file_disp}");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -7043,7 +7047,7 @@ fn m4_real_yaml_provider_pulls_config_and_reports_diagnostics() {
|
|||
fn m4_lua_bundle_debounces_did_change_per_keystroke() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
let dir = tempfile::TempDir::new().unwrap();
|
||||
let file = dir.path().join("debounce.rs");
|
||||
|
|
@ -7144,12 +7148,12 @@ fn m4_lua_bundle_debounces_did_change_per_keystroke() {
|
|||
fn m4_12_default_bundle_after_load_robust_to_missing_server() {
|
||||
use pmacs::editor::EditorState;
|
||||
let _dir = tempfile::TempDir::new().unwrap();
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// Plain after-load with no buffer path attached — the hook should
|
||||
// run, find no language for path=nil, and exit silently. If it
|
||||
// throws, the *errors* buffer would record it; verify it doesn't.
|
||||
s.lua_host.hooks();
|
||||
let s2 = EditorState::new();
|
||||
let s2 = EditorState::new_with_roots(&crate::iso::roots());
|
||||
drop(s);
|
||||
let saw_error: bool = s2
|
||||
.lua_host
|
||||
|
|
@ -7199,7 +7203,7 @@ fn project_set_search_boundary_clamps_lua_detect_call() {
|
|||
let f = workspace.join("src/main.rs");
|
||||
std::fs::write(&f, b"").expect("touch file");
|
||||
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
let workspace_str = workspace.display().to_string();
|
||||
let f_str = f.display().to_string();
|
||||
|
|
@ -7234,7 +7238,7 @@ fn project_search_boundary_round_trips_via_lua() {
|
|||
|
||||
let dir = tempfile::tempdir().expect("dir");
|
||||
let dir_str = dir.path().display().to_string();
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
|
||||
let (initial, after_set, after_clear): (Option<String>, Option<String>, Option<String>) = lua
|
||||
|
|
@ -7420,7 +7424,7 @@ fn spawn_lsp_and_init(state: &mut pmacs::editor::EditorState, mode: Option<&str>
|
|||
#[test]
|
||||
fn m4_5_await_completion_returns_result_and_populates_store() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, None);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7456,7 +7460,7 @@ fn m4_5_await_completion_returns_result_and_populates_store() {
|
|||
#[test]
|
||||
fn m4_5_await_server_error_raises_failed() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, Some("error"));
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7496,7 +7500,7 @@ fn m4_5_await_server_error_raises_failed() {
|
|||
#[test]
|
||||
fn m4_5_await_cancelled_when_server_stops_mid_request() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, Some("silent"));
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7534,7 +7538,7 @@ fn m4_5_await_cancelled_when_server_stops_mid_request() {
|
|||
#[test]
|
||||
fn m4_5_await_times_out_against_silent_server() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, Some("silent"));
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7576,7 +7580,7 @@ fn m4_5_await_times_out_against_silent_server() {
|
|||
#[test]
|
||||
fn m4_5_await_superseded_request_is_cancelled() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, Some("silent"));
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7622,7 +7626,7 @@ fn m4_5_await_superseded_request_is_cancelled() {
|
|||
#[test]
|
||||
fn m4_5_await_resolves_same_frame_as_response_absorbed() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, None);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7710,7 +7714,7 @@ fn m4_5_await_resolves_same_frame_as_response_absorbed() {
|
|||
#[test]
|
||||
fn m4_5_position_encoding_utf16_round_trips_non_ascii() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, Some("posecho"));
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7766,7 +7770,7 @@ fn m4_5_position_encoding_utf16_round_trips_non_ascii() {
|
|||
fn m4_5_utf16_rename_and_prepare_rename_convert_positions() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, Some("posecho"));
|
||||
let uri = "file:///tmp/m4_5_rename_utf16.rs";
|
||||
state
|
||||
|
|
@ -7806,7 +7810,7 @@ fn m4_5_utf16_rename_and_prepare_rename_convert_positions() {
|
|||
#[test]
|
||||
fn m4_5_workspace_configuration_answered_from_settings() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7863,7 +7867,7 @@ fn m4_5_workspace_configuration_answered_from_settings() {
|
|||
#[test]
|
||||
fn m4_5_location_nav_requests_route_by_kind() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, None);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7913,7 +7917,7 @@ fn m4_5_location_nav_requests_route_by_kind() {
|
|||
#[test]
|
||||
fn m4_5_symbols_and_highlight_round_trip() {
|
||||
use pmacs::editor::EditorState;
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_lsp_and_init(&mut state, None);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -7977,7 +7981,7 @@ fn m4_5_symbols_and_highlight_round_trip() {
|
|||
/// Open `path` against the fake server and wait for initialization
|
||||
/// (shared bootstrap for the panel tests).
|
||||
fn open_against_fake(path: &std::path::Path) -> pmacs::editor::EditorState {
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_lsp_path();
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -8223,7 +8227,7 @@ fn rd1_delete_refuses_when_a_bound_buffer_is_modified() {
|
|||
let f = dir.path().join("a.rs");
|
||||
std::fs::write(&f, b"original\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &f);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -8265,7 +8269,7 @@ fn rd2_delete_still_succeeds_for_a_clean_open_buffer() {
|
|||
let f = dir.path().join("clean.rs");
|
||||
std::fs::write(&f, b"untouched\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &f);
|
||||
|
||||
let (ok, err) = rd_delete(&mut state, &f, "");
|
||||
|
|
@ -8310,7 +8314,7 @@ fn rd3_filesystem_failure_leaves_the_clean_buffer_intact() {
|
|||
let target = dir.path().join("target");
|
||||
std::fs::write(&target, b"was a file\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &target);
|
||||
|
||||
// The path changes type behind pmacs's back — the same class of
|
||||
|
|
@ -8354,7 +8358,7 @@ fn rd4_on_removed_observes_the_path_already_gone() {
|
|||
std::fs::write(&f, b"bye\n").expect("write");
|
||||
let p = f.display().to_string();
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &f);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -8399,7 +8403,7 @@ fn rd5_delete_from_inside_the_targets_own_intercept_refuses_before_disk() {
|
|||
std::fs::write(&f, b"busy\n").expect("write");
|
||||
let p = f.display().to_string();
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &f);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -8455,7 +8459,7 @@ fn rd6_a_clean_first_match_cannot_hide_a_modified_duplicate() {
|
|||
std::fs::write(&f, b"shared\n").expect("write");
|
||||
let p = f.display().to_string();
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -8493,7 +8497,7 @@ fn rd7_a_sibling_directory_sharing_a_name_prefix_does_not_block() {
|
|||
let outside = sibling.join("out.rs");
|
||||
std::fs::write(&outside, b"out\n").expect("write out");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &outside);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -8527,7 +8531,7 @@ fn rd8_recursive_delete_refuses_for_a_modified_descendant() {
|
|||
let inner = nested.join("deep.rs");
|
||||
std::fs::write(&inner, b"deep\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &inner);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -8575,7 +8579,7 @@ fn rd9_clean_recursive_delete_reconciles_descendants_through_both_phases() {
|
|||
let inner = tree.join("kept.rs");
|
||||
std::fs::write(&inner, b"kept\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &inner);
|
||||
// Display it, so the phase-1 window redirect has something to do.
|
||||
state
|
||||
|
|
@ -8629,7 +8633,7 @@ fn rd10_absent_plus_ignore_does_not_destroy_a_modified_buffer() {
|
|||
let f = dir.path().join("vanished.rs");
|
||||
std::fs::write(&f, b"content\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &f);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -8684,7 +8688,7 @@ fn rd14_clean_duplicates_all_reconcile() {
|
|||
std::fs::write(&f, b"twin\n").expect("write");
|
||||
let p = f.display().to_string();
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -8940,7 +8944,7 @@ fn rd11_absent_plus_ignore_succeeds_through_the_server_pump() {
|
|||
let victim = dir.path().join("victim.rs");
|
||||
std::fs::write(&victim, b"content\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
{ "kind": "delete", "uri": rd_uri(&victim),
|
||||
|
|
@ -8991,7 +8995,7 @@ fn rd11a_present_plus_ignore_with_a_modified_buffer_is_refused() {
|
|||
let victim = dir.path().join("victim.rs");
|
||||
std::fs::write(&victim, b"content\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
{ "kind": "delete", "uri": rd_uri(&victim),
|
||||
|
|
@ -9049,7 +9053,7 @@ fn rd11b_a_dangling_symlink_counts_as_present() {
|
|||
std::fs::write(&real, b"content\n").expect("write");
|
||||
std::os::unix::fs::symlink(&real, &link).expect("symlink");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
{ "kind": "delete", "uri": rd_uri(&link),
|
||||
|
|
@ -9113,7 +9117,7 @@ fn rd11c_absent_without_ignore_refuses_before_the_earlier_op_runs() {
|
|||
let missing = dir.path().join("never-existed.rs");
|
||||
std::fs::write(&a, b"abcfooxyz\n").expect("write a");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
rd_edit_op(&a, 0, 3, 6, "EDITED"),
|
||||
|
|
@ -9167,7 +9171,7 @@ fn rd11d_an_unanswerable_stat_fails_closed_in_the_plan() {
|
|||
std::fs::write(¬_a_dir, b"I am a file\n").expect("write the would-be parent");
|
||||
let target = not_a_dir.join("child.rs");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
rd_edit_op(&a, 0, 3, 6, "EDITED"),
|
||||
|
|
@ -9223,7 +9227,7 @@ fn rd12a_edit_then_delete_answers_the_server_and_reports_partial_work() {
|
|||
let a = dir.path().join("a.rs");
|
||||
std::fs::write(&a, b"abcfooxyz\n").expect("write a");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
rd_edit_op(&a, 0, 3, 6, "EDITED"),
|
||||
|
|
@ -9276,7 +9280,7 @@ fn rd12b_rename_into_delete_answers_the_server_and_reports_partial_work() {
|
|||
let inside = tree.join("m.rs");
|
||||
std::fs::write(&outside, b"content\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
{ "kind": "rename", "oldUri": rd_uri(&outside), "newUri": rd_uri(&inside) },
|
||||
|
|
@ -9341,7 +9345,7 @@ fn rd13_the_refusal_answers_the_server_and_leaves_a_durable_trace() {
|
|||
let victim = dir.path().join("victim.rs");
|
||||
std::fs::write(&victim, b"content\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [ { "kind": "delete", "uri": rd_uri(&victim) } ]
|
||||
});
|
||||
|
|
@ -9411,7 +9415,7 @@ fn rd15_defensive_a_parse_failure_still_attempts_a_response() {
|
|||
let a = dir.path().join("a.rs");
|
||||
std::fs::write(&a, b"abcfooxyz\n").expect("write a");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
// A plan that would otherwise succeed, so a response saying
|
||||
// `applied = false` can only have come from the parse stub.
|
||||
let plan = serde_json::json!({
|
||||
|
|
@ -9476,7 +9480,7 @@ fn rd18_non_recursive_delete_is_not_blocked_by_an_orphan_beneath_it() {
|
|||
let gone = tree.join("gone.rs");
|
||||
std::fs::write(&gone, b"content\n").expect("write");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
rd_open(&mut state, "B", &gone);
|
||||
state
|
||||
.lua_host
|
||||
|
|
@ -9521,7 +9525,7 @@ fn rd19a_create_then_delete_is_not_refused_by_the_plan_time_preflight() {
|
|||
let transient = dir.path().join("transient.rs");
|
||||
let witness = dir.path().join("witness.rs");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
{ "kind": "create", "uri": rd_uri(&transient) },
|
||||
|
|
@ -9563,7 +9567,7 @@ fn rd19b_rename_then_delete_is_not_refused_by_the_plan_time_preflight() {
|
|||
let destination = dir.path().join("destination.rs");
|
||||
std::fs::write(&source, b"moving\n").expect("write source");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
{ "kind": "rename", "oldUri": rd_uri(&source), "newUri": rd_uri(&destination) },
|
||||
|
|
@ -9607,7 +9611,7 @@ fn rd19c_deferring_the_check_does_not_skip_it() {
|
|||
std::fs::write(&a, b"abcfooxyz\n").expect("write a");
|
||||
let fresh = dir.path().join("fresh.rs");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
{ "kind": "create", "uri": rd_uri(&fresh) },
|
||||
|
|
@ -9671,7 +9675,7 @@ fn rd20_the_user_facing_message_reports_partial_application() {
|
|||
let a = dir.path().join("a.rs");
|
||||
std::fs::write(&a, b"abcfooxyz\n").expect("write a");
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
rd_edit_op(&a, 0, 3, 6, "EDITED"),
|
||||
|
|
@ -9753,7 +9757,7 @@ fn rd21_equivalent_dot_path_create_then_delete_is_not_preflight_refused() {
|
|||
"fixture: the URI spellings must differ"
|
||||
);
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [
|
||||
{ "kind": "create", "uri": dot_uri },
|
||||
|
|
@ -9792,7 +9796,7 @@ fn rd22a_partial_edits_inside_one_item_are_reported_conservatively() {
|
|||
let target = dir.path().join("target.rs");
|
||||
std::fs::write(&target, b"abcdef\n").expect("write target");
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [{
|
||||
"textDocument": { "uri": rd_uri(&target), "version": 1 },
|
||||
|
|
@ -9877,7 +9881,7 @@ fn rd22b_a_failing_resource_item_can_leave_filesystem_state() {
|
|||
"fixture: destination parent must start absent"
|
||||
);
|
||||
|
||||
let mut state = pmacs::editor::EditorState::new();
|
||||
let mut state = pmacs::editor::EditorState::new_with_roots(&crate::iso::roots());
|
||||
let plan = serde_json::json!({
|
||||
"documentChanges": [{
|
||||
"kind": "rename",
|
||||
|
|
@ -9907,3 +9911,10 @@ fn rd22b_a_failing_resource_item_can_leave_filesystem_state() {
|
|||
"the response must not deny the directory left on disk: {reason:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -253,6 +253,10 @@ fn second_daemon_same_socket_fails_clearly() {
|
|||
.arg(daemon_a.socket_path())
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
|
|
|
|||
|
|
@ -139,6 +139,10 @@ fn spawn_pmacs_daemon(socket_path: &Path) -> Child {
|
|||
.arg(socket_path)
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
|
|
@ -270,6 +274,10 @@ fn cli_attach_invokes_test_ssh_with_expected_argv() {
|
|||
.env(PMACS_TEST_SSH_BIN, &fake_ssh)
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
|
|
@ -323,6 +331,10 @@ fn cli_attach_with_user_and_instance_name_passes_through_dash_l_and_dash_dash_so
|
|||
.env(PMACS_TEST_SSH_BIN, &fake_ssh)
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
|
|
@ -361,6 +373,10 @@ fn spawn_bridge(socket: &Path, isolated_home: &Path) -> (Child, ChildStdin, Chil
|
|||
.arg(socket)
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
|
|
@ -466,6 +482,10 @@ fn cli_attach_with_missing_ssh_binary_surfaces_spawn_failure() {
|
|||
.env(PMACS_TEST_SSH_BIN, &missing_ssh)
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
|
|
|
|||
|
|
@ -151,6 +151,10 @@ fn handshake_retry_cap_fires_after_three_failed_handshakes() {
|
|||
.env(PMACS_TEST_BACKOFF_SCALE_MS, "1")
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
|
|
@ -198,6 +202,10 @@ fn backoff_scaling_observable_in_wall_clock_runtime() {
|
|||
.env(PMACS_TEST_BACKOFF_SCALE_MS, scale_ms)
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
|
|
@ -278,6 +286,10 @@ fn ssh_stderr_from_handshake_attempts_reaches_user() {
|
|||
.env(PMACS_TEST_BACKOFF_SCALE_MS, "1")
|
||||
.env("HOME", isolated_home)
|
||||
.env("XDG_CONFIG_HOME", isolated_home)
|
||||
.env("XDG_DATA_HOME", isolated_home)
|
||||
.env("XDG_STATE_HOME", isolated_home)
|
||||
.env("PMACS_STATE_HOME", isolated_home)
|
||||
.env("XDG_CACHE_HOME", isolated_home)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ impl TestDaemon {
|
|||
.arg(&socket_path)
|
||||
.env("HOME", tempdir.path())
|
||||
.env("XDG_CONFIG_HOME", tempdir.path())
|
||||
.env("XDG_DATA_HOME", tempdir.path())
|
||||
.env("XDG_STATE_HOME", tempdir.path())
|
||||
.env("PMACS_STATE_HOME", tempdir.path())
|
||||
.env("XDG_CACHE_HOME", tempdir.path())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use pmacs::lua_bindings::BufferIdLua;
|
|||
/// translate them into typed Rust errors because the chunk's
|
||||
/// assertions are the test contract.
|
||||
fn run(chunk: &str) {
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
editor
|
||||
.lua_host
|
||||
.eval(Some("@m6_4_test"), chunk)
|
||||
|
|
@ -53,7 +53,7 @@ fn run(chunk: &str) {
|
|||
/// Construct a fresh editor and return a captured value (for tests
|
||||
/// that want to do final assertions on the Rust side).
|
||||
fn run_returning<T: mlua::FromLuaMulti>(chunk: &str) -> T {
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
editor
|
||||
.lua_host
|
||||
.eval(Some("@m6_4_test"), chunk)
|
||||
|
|
@ -438,7 +438,7 @@ fn m6_4_buffer_id_is_a_real_buffer_handle() {
|
|||
// The handle's `buffer_id()` returns a real BufferIdLua that
|
||||
// resolves through the registry. This locks in that the package
|
||||
// is built atop genuinely-public surface; no shadow APIs.
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let returned: mlua::Value = editor
|
||||
.lua_host
|
||||
.eval(
|
||||
|
|
@ -457,3 +457,10 @@ fn m6_4_buffer_id_is_a_real_buffer_handle() {
|
|||
let buf = r.get(id_lua.id()).expect("registered");
|
||||
assert_eq!(buf.name(), "*handle*");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ fn locate_shell(name: &str) -> Option<PathBuf> {
|
|||
/// Construct a fresh editor and run the given Lua chunk against it.
|
||||
fn run(chunk: &str) {
|
||||
let _guard = pump_test_guard();
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
editor
|
||||
.lua_host
|
||||
.eval(Some("@m6_5_test"), chunk)
|
||||
|
|
@ -95,7 +95,7 @@ fn run(chunk: &str) {
|
|||
/// after-tick contract is exercised end-to-end.
|
||||
fn run_with_pump(setup_chunk: &str, predicate_chunk: &str, timeout_ms: u64) {
|
||||
let _guard = pump_test_guard();
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
editor
|
||||
.lua_host
|
||||
.eval(Some("@m6_5_setup"), setup_chunk)
|
||||
|
|
@ -528,3 +528,10 @@ fn m6_5_close_terminates_child_and_unregisters() {
|
|||
pmacs.hook.run("process.after-tick")
|
||||
"#);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ use pmacs::editor::EditorState;
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
fn run(chunk: &str) {
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
editor
|
||||
.lua_host
|
||||
.eval(Some("@m6_7_test"), chunk)
|
||||
|
|
@ -341,3 +341,10 @@ fn m6_7_50_truncation_events_preserve_block_boundaries() {
|
|||
pmacs.repl.config.scrollback_bytes = 16 * 1024 * 1024
|
||||
"#);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ fn m6_8_three_repls_render_independently() {
|
|||
let Some(lua) = locate_lua() else {
|
||||
return;
|
||||
};
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_three_and_wait_running(&mut editor, &lua);
|
||||
|
||||
// Write a unique marker through each REPL: `io.write("MARK_<n>\n")`
|
||||
|
|
@ -246,7 +246,7 @@ fn m6_8_three_repls_respond_independently() {
|
|||
let Some(lua) = locate_lua() else {
|
||||
return;
|
||||
};
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_three_and_wait_running(&mut editor, &lua);
|
||||
|
||||
// Type into h1, switch to h2, type into h2, etc. Each typed marker
|
||||
|
|
@ -308,7 +308,7 @@ fn m6_8_close_one_does_not_affect_others() {
|
|||
let Some(lua) = locate_lua() else {
|
||||
return;
|
||||
};
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_three_and_wait_running(&mut editor, &lua);
|
||||
|
||||
// Capture h2's proc_id before close; Handle:close clears the
|
||||
|
|
@ -375,7 +375,7 @@ fn m6_8_supervisor_reaps_all_children_across_cycles() {
|
|||
let Some(lua) = locate_lua() else {
|
||||
return;
|
||||
};
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
|
||||
// Baseline: list size before any spawning. The post-cycle list
|
||||
// size must equal this — no REPL processes left behind.
|
||||
|
|
@ -480,7 +480,7 @@ fn m6_8_supervisor_reaps_all_children_across_cycles() {
|
|||
/// `pmacs.repl.create` (which the spawn path also calls).
|
||||
#[test]
|
||||
fn m6_8_repls_have_independent_parser_state() {
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
editor
|
||||
.lua_host
|
||||
.eval(
|
||||
|
|
@ -505,7 +505,7 @@ fn m6_8_repls_have_independent_parser_state() {
|
|||
/// accidentally shared.
|
||||
#[test]
|
||||
fn m6_8_repls_have_independent_scrollback_state() {
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
editor
|
||||
.lua_host
|
||||
.eval(
|
||||
|
|
@ -535,7 +535,7 @@ fn m6_8_buffer_scoped_bindings_route_to_active_buffer() {
|
|||
let Some(lua) = locate_lua() else {
|
||||
return;
|
||||
};
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let setup = format!(
|
||||
r#"
|
||||
_G.h1 = pmacs.repl.spawn {{ argv = {{ "{lua}", "-i" }} }}
|
||||
|
|
@ -618,7 +618,7 @@ fn m6_8_after_tick_hook_drains_all_handles_per_tick() {
|
|||
let Some(lua) = locate_lua() else {
|
||||
return;
|
||||
};
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
spawn_three_and_wait_running(&mut editor, &lua);
|
||||
|
||||
// Capture pre-tick history_end on each handle.
|
||||
|
|
@ -692,3 +692,10 @@ fn m6_8_after_tick_hook_drains_all_handles_per_tick() {
|
|||
.exec()
|
||||
.expect("teardown");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ fn m6_6_sustained_ingest_rate_meets_100mbps_gate() {
|
|||
)
|
||||
.max(1);
|
||||
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// 100 chars + newline per line. The exact value is unimportant;
|
||||
// what matters is that `yes` blasts at a higher rate than pmacs's
|
||||
// ingest path, so pmacs is the bottleneck under measurement.
|
||||
|
|
@ -389,7 +389,7 @@ fn m6_6_buffer_memory_stays_under_200mb_during_run() {
|
|||
baseline_rss as f64 / (1024.0 * 1024.0)
|
||||
);
|
||||
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// 100-char line; the line content is unimportant for the ceiling
|
||||
// gate. Total target = 150 MB of bytes-into-history.
|
||||
let line = "a".repeat(100);
|
||||
|
|
@ -526,7 +526,7 @@ fn m6_6_cancel_response_p99_under_100ms() {
|
|||
let delay_ms = MIN_DELAY_MS + (r % (max_delay_ms - MIN_DELAY_MS + 1));
|
||||
let delay = Duration::from_millis(delay_ms);
|
||||
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let _ = spawn_repl(&mut editor, &["yes"]);
|
||||
wait_until_running(&mut editor);
|
||||
|
||||
|
|
@ -670,7 +670,7 @@ fn m6_7_scrollback_navigation_p99_under_16ms() {
|
|||
const TRIALS: usize = 1000;
|
||||
const P99_THRESHOLD: Duration = Duration::from_millis(16);
|
||||
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
populate_scrollback(&mut editor, LINES);
|
||||
seek_cursor_to_middle(&mut editor, LINES);
|
||||
|
||||
|
|
@ -749,7 +749,7 @@ fn m6_7_scrollback_search_p99_under_100ms() {
|
|||
const TRIALS: usize = 1000;
|
||||
const P99_THRESHOLD: Duration = Duration::from_millis(100);
|
||||
|
||||
let mut editor = EditorState::new();
|
||||
let mut editor = EditorState::new_with_roots(&crate::iso::roots());
|
||||
populate_scrollback(&mut editor, LINES);
|
||||
|
||||
// Warmup: 100 searches at varied positions. Same trace-compilation
|
||||
|
|
@ -815,3 +815,10 @@ fn m6_7_scrollback_search_p99_under_100ms() {
|
|||
p50={p50:?}, p90={p90:?}, max={max:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -89,14 +89,14 @@ fn repl_manifest_declares_exports_and_pmacs_required() {
|
|||
// Bullet 2b: bootstrap loads through the package system
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// EditorState::new() runs the M7.11 bootstrap that materializes
|
||||
// `EditorState::new()` runs the M7.11 bootstrap that materializes
|
||||
// the bundled REPL and pushes its InstalledPackage record into
|
||||
// the roster. After that, `require("repl")` from Lua resolves
|
||||
// through the M7.7 searcher (not the legacy direct eval).
|
||||
|
||||
#[test]
|
||||
fn editor_init_makes_repl_loadable_via_require() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let result: bool = state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -202,3 +202,10 @@ fn materialize_all_produces_one_record_per_bundled_package() {
|
|||
assert!(repl.entry_path().exists());
|
||||
let _ = std::fs::remove_dir_all(&tmp);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ fn outline_package_path() -> PathBuf {
|
|||
fn editor_with_outline() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -1262,3 +1262,10 @@ fn outline_aggregate_empty_sources_rejected() {
|
|||
"error must mention non-empty; got: {msg}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ fn pump_until<F: Fn(&EditorState) -> bool>(state: &mut EditorState, predicate: F
|
|||
/// Spin up a fresh editor with no file open. The caller drives the
|
||||
/// fs API entirely from Lua chunks via `lua_host.eval`.
|
||||
fn fresh_editor() -> EditorState {
|
||||
EditorState::new()
|
||||
EditorState::new_with_roots(&crate::iso::roots())
|
||||
}
|
||||
|
||||
/// Run a Lua chunk to completion (no `:await`); returns the chunk's
|
||||
|
|
@ -609,3 +609,10 @@ fn fs_watch_reports_file_change_and_can_cancel() {
|
|||
.expect("cancelled");
|
||||
assert!(cancelled, "watch handle must report cancellation");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ fn pump_until<F: Fn(&EditorState) -> bool>(state: &mut EditorState, predicate: F
|
|||
fn editor_with_dired() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -1251,3 +1251,10 @@ fn dired_source_size_under_audit_ceiling() {
|
|||
"M8.2 spec: dired source under 1500 lines; got {lines}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ fn pump_until<F: Fn(&EditorState) -> bool>(state: &mut EditorState, predicate: F
|
|||
fn editor_with_dired() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -2101,3 +2101,10 @@ fn dired_active_handle_is_cleared_when_active_buffer_removed() {
|
|||
"active_handle must not return a stale handle after its buffer is removed"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ fn pump_until<F: Fn(&EditorState) -> bool>(state: &mut EditorState, predicate: F
|
|||
fn editor_with_magit() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -913,3 +913,10 @@ fn magit_cursor_on_hidden_descendant_reseats_to_visible_ancestor() {
|
|||
"cursor on hidden A1 must reseat to A's header (line 0)"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ fn pump_until_with_deadline<F: Fn(&EditorState) -> bool>(
|
|||
fn editor_with_magit() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -653,3 +653,10 @@ fn magit_build_spec_section_ids_are_stable_canonical_set() {
|
|||
.expect("build_spec ids");
|
||||
assert_eq!(ids, "working,staged,log,branches,stashes");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ fn pump_until_with_deadline<F: Fn(&EditorState) -> bool>(
|
|||
fn editor_with_magit() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -1011,3 +1011,10 @@ fn magit_b_c_keybinding_dispatches_to_branch_create_prompt() {
|
|||
.eval(Some("cancel"), "pmacs.minibuffer.cancel()")
|
||||
.expect("cancel");
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ fn outline_package_path() -> PathBuf {
|
|||
fn editor_with_outline() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -1090,3 +1090,10 @@ fn outline_parser_unit_parses_org_subset() {
|
|||
.expect("tags");
|
||||
assert_eq!(n_tags, 2);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -725,7 +725,7 @@ fn m9_1_oncrash_policy_does_not_restart_on_clean_exit() {
|
|||
fn m9_1_lua_surface_drives_mcp_lifecycle() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_mcp_path();
|
||||
let lua = state.lua_host.lua();
|
||||
let sid_raw: u64 = lua
|
||||
|
|
@ -815,7 +815,7 @@ fn m9_1_lua_surface_drives_mcp_lifecycle() {
|
|||
fn m9_1_lua_send_request_returns_awaitable_handle() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_mcp_path();
|
||||
let lua = state.lua_host.lua();
|
||||
|
||||
|
|
@ -924,3 +924,10 @@ fn m9_1_lua_send_request_returns_awaitable_handle() {
|
|||
.load("pmacs.mcp.stop(_G._mcp_test_server)")
|
||||
.exec();
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -587,7 +587,7 @@ fn m9_2_cancelled_sibling_wins_over_queued_response() {
|
|||
fn m9_2_lua_read_resource_returns_awaitable_handle() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_mcp_path();
|
||||
|
||||
state
|
||||
|
|
@ -738,3 +738,10 @@ fn m9_2_lua_read_resource_returns_awaitable_handle() {
|
|||
.load("pmacs.mcp.stop(_G._mcp_test_server)")
|
||||
.exec();
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ fn m9_3_cancellation_reaches_server() {
|
|||
fn m9_3_lua_invoke_tool_returns_awaitable_handle() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_mcp_path();
|
||||
|
||||
state
|
||||
|
|
@ -496,3 +496,10 @@ fn m9_3_lua_invoke_tool_returns_awaitable_handle() {
|
|||
.load("pmacs.mcp.stop(_G._mcp_test_server)")
|
||||
.exec();
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ fn m9_4_no_args_wire_shape_is_empty_object() {
|
|||
fn m9_4_lua_get_prompt_returns_awaitable_handle() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = fake_mcp_path();
|
||||
|
||||
state
|
||||
|
|
@ -494,3 +494,10 @@ fn m9_4_lua_get_prompt_returns_awaitable_handle() {
|
|||
.load("pmacs.mcp.stop(_G._mcp_test_server)")
|
||||
.exec();
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ fn resources_package_path() -> PathBuf {
|
|||
fn editor_with_resources() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -1275,3 +1275,10 @@ fn m9_5_stale_buffer_recovers_on_reopen_after_restart() {
|
|||
"after re-open, the buffer must have fresh content; got {reopen_body:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ fn tools_package_path() -> PathBuf {
|
|||
fn editor_with_tools() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -1532,3 +1532,10 @@ fn m9_6_editor_describe_command_unknown_name_status_only() {
|
|||
"*help* must not be created for an unknown-name describe-command call"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ fn prompts_package_path() -> PathBuf {
|
|||
fn editor_with_prompts() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -1386,3 +1386,10 @@ fn m9_7_prompt_hash_includes_required_argument_order() {
|
|||
"reordering required args must change the prompt hash so reconcile re-registers"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ fn prompts_package_path() -> PathBuf {
|
|||
fn editor_with_ai() -> (EditorState, TempDir, TempDir) {
|
||||
let cache = tempfile::tempdir().expect("cache tempdir");
|
||||
let user_root = tempfile::tempdir().expect("user-root tempdir");
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state.lua_host.reopen_init_phase_for_testing();
|
||||
state.lua_host.set_package_install_override(
|
||||
PackageInstallOverride::new()
|
||||
|
|
@ -1122,3 +1122,10 @@ fn m9_8_configured_prompt_missing_on_server_surfaces_error() {
|
|||
state.core.borrow().status
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ fn count_of(kinds: &[String], kind: &str) -> usize {
|
|||
|
||||
#[test]
|
||||
fn switch_away_and_back_reattaches_syntax_overlay_exactly_once() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let path = open_probe_file(&s);
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -86,7 +86,7 @@ fn switch_away_and_back_reattaches_syntax_overlay_exactly_once() {
|
|||
|
||||
#[test]
|
||||
fn panel_quit_restores_overlays_on_the_source_buffer() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let path = open_probe_file(&s);
|
||||
s.lua_host
|
||||
.lua()
|
||||
|
|
@ -121,3 +121,10 @@ fn panel_quit_restores_overlays_on_the_source_buffer() {
|
|||
"leaving a panel restores the source buffer's styling (got {after:?})"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ fn editor_with_state_dir() -> (EditorState, PathBuf) {
|
|||
SEQ.fetch_add(1, Ordering::Relaxed)
|
||||
));
|
||||
std::fs::create_dir_all(&dir).expect("mk state tempdir");
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// Override whatever startup configured with our tempdir.
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host.lua().set_app_data(StateDir(dir.clone()));
|
||||
|
|
@ -73,12 +73,12 @@ fn state_round_trips_and_rejects_escapes() {
|
|||
|
||||
#[test]
|
||||
fn state_is_inert_when_unconfigured() {
|
||||
// A plain EditorState::new() must NOT configure a state dir — that
|
||||
// A plain `EditorState::new()` must NOT configure a state dir — that
|
||||
// is what keeps the whole integration-test suite (which links the
|
||||
// lib without cfg(test)) from writing to a developer's real
|
||||
// ~/.local/state/pmacs. Only the real entry points call
|
||||
// install_state_dirs(); tests construct EditorState directly.
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
assert!(
|
||||
s.lua_host.lua().app_data_ref::<StateDir>().is_none(),
|
||||
"new() must leave the state dir unconfigured"
|
||||
|
|
@ -182,3 +182,10 @@ fn saveplace_can_be_disabled() {
|
|||
assert_eq!(cursor, 0, "disabled saveplace leaves the cursor at the top");
|
||||
std::fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ fn start_query_replace(s: &mut EditorState, from: &str, to: &str, regex: bool) {
|
|||
|
||||
#[test]
|
||||
fn replace_skip_and_quit_is_selective() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "x x x x");
|
||||
// Cursor to buffer start so all four are ahead of point.
|
||||
goto_start(&s);
|
||||
|
|
@ -107,7 +107,7 @@ fn replace_skip_and_quit_is_selective() {
|
|||
|
||||
#[test]
|
||||
fn bang_replaces_all_remaining() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a a a a");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "a", "b", false);
|
||||
|
|
@ -119,7 +119,7 @@ fn bang_replaces_all_remaining() {
|
|||
|
||||
#[test]
|
||||
fn dot_replaces_current_then_quits() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a a a");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "a", "z", false);
|
||||
|
|
@ -133,7 +133,7 @@ fn dot_replaces_current_then_quits() {
|
|||
fn growing_replacement_does_not_loop() {
|
||||
// a → aa must not re-match the inserted text (offset-shift + the
|
||||
// search-forward-past-replacement rule).
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a a a");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "a", "aa", false);
|
||||
|
|
@ -144,7 +144,7 @@ fn growing_replacement_does_not_loop() {
|
|||
|
||||
#[test]
|
||||
fn empty_to_deletes() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a-b-c");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "-", "", false);
|
||||
|
|
@ -155,7 +155,7 @@ fn empty_to_deletes() {
|
|||
|
||||
#[test]
|
||||
fn regex_query_replace_via_binding() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a1 b2 c3");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "[0-9]", "#", true);
|
||||
|
|
@ -167,7 +167,7 @@ fn regex_query_replace_via_binding() {
|
|||
#[test]
|
||||
fn m_percent_binding_starts_query_replace() {
|
||||
// The literal chord: M-% (Alt + Shift+5 → Char('%') with ALT).
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "cat cat");
|
||||
goto_start(&s);
|
||||
s.dispatch_key(
|
||||
|
|
@ -196,7 +196,7 @@ fn m_percent_binding_starts_query_replace() {
|
|||
fn c_m_percent_binding_starts_regexp_query_replace() {
|
||||
// Control-meta-shifted punctuation — the chord most likely to parse
|
||||
// differently across key paths (the C-c H lesson).
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "x1 x2");
|
||||
goto_start(&s);
|
||||
s.dispatch_key(
|
||||
|
|
@ -224,7 +224,7 @@ fn c_m_percent_binding_starts_regexp_query_replace() {
|
|||
|
||||
#[test]
|
||||
fn nothing_matched_leaves_buffer_untouched() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "hello");
|
||||
start_query_replace(&mut s, "zzz", "q", false);
|
||||
let (text, active) = probe(&s);
|
||||
|
|
@ -236,7 +236,7 @@ fn nothing_matched_leaves_buffer_untouched() {
|
|||
fn query_replace_flips_dispatch_idle_so_gpu_round_trips() {
|
||||
// While the interactive phase runs, dispatch_idle must be false so a
|
||||
// semantic frontend round-trips y/n/etc. instead of self-inserting.
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a a");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "a", "b", false);
|
||||
|
|
@ -251,7 +251,7 @@ fn query_replace_flips_dispatch_idle_so_gpu_round_trips() {
|
|||
#[test]
|
||||
fn replace_fires_after_edit_hook() {
|
||||
// The Q#QR1 hook: an LSP/syntax observer must see replaced text.
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host
|
||||
.lua()
|
||||
.load(
|
||||
|
|
@ -289,7 +289,7 @@ fn bang_fires_after_edit_hook_once_for_the_batch() {
|
|||
// Q#QR1: `!` applies many replacements under one keypress, but the
|
||||
// debounced didChange wants a single after-edit — the shadow
|
||||
// compares revision once across the whole handler.
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host
|
||||
.lua()
|
||||
.load(
|
||||
|
|
@ -322,7 +322,7 @@ fn bang_fires_after_edit_hook_once_for_the_batch() {
|
|||
fn quit_via_ret_and_esc_keeps_replacements() {
|
||||
// Q#QR10: RET and Esc both quit (keeping replacements), not just q.
|
||||
for quit in [KeyCode::Enter, KeyCode::Esc] {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a a a");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "a", "b", false);
|
||||
|
|
@ -337,7 +337,7 @@ fn quit_via_ret_and_esc_keeps_replacements() {
|
|||
#[test]
|
||||
fn ctrl_g_quits_keeping_replacements() {
|
||||
// Q#QR10: C-g exits and KEEPS replacements (unlike isearch's C-g).
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a a a");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "a", "b", false);
|
||||
|
|
@ -353,7 +353,7 @@ fn ctrl_g_quits_keeping_replacements() {
|
|||
|
||||
#[test]
|
||||
fn del_key_skips_like_n() {
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "a a a");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "a", "b", false);
|
||||
|
|
@ -373,7 +373,7 @@ fn focus_drift_mid_session_aborts_without_touching_either_buffer() {
|
|||
// (simulated by switch_buffer, which the pointer path also uses)
|
||||
// while query-replace is active. The next y must abort, not apply
|
||||
// the origin-buffer match to the now-active unrelated buffer.
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
type_str(&mut s, "foo foo");
|
||||
goto_start(&s);
|
||||
start_query_replace(&mut s, "foo", "bar", false);
|
||||
|
|
@ -416,3 +416,10 @@ fn focus_drift_mid_session_aborts_without_touching_either_buffer() {
|
|||
"origin buffer untouched by the aborted replace"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ impl Fixture {
|
|||
}
|
||||
|
||||
fn editor() -> EditorState {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// No language server may spawn from these fixtures. The LSP rows
|
||||
// that DO want one configure it explicitly.
|
||||
exec(&state, "pmacs.lsp.config = {}");
|
||||
|
|
@ -2043,3 +2043,10 @@ fn a_subscriber_reconciliation_failure_is_reported_and_the_rest_still_reconcile(
|
|||
unreconciled — the raise has to come after the loop, not inside it"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ fn save_refuses_to_clobber_a_file_changed_on_disk() {
|
|||
write(&f, "original\n");
|
||||
let fs = f.display().to_string();
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_and_dirty(&s, &fs);
|
||||
|
||||
// Another writer lands between our read and our save.
|
||||
|
|
@ -88,7 +88,7 @@ fn the_save_command_does_not_fire_after_save_when_it_refuses() {
|
|||
write(&f, "original\n");
|
||||
let fs = f.display().to_string();
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_and_dirty(&s, &fs);
|
||||
exec(
|
||||
&s,
|
||||
|
|
@ -110,7 +110,7 @@ fn save_anyway_overwrites_deliberately_and_resyncs_meta() {
|
|||
write(&f, "original\n");
|
||||
let fs = f.display().to_string();
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_and_dirty(&s, &fs);
|
||||
write(&f, "theirs\n");
|
||||
assert!(!eval::<bool>(&s, "return pmacs.editor.save()"));
|
||||
|
|
@ -137,7 +137,7 @@ fn an_unchanged_file_saves_normally_and_repeatedly() {
|
|||
write(&f, "original\n");
|
||||
let fs = f.display().to_string();
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_and_dirty(&s, &fs);
|
||||
assert!(eval::<bool>(&s, "return pmacs.editor.save()"));
|
||||
assert_eq!(read(&f), "mine original\n");
|
||||
|
|
@ -157,7 +157,7 @@ fn a_deleted_file_is_recreated_not_refused() {
|
|||
write(&f, "original\n");
|
||||
let fs = f.display().to_string();
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
open_and_dirty(&s, &fs);
|
||||
// Nothing on disk to clobber, so recreating it is not data loss.
|
||||
std::fs::remove_file(&f).unwrap();
|
||||
|
|
@ -174,7 +174,7 @@ fn a_new_file_buffer_refuses_once_someone_else_creates_the_file() {
|
|||
let dir = tempdir();
|
||||
let missing = dir.join("draft.txt");
|
||||
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// The argv `[new file]` shape: a path with nothing on disk, no meta.
|
||||
exec(
|
||||
&s,
|
||||
|
|
@ -200,3 +200,10 @@ fn a_new_file_buffer_refuses_once_someone_else_creates_the_file() {
|
|||
assert_eq!(read(&missing), "my draft");
|
||||
std::fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ use pmacs::statusline::{
|
|||
StatuslineEvaluationOutcome, StatuslineEvaluationTarget, evaluate_statusline,
|
||||
};
|
||||
|
||||
#[cfg(feature = "crdt")]
|
||||
// Ungated: `common::iso` (isolated bootstrap roots) is needed in every
|
||||
// build, not only the CRDT one.
|
||||
mod common;
|
||||
|
||||
fn exec(state: &EditorState, source: &str) {
|
||||
|
|
@ -41,7 +42,7 @@ fn eval<T: mlua::FromLuaMulti>(state: &EditorState, source: &str) -> T {
|
|||
}
|
||||
|
||||
fn editor() -> EditorState {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, "pmacs.lsp.config = {}");
|
||||
state
|
||||
}
|
||||
|
|
@ -689,7 +690,7 @@ fn a09_11_full_tui_frame_composes_unicode_clips_and_preserves_echo() {
|
|||
// map and polls the real tracker without a buffer edit.
|
||||
#[test]
|
||||
fn a12_builtin_lsp_provider_tracks_real_attachment_and_unknown_label() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let fake = env!("CARGO_BIN_EXE_pmacs_fake_lsp");
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let path = temp.path().join("statusline.rs");
|
||||
|
|
@ -1048,3 +1049,11 @@ fn a16_26_real_daemon_v17_gate_v18_first_frame_and_late_join() {
|
|||
"late join sees established state"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root. Re-exported rather than re-declared
|
||||
// with `#[path]` — this file already pulls in `common`, and loading one
|
||||
// source file as two modules is `clippy::duplicate_mod`.
|
||||
use common::iso;
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ fn escape_was_armed(state: &mut EditorState, buffer: pmacs::buffer::BufferId, pr
|
|||
/// Acceptance 1: a profile spec is strict, and rejects before anything spawns.
|
||||
#[test]
|
||||
fn acc1_profile_specs_are_strict_and_reject_before_spawning() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let before = state.core.borrow().registry.borrow().ids().len();
|
||||
|
||||
exec(
|
||||
|
|
@ -218,7 +218,7 @@ fn acc1_profile_specs_are_strict_and_reject_before_spawning() {
|
|||
/// Acceptance 2: an unknown profile names the known ones and creates nothing.
|
||||
#[test]
|
||||
fn acc2_unknown_profile_lists_known_names_and_creates_nothing() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, CAT_PROFILE);
|
||||
exec(
|
||||
&state,
|
||||
|
|
@ -262,7 +262,7 @@ fn acc2_unknown_profile_lists_known_names_and_creates_nothing() {
|
|||
/// unknown-profile path, replacing the exact error being asked for.
|
||||
#[test]
|
||||
fn acc2_malformed_profile_keys_do_not_mask_the_unknown_profile_error() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, CAT_PROFILE);
|
||||
exec(
|
||||
&state,
|
||||
|
|
@ -297,7 +297,7 @@ fn acc2_malformed_profile_keys_do_not_mask_the_unknown_profile_error() {
|
|||
/// `env` MERGES rather than replacing.
|
||||
#[test]
|
||||
fn acc3_field_resolution_order_and_env_merge() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&state,
|
||||
r#"
|
||||
|
|
@ -323,7 +323,7 @@ fn acc3_field_resolution_order_and_env_merge() {
|
|||
/// Acceptance 3 (explicit command wins) and 4 (`""` means no profile).
|
||||
#[test]
|
||||
fn acc3_acc4_explicit_command_wins_and_empty_default_means_no_profile() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, CAT_PROFILE);
|
||||
exec(
|
||||
&state,
|
||||
|
|
@ -373,7 +373,7 @@ pmacs.terminal.profiles.fill = {
|
|||
/// fallback) deleted outright.
|
||||
#[test]
|
||||
fn acc5_scrollback_setting_reaches_retained_history() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, FILL_PROFILE);
|
||||
|
||||
// Arm 1: `0` is legal, and means the early rows are GONE.
|
||||
|
|
@ -423,7 +423,7 @@ fn acc5_scrollback_setting_reaches_retained_history() {
|
|||
/// values, and `0` is inside it rather than a disabled sentinel.
|
||||
#[test]
|
||||
fn acc5_scrollback_bounds() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, r#"pmacs.config.set("terminal.scrollback-rows", 0)"#);
|
||||
assert_eq!(
|
||||
state
|
||||
|
|
@ -458,7 +458,7 @@ fn acc5_scrollback_bounds() {
|
|||
/// THAT chord to the child, and an ordinary `C-c` still reaches the child.
|
||||
#[test]
|
||||
fn acc6_acc9_configured_escape_chord_and_literal_repeat() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, CAT_PROFILE);
|
||||
let buffer = open_cat_terminal(&state, r#"profile = "echo""#);
|
||||
assert!(tick_until(&mut state, "READY", buffer));
|
||||
|
|
@ -499,7 +499,7 @@ fn acc6_acc9_configured_escape_chord_and_literal_repeat() {
|
|||
/// count that does not grow, and a cache that dies with its terminal.
|
||||
#[test]
|
||||
fn acc7_acc8_acc8a_per_terminal_escape_cache_identity_and_lifecycle() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, CAT_PROFILE);
|
||||
let a = open_cat_terminal(&state, r#"profile = "echo""#);
|
||||
exec(&state, "TERM_A = TERM_BUF");
|
||||
|
|
@ -633,7 +633,7 @@ fn acc7_acc8_acc8a_per_terminal_escape_cache_identity_and_lifecycle() {
|
|||
/// the status line, and reports once per terminal per effective bad value.
|
||||
#[test]
|
||||
fn acc10_acc10a_invalid_escape_falls_back_and_reports_once() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, CAT_PROFILE);
|
||||
let buffer = open_cat_terminal(&state, r#"profile = "echo""#);
|
||||
assert!(tick_until(&mut state, "READY", buffer));
|
||||
|
|
@ -699,7 +699,7 @@ fn acc10_acc10a_invalid_escape_falls_back_and_reports_once() {
|
|||
/// proves the second half).
|
||||
#[test]
|
||||
fn acc11_terminal_opening_binding_is_bound_and_shadowed_nothing() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let command: Option<String> = state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -717,7 +717,7 @@ fn acc11_terminal_opening_binding_is_bound_and_shadowed_nothing() {
|
|||
/// defaults reproduce the pre-arc behavior.
|
||||
#[test]
|
||||
fn acc12_defaults_reproduce_prior_behavior() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let lua = state.lua_host.lua();
|
||||
assert_eq!(
|
||||
lua.load(r#"return pmacs.config.get("terminal.default-profile")"#)
|
||||
|
|
@ -744,3 +744,10 @@ fn acc12_defaults_reproduce_prior_behavior() {
|
|||
"no profiles are registered by default"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ fn buffer_count(state: &EditorState) -> usize {
|
|||
/// `copy_selection_bytes` itself.
|
||||
#[test]
|
||||
fn acc13_snapshot_is_the_whole_retained_range_through_the_shared_serializer() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
|
||||
|
|
@ -250,7 +250,7 @@ fn acc13_snapshot_is_the_whole_retained_range_through_the_shared_serializer() {
|
|||
/// buffer-shaped consumer work and what removes the transport arm.
|
||||
#[test]
|
||||
fn acc14_the_snapshot_is_an_ordinary_non_terminal_buffer() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
exec(&state, "pmacs.terminal.copy_mode(TERM_BUF)");
|
||||
|
|
@ -272,7 +272,7 @@ fn acc14_the_snapshot_is_an_ordinary_non_terminal_buffer() {
|
|||
/// with no change to `src/search.rs` (B1).
|
||||
#[test]
|
||||
fn acc15_isearch_finds_content_only_in_scrollback() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
|
||||
|
|
@ -319,7 +319,7 @@ fn acc15_isearch_finds_content_only_in_scrollback() {
|
|||
/// agreement; what stops the mutation is this.
|
||||
#[test]
|
||||
fn acc16_dispatch_idle_is_false_while_the_snapshot_is_focused() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
exec(&state, "pmacs.terminal.copy_mode(TERM_BUF)");
|
||||
|
|
@ -344,7 +344,7 @@ fn acc16_dispatch_idle_is_false_while_the_snapshot_is_focused() {
|
|||
/// protection does not depend on which key or command was used.
|
||||
#[test]
|
||||
fn acc16b_the_snapshot_is_immutable_at_the_rope_not_merely_intercepted() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
exec(&state, "pmacs.terminal.copy_mode(TERM_BUF)");
|
||||
|
|
@ -390,7 +390,7 @@ fn acc16b_the_snapshot_is_immutable_at_the_rope_not_merely_intercepted() {
|
|||
/// leaves the buffer emptiable. Only rope-level `read_only` closes both.
|
||||
#[test]
|
||||
fn acc16c_undo_cannot_empty_the_snapshot_by_chord_or_by_command() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
exec(&state, "pmacs.terminal.copy_mode(TERM_BUF)");
|
||||
|
|
@ -523,7 +523,7 @@ fn grid_row(cells: &[pmacs::cell::Cell], row: u32, cols: u32) -> String {
|
|||
/// any other owner that adopts the primitive later.
|
||||
#[test]
|
||||
fn acc16d_a_generated_write_notifies_the_window_that_displays_it() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&state,
|
||||
r"
|
||||
|
|
@ -567,7 +567,7 @@ fn acc16d_a_generated_write_notifies_the_window_that_displays_it() {
|
|||
#[cfg(feature = "crdt")]
|
||||
#[test]
|
||||
fn acc16e_a_refresh_queues_the_owners_write_for_replica_mirrors() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
exec(&state, "pmacs.terminal.copy_mode(TERM_BUF)");
|
||||
|
|
@ -606,7 +606,7 @@ fn acc16e_a_refresh_queues_the_owners_write_for_replica_mirrors() {
|
|||
/// both directions.
|
||||
#[test]
|
||||
fn acc18_reinvoke_refreshes_in_place_and_lifecycle_runs_both_ways() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
|
||||
|
|
@ -681,7 +681,7 @@ fn acc18_reinvoke_refreshes_in_place_and_lifecycle_runs_both_ways() {
|
|||
/// `q` returns to the source terminal.
|
||||
#[test]
|
||||
fn acc19_escape_c_t_enters_copy_mode_and_g_and_q_work() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
let terminal_name = active_buffer_name(&state);
|
||||
|
|
@ -749,7 +749,7 @@ fn acc19_escape_c_t_enters_copy_mode_and_g_and_q_work() {
|
|||
/// tail.
|
||||
#[test]
|
||||
fn acc20_live_terminal_keys_are_unchanged_while_a_snapshot_exists() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
let key = focus_terminal(&state, terminal);
|
||||
exec(&state, "pmacs.terminal.copy_mode(TERM_BUF)");
|
||||
|
|
@ -806,7 +806,7 @@ fn acc20_live_terminal_keys_are_unchanged_while_a_snapshot_exists() {
|
|||
/// (or nothing) while the keys behaved differently.
|
||||
#[test]
|
||||
fn acc21_describe_key_reports_the_truth_for_the_snapshot_bindings() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
exec(&state, "pmacs.terminal.copy_mode(TERM_BUF)");
|
||||
|
|
@ -847,7 +847,7 @@ fn acc21_describe_key_reports_the_truth_for_the_snapshot_bindings() {
|
|||
/// (dired's F7 rule); a taken name gets a `<2>` variant instead.
|
||||
#[test]
|
||||
fn acc18a_a_foreign_same_named_buffer_is_never_adopted_or_clobbered() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let terminal = open_fill_terminal(&mut state);
|
||||
focus_terminal(&state, terminal);
|
||||
|
||||
|
|
@ -898,7 +898,7 @@ fn acc18a_a_foreign_same_named_buffer_is_never_adopted_or_clobbered() {
|
|||
/// terminal, and killing either one removes the shared snapshot.
|
||||
#[test]
|
||||
fn acc18b_two_same_named_terminals_get_two_independent_snapshots() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&state, FILL_PROFILE);
|
||||
|
||||
let before = terminal_buffers(&state);
|
||||
|
|
@ -985,7 +985,7 @@ fn acc18b_two_same_named_terminals_get_two_independent_snapshots() {
|
|||
/// snapshot of nothing.
|
||||
#[test]
|
||||
fn copy_mode_refuses_a_non_terminal_buffer() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let err = eval_err(&state, "return pmacs.terminal.copy_mode()");
|
||||
assert!(
|
||||
err.contains("not a terminal"),
|
||||
|
|
@ -1010,7 +1010,7 @@ fn copy_mode_refuses_a_non_terminal_buffer() {
|
|||
/// state. Falsify by deleting the `win.cursor > len` clamp.
|
||||
#[test]
|
||||
fn acc16f_a_shrinking_generated_write_clamps_the_window_cursor() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&state,
|
||||
r"
|
||||
|
|
@ -1066,7 +1066,7 @@ fn acc16f_a_shrinking_generated_write_clamps_the_window_cursor() {
|
|||
/// the measurement.
|
||||
#[test]
|
||||
fn acc16g_a_line_collapsing_generated_write_clamps_view_top() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&state,
|
||||
r"
|
||||
|
|
@ -1129,7 +1129,7 @@ fn acc16g_a_line_collapsing_generated_write_clamps_view_top() {
|
|||
/// `notify_buffer_edit`; the first copy reaches the stale-anchor panic.
|
||||
#[test]
|
||||
fn acc16h_a_shrinking_generated_write_clamps_or_clears_the_selection() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(
|
||||
&state,
|
||||
r"
|
||||
|
|
@ -1224,7 +1224,7 @@ fn acc16h_a_shrinking_generated_write_clamps_or_clears_the_selection() {
|
|||
/// `acc16h` stays green.
|
||||
#[test]
|
||||
fn acc16i_a_shrinking_view_rebuild_clamps_or_clears_the_selection() {
|
||||
let state = EditorState::new();
|
||||
let state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
// 286 bytes, then 154: a real shrink through the help renderer.
|
||||
exec(
|
||||
&state,
|
||||
|
|
@ -1300,3 +1300,10 @@ fn acc16i_a_shrinking_view_rebuild_clamps_or_clears_the_selection() {
|
|||
"the collapsed region is not retained as active-but-empty"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ use pmacs::protocol::{ByteRange, FrontendId, InstanceMessage, ThemeFace};
|
|||
use pmacs::semantic_render::SemanticRenderState;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(feature = "crdt")]
|
||||
// Ungated: `common::iso` (isolated bootstrap roots) is needed in every
|
||||
// build, not only the CRDT one.
|
||||
mod common;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -78,7 +79,7 @@ fn eval<T: mlua::FromLuaMulti>(s: &EditorState, src: &str) -> T {
|
|||
|
||||
/// Fresh editor with LSP spawning disabled.
|
||||
fn editor() -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
s
|
||||
}
|
||||
|
|
@ -184,7 +185,7 @@ fn current_tree_language(state: &EditorState) -> Option<String> {
|
|||
|
||||
/// Open `path` and pump until its parse settles (highlights attached).
|
||||
fn open_and_wait_for_parse(path: std::path::PathBuf) -> EditorState {
|
||||
let mut state = EditorState::open(path).expect("open file");
|
||||
let mut state = EditorState::open_with_roots(path, &crate::iso::roots()).expect("open file");
|
||||
exec(&state, "pmacs.lsp.config = {}");
|
||||
pump_async(&mut state, |s| current_tree_language(s).is_some());
|
||||
state
|
||||
|
|
@ -1511,3 +1512,11 @@ fn minibuffer_and_candidate_faces_apply_through_m_x() {
|
|||
panic!("no candidate suffix rendered: {text:?}");
|
||||
}
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root. Re-exported rather than re-declared
|
||||
// with `#[path]` — this file already pulls in `common`, and loading one
|
||||
// source file as two modules is `clippy::duplicate_mod`.
|
||||
use common::iso;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ fn status(s: &EditorState) -> String {
|
|||
/// Fresh scratch-buffer editor, cursor at 0. Scratch pairing uses the
|
||||
/// `default` set, so `(` pairs — which is what 46c reads.
|
||||
fn editor_with(body: &str) -> EditorState {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
if !body.is_empty() {
|
||||
exec(&s, &format!("pmacs.window.buffer():insert(0, {body:?})"));
|
||||
}
|
||||
|
|
@ -656,7 +656,7 @@ fn a_chain_consumers_edit_reaches_the_first_did_change() {
|
|||
let sink_disp = sink.display().to_string();
|
||||
let fake = fake_lsp_path();
|
||||
|
||||
let mut s = EditorState::new();
|
||||
let mut s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.lua_host.lua().remove_app_data::<StateDir>();
|
||||
s.lua_host.lua().set_app_data(StateDir(dir.clone()));
|
||||
exec(&s, "pmacs.lsp.config = {}");
|
||||
|
|
@ -733,3 +733,10 @@ fn a_chain_consumers_edit_reaches_the_first_did_change() {
|
|||
before lsp.lua's synchronous flush (Q#AP7)"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ fn terminal_cells_reject_child_control_characters() {
|
|||
|
||||
#[test]
|
||||
fn spawn_failure_is_transactional() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let buffers_before = state.core.borrow().registry.borrow().len();
|
||||
let processes_before = state.process_supervisor.borrow().ids().count();
|
||||
state.process_supervisor.borrow_mut().shutdown();
|
||||
|
|
@ -77,7 +77,7 @@ fn spawn_failure_is_transactional() {
|
|||
|
||||
#[test]
|
||||
fn strict_owned_spec_rejects_before_spawn_and_is_mutation_independent() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let buffers_before = state.core.borrow().registry.borrow().len();
|
||||
let mut invalid = TerminalSpec::new("/bin/sh");
|
||||
invalid.rows = 0;
|
||||
|
|
@ -193,7 +193,7 @@ fn read_only_empty_crdt_bootstrap_is_immutable_against_remote_content() {
|
|||
|
||||
#[test]
|
||||
fn final_output_precedes_exact_nonzero_annotation_and_buffer_is_retained() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let mut spec = TerminalSpec::new("/bin/sh");
|
||||
spec.args = vec![
|
||||
"-c".into(),
|
||||
|
|
@ -306,7 +306,7 @@ fn normal_and_signal_annotations_use_exact_pid_and_outcome() {
|
|||
"exited abnormally with signal SIGTERM",
|
||||
),
|
||||
] {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let mut spec = TerminalSpec::new("/bin/sh");
|
||||
spec.args = vec!["-c".into(), script.into()];
|
||||
spec.rows = 6;
|
||||
|
|
@ -334,7 +334,7 @@ fn normal_and_signal_annotations_use_exact_pid_and_outcome() {
|
|||
|
||||
#[test]
|
||||
fn killing_terminal_buffer_prunes_session_and_reaps_owned_process() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let mut spec = TerminalSpec::new("/bin/sh");
|
||||
spec.args = vec!["-c".into(), "sleep 30".into()];
|
||||
let buffer_id = state.open_terminal(spec).expect("open terminal");
|
||||
|
|
@ -364,7 +364,7 @@ fn killing_terminal_buffer_prunes_session_and_reaps_owned_process() {
|
|||
#[test]
|
||||
fn editor_shutdown_kills_term_ignoring_terminal_child() {
|
||||
let pid = {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state
|
||||
.process_supervisor
|
||||
.borrow_mut()
|
||||
|
|
@ -397,7 +397,7 @@ fn editor_shutdown_kills_term_ignoring_terminal_child() {
|
|||
|
||||
#[test]
|
||||
fn terminal_tick_does_not_take_non_terminal_process_events() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let mut process = pmacs::process::ProcessSpec::new("ordinary", "/bin/sh");
|
||||
process.args = vec!["-c".into(), "printf ordinary".into()];
|
||||
let ordinary_id = state
|
||||
|
|
@ -424,3 +424,10 @@ fn terminal_tick_does_not_take_non_terminal_process_events() {
|
|||
"TerminalManager must not steal ordinary process output"
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ fn snapshot_text(snapshot: &pmacs::terminal::TerminalSnapshot) -> String {
|
|||
reason = "cross-surface Lua transaction scenario"
|
||||
)]
|
||||
fn lua_surface_is_strict_fresh_transactional_and_context_safe() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let command_lua = lua_string("/bin/sh");
|
||||
let baseline_buffer_id = state.core.borrow().active_buffer_id();
|
||||
|
||||
|
|
@ -364,7 +364,7 @@ fn lua_surface_is_strict_fresh_transactional_and_context_safe() {
|
|||
#[test]
|
||||
#[allow(clippy::too_many_lines, reason = "shared view and controller scenario")]
|
||||
fn shared_screen_keeps_view_scroll_selection_and_controller_independent() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let mut spec = TerminalSpec::new("/bin/sh");
|
||||
spec.args = vec![
|
||||
"-c".into(),
|
||||
|
|
@ -508,7 +508,7 @@ fn terminal_escape_gates_local_bindings_and_double_escape_sends_interrupt() {
|
|||
ready_path.to_str().expect("UTF-8 ready path"),
|
||||
input_path.to_str().expect("UTF-8 input path")
|
||||
);
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
state
|
||||
.lua_host
|
||||
.lua()
|
||||
|
|
@ -986,3 +986,11 @@ fn longest_rendered_prefix_separates_absent_child_text_from_a_split_render() {
|
|||
needle.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root. Re-exported rather than re-declared
|
||||
// with `#[path]` — this file already pulls in `common`, and loading one
|
||||
// source file as two modules is `clippy::duplicate_mod`.
|
||||
use common::iso;
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ fn one_frame(messages: &[InstanceMessage]) -> TerminalFrame {
|
|||
reason = "one producer-baseline lifecycle scenario"
|
||||
)]
|
||||
fn a30_first_frame_is_authoritative_then_only_real_changes_emit() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let frontend_id = FrontendId(31);
|
||||
let terminal_buffer = open_terminal(
|
||||
&mut state,
|
||||
|
|
@ -314,7 +314,7 @@ fn a30_first_frame_is_authoritative_then_only_real_changes_emit() {
|
|||
reason = "one shared-session two-frontend scenario"
|
||||
)]
|
||||
fn a31_two_semantic_frontends_share_one_session_with_independent_views() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let first_id = FrontendId(41);
|
||||
let second_id = FrontendId(42);
|
||||
let terminal_buffer = open_terminal(
|
||||
|
|
@ -459,7 +459,7 @@ fn a31_two_semantic_frontends_share_one_session_with_independent_views() {
|
|||
reason = "one case per rejected identity or bound"
|
||||
)]
|
||||
fn a32_forged_stale_and_out_of_bounds_terminal_events_change_nothing() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let owner = FrontendId(51);
|
||||
let attacker = FrontendId(52);
|
||||
let terminal_buffer = open_terminal(&mut state, "sleep 30", 6, 20);
|
||||
|
|
@ -978,7 +978,7 @@ fn terminal_mode_keeps_reporting_presence_so_peers_drop_the_stale_caret() {
|
|||
/// gesture still claims; only motion does not.
|
||||
#[test]
|
||||
fn hover_does_not_steal_terminal_control_from_the_active_frontend() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let owner = FrontendId(71);
|
||||
let bystander = FrontendId(72);
|
||||
let terminal_buffer = open_terminal(&mut state, "sleep 30", 6, 20);
|
||||
|
|
@ -1075,7 +1075,7 @@ fn hover_does_not_steal_terminal_control_from_the_active_frontend() {
|
|||
/// the empty identity buffer.
|
||||
#[test]
|
||||
fn a28_a30_a_v18_semantic_peer_has_no_terminal_surface() {
|
||||
let mut state = EditorState::new();
|
||||
let mut state = EditorState::new_with_roots(&crate::iso::roots());
|
||||
let frontend_id = FrontendId(61);
|
||||
let terminal_buffer = open_terminal(&mut state, "sleep 30", 4, 20);
|
||||
tick_until(&mut state, Duration::from_secs(5), |state| {
|
||||
|
|
@ -1341,3 +1341,11 @@ fn gpu_terminal_input_reaches_the_child_and_returns_in_a_frame() {
|
|||
report()
|
||||
);
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root. Re-exported rather than re-declared
|
||||
// with `#[path]` — this file already pulls in `common`, and loading one
|
||||
// source file as two modules is `clippy::duplicate_mod`.
|
||||
use common::iso;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ fn live_threads() -> usize {
|
|||
fn editor_state_drop_releases_workers_and_shutdown_is_idempotent() {
|
||||
let baseline = live_threads();
|
||||
for _ in 0..3 {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
drop(s);
|
||||
}
|
||||
// Signal-only shutdown: parked workers exit within their 100ms
|
||||
|
|
@ -45,7 +45,7 @@ fn editor_state_drop_releases_workers_and_shutdown_is_idempotent() {
|
|||
|
||||
// Idempotence: explicit shutdown twice, then drop runs it a
|
||||
// third time --- none may hang or panic.
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.async_runtime.shutdown_workers();
|
||||
s.async_runtime.shutdown_workers();
|
||||
drop(s);
|
||||
|
|
@ -55,8 +55,15 @@ fn editor_state_drop_releases_workers_and_shutdown_is_idempotent() {
|
|||
#[cfg(not(target_os = "linux"))]
|
||||
#[test]
|
||||
fn explicit_shutdown_is_idempotent() {
|
||||
let s = EditorState::new();
|
||||
let s = EditorState::new_with_roots(&crate::iso::roots());
|
||||
s.async_runtime.shutdown_workers();
|
||||
s.async_runtime.shutdown_workers(); // second call must not hang or panic
|
||||
drop(s); // drop runs shutdown a third time
|
||||
}
|
||||
|
||||
// Isolated bootstrap storage roots (see the module docs): an
|
||||
// integration test is compiled without `cfg(test)`, so a raw
|
||||
// `EditorState::new()` would read the developer's real `init.lua` and
|
||||
// write into their real data root.
|
||||
#[path = "common/iso.rs"]
|
||||
mod iso;
|
||||
|
|
|
|||
Loading…
Reference in New Issue