mxsh 0.1.0

Embeddable POSIX-style shell parser and runtime
Documentation
#![cfg(all(feature = "embed", feature = "frontend", feature = "test-support"))]

use std::fs;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};

use mxsh::ShellBuilder;
use mxsh::embed::StdioConfig;
use mxsh::policy::{StartupPolicy, VariableAttributes};
use mxsh::runtime::testing::{InMemoryRuntime, StringStdioIn, StringStdioOut};

fn temp_path(label: &str) -> PathBuf {
    static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
    std::env::temp_dir().join(format!(
        "mxsh-{label}-{}-{}",
        std::process::id(),
        NEXT_ID.fetch_add(1, Ordering::Relaxed)
    ))
}

#[test]
fn explicit_none_startup_policy_disables_cli_env_hook_defaults() {
    let input = StringStdioIn::new("helper\nexit\n");
    let stdout = StringStdioOut::new();
    let stderr = StringStdioOut::new();
    let env_path = temp_path("cli-explicit-startup-policy");
    fs::write(&env_path, "helper() { echo from_env; }\n").expect("write ENV helper");

    let argv = vec!["mxsh".to_string()];
    let mut shell = ShellBuilder::new()
        .interactive(true)
        .startup_policy(StartupPolicy::None)
        .clear_inherited_env()
        .env(
            "ENV",
            env_path.display().to_string(),
            VariableAttributes::empty(),
        )
        .env("PS1", "", VariableAttributes::empty())
        .env("PS2", "", VariableAttributes::empty())
        .stdio(StdioConfig {
            stdin: input.fd(),
            stdout: stdout.fd(),
            stderr: stderr.fd(),
        })
        .build(InMemoryRuntime::new())
        .expect("shell should build");
    let outcome = shell.run_cli(&argv);

    input.join();
    let _ = fs::remove_file(&env_path);

    assert_eq!(outcome.status, 127);
    assert_eq!(outcome.exit_code, Some(127));
    assert_eq!(stdout.collect(), "");
    assert!(stderr.collect().contains("helper: command not found"));
}