mxsh 0.2.0

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

use mxsh::embed::{DiagnosticCategory, StdioConfig, TraceEvent};
use mxsh::runtime::testing::{InMemoryRuntime, StringStdioOut};
use mxsh::{Shell, ShellBuilder};

#[test]
fn cli_outcome_captures_diagnostics_and_trace_for_noninteractive_runs() {
    let stdout = StringStdioOut::new();
    let stderr = StringStdioOut::new();
    let argv = vec![
        "mxsh".to_string(),
        "-c".to_string(),
        "definitely_missing_command".to_string(),
    ];
    let mut shell = ShellBuilder::new()
        .stdio(StdioConfig {
            stdout: stdout.fd(),
            stderr: stderr.fd(),
            ..StdioConfig::default()
        })
        .build(InMemoryRuntime::new())
        .expect("shell should build");
    let outcome = shell.run_cli(&argv);

    assert_eq!(outcome.status, 127);
    assert_eq!(outcome.exit_code, None);
    assert!(
        outcome.trace.len() >= 2,
        "expected trace events in {outcome:?}"
    );
    assert!(matches!(
        outcome.trace.first(),
        Some(TraceEvent::RunStarted { .. })
    ));
    assert!(matches!(
        outcome.trace.last(),
        Some(TraceEvent::RunFinished { status: 127, .. })
    ));
    assert!(
        outcome
            .diagnostics
            .iter()
            .any(|diagnostic| diagnostic.category == DiagnosticCategory::CommandLookup)
    );
    assert_eq!(stdout.collect(), "");
    assert!(
        stderr
            .collect()
            .contains("definitely_missing_command: command not found")
    );
}

#[test]
fn cli_arg_errors_return_empty_outcome() {
    let argv = vec!["mxsh".to_string(), "--definitely-invalid".to_string()];
    let mut shell = Shell::new(InMemoryRuntime::new());
    let outcome = shell.run_cli(&argv);

    assert_eq!(outcome.status, 1);
    assert_eq!(outcome.exit_code, None);
    assert!(outcome.diagnostics.is_empty());
    assert!(outcome.trace.is_empty());
}