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 mxsh::ShellBuilder;
use mxsh::embed::StdioConfig;
use mxsh::policy::{StartupPolicy, VariableAttributes};
use mxsh::runtime::testing::{InMemoryRuntime, StringStdioIn, StringStdioOut};

mod support;

#[test]
fn interactive_shell_can_use_custom_startup_env_var() {
    let env_path = support::temp_path("custom-startup-env");
    fs::write(&env_path, "helper() { echo from_custom_env; }\n").expect("write startup env file");
    let input = StringStdioIn::new("helper\nexit\n");
    let stdout = StringStdioOut::new();
    let stderr = StringStdioOut::new();
    let mut shell = ShellBuilder::new()
        .interactive(true)
        .startup_policy(StartupPolicy::InteractiveEnvHook)
        .startup_env_var("TOYSH_ENV")
        .clear_inherited_env()
        .env(
            "TOYSH_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(),
        })
        .new_session()
        .expect("session should build");
    let mut runtime = InMemoryRuntime::new();

    assert_eq!(shell.startup_sources().env_file_var(), Some("TOYSH_ENV"));
    shell.initialize(&mut runtime);
    let result = shell
        .run_interactive(&mut runtime)
        .expect("interactive shell should succeed");

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

    assert_eq!(result.exit_code, Some(0));
    assert!(stdout.collect().contains("from_custom_env\n"));
    assert_eq!(stderr.collect(), "");
}

#[test]
fn session_can_use_custom_user_profile_path() {
    let home_dir = support::temp_path("custom-home");
    fs::create_dir_all(&home_dir).expect("create temp home");
    let profile_path = home_dir.join(".toysh_profile");
    fs::write(&profile_path, "GREETING=from_profile\n").expect("write startup profile");
    let stdout = StringStdioOut::new();
    let mut shell = ShellBuilder::new()
        .startup_policy(StartupPolicy::PosixLoginFiles)
        .disable_system_profile()
        .user_profile_path(".toysh_profile")
        .clear_inherited_env()
        .env(
            "HOME",
            home_dir.display().to_string(),
            VariableAttributes::empty(),
        )
        .stdio(StdioConfig {
            stdout: stdout.fd(),
            ..StdioConfig::default()
        })
        .new_session()
        .expect("session should build");
    let mut runtime = InMemoryRuntime::new();

    shell.initialize(&mut runtime);
    let result = shell.run(&mut runtime, "echo $GREETING");

    let _ = fs::remove_file(&profile_path);
    let _ = fs::remove_dir(&home_dir);

    assert_eq!(result.status, 0);
    assert_eq!(stdout.collect(), "from_profile\n");
}

#[test]
fn cli_can_use_custom_startup_env_var_when_interactive_is_profile_defined() {
    let env_path = support::temp_path("custom-startup-env-cli");
    fs::write(&env_path, "helper() { echo from_cli_env; }\n").expect("write startup env file");
    let input = StringStdioIn::new("helper\nexit\n");
    let stdout = StringStdioOut::new();
    let stderr = StringStdioOut::new();
    let argv = vec!["toysh".to_string()];
    let mut shell = ShellBuilder::new()
        .interactive(true)
        .startup_policy(StartupPolicy::InteractiveEnvHook)
        .startup_env_var("TOYSH_ENV")
        .clear_inherited_env()
        .env(
            "TOYSH_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.exit_code, Some(0));
    assert!(stdout.collect().contains("from_cli_env\n"));
    assert_eq!(stderr.collect(), "");
}