conch-runtime 0.1.2

A library for evaluating/executing programs written in the shell programming language.
Documentation
#![cfg(feature = "conch-parser")]

extern crate conch_parser;
extern crate conch_runtime;
extern crate tokio_core;

use conch_parser::ast::Parameter::*;
use conch_runtime::ExitStatus;
use conch_runtime::env::{ArgsEnv, ArgumentsEnvironment, Env, EnvConfig,
                         LastStatusEnvironment, VariableEnvironment};
use conch_runtime::eval::{Fields, ParamEval};
use tokio_core::reactor::Core;

#[test]
fn test_eval_parameter_with_set_vars() {
    use conch_runtime::io::getpid;

    let var1 = "var1_value".to_owned();
    let var2 = "var2_value".to_owned();
    let var3 = "".to_owned(); // var3 is set to the empty string

    let arg1 = "arg1_value".to_owned();
    let arg2 = "arg2_value".to_owned();
    let arg3 = "arg3_value".to_owned();

    let args = vec!(
        arg1.clone(),
        arg2.clone(),
        arg3.clone(),
    );

    let lp = Core::new().unwrap();
    let mut env = Env::with_config(EnvConfig {
        args_env: ArgsEnv::with_name_and_args("shell name".to_owned(), args.clone()),
        .. EnvConfig::new(lp.remote(), Some(1)).expect("failed to create env")
    });

    env.set_var("var1".to_owned(), var1.clone());
    env.set_var("var2".to_owned(), var2.clone());
    env.set_var("var3".to_owned(), var3.clone());

    assert_eq!(At.eval(false, &env), Some(Fields::At(args.clone())));
    assert_eq!(Star.eval(false, &env), Some(Fields::Star(args.clone())));

    assert_eq!(Dollar.eval(false, &env), Some(Fields::Single(getpid().to_string())));

    // FIXME: test these
    //assert_eq!(Dash.eval(false, &env), ...);
    //assert_eq!(Bang.eval(false, &env), ...);

    // Before anything is run it should be considered a success
    assert_eq!(Question.eval(false, &env), Some(Fields::Single("0".to_owned())));
    env.set_last_status(ExitStatus::Code(3));
    assert_eq!(Question.eval(false, &env), Some(Fields::Single("3".to_owned())));
    // Signals should have 128 added to them
    env.set_last_status(ExitStatus::Signal(5));
    assert_eq!(Question.eval(false, &env), Some(Fields::Single("133".to_owned())));

    assert_eq!(Positional(0).eval(false, &env), Some(Fields::Single(env.name().clone())));
    assert_eq!(Positional(1).eval(false, &env), Some(Fields::Single(arg1)));
    assert_eq!(Positional(2).eval(false, &env), Some(Fields::Single(arg2)));
    assert_eq!(Positional(3).eval(false, &env), Some(Fields::Single(arg3)));

    assert_eq!(Var("var1".to_owned()).eval(false, &env), Some(Fields::Single(var1)));
    assert_eq!(Var("var2".to_owned()).eval(false, &env), Some(Fields::Single(var2)));
    assert_eq!(Var("var3".to_owned()).eval(false, &env), Some(Fields::Single(var3)));

    assert_eq!(Pound.eval(false, &env), Some(Fields::Single("3".to_owned())));
}

#[test]
fn test_eval_parameter_with_unset_vars() {
    let lp = Core::new().unwrap();
    let env = Env::new(lp.remote(), Some(1)).expect("failed to create env");

    assert_eq!(At.eval(false, &env), Some(Fields::Zero));
    assert_eq!(Star.eval(false, &env), Some(Fields::Zero));

    // FIXME: test these
    //assert_eq!(Dash.eval(false, &env), ...);
    //assert_eq!(Bang.eval(false, &env), ...);

    assert_eq!(Pound.eval(false, &env), Some(Fields::Single("0".to_owned())));

    assert_eq!(Positional(0).eval(false, &env), Some(Fields::Single(env.name().clone())));
    assert_eq!(Positional(1).eval(false, &env), None);
    assert_eq!(Positional(2).eval(false, &env), None);

    assert_eq!(Var("var1".to_owned()).eval(false, &env), None);
    assert_eq!(Var("var2".to_owned()).eval(false, &env), None);
}

#[test]
fn test_eval_parameter_splitting_with_default_ifs() {
    let val1 = " \t\nfoo\n\n\nbar \t\n".to_owned();
    let val2 = "".to_owned();

    let args = vec!(
        val1.clone(),
        val2.clone(),
    );

    let lp = Core::new().unwrap();
    let mut env = Env::with_config(EnvConfig {
        args_env: ArgsEnv::with_name_and_args("shell name".to_owned(), args.clone()),
        .. EnvConfig::new(lp.remote(), Some(1)).expect("failed to create env")
    });

    env.set_var("var1".to_owned(), val1.clone());
    env.set_var("var2".to_owned(), val2.clone());

    // Splitting should NOT keep any IFS whitespace fields
    let fields_args = vec!("foo".to_owned(), "bar".to_owned());

    // With splitting
    assert_eq!(At.eval(true, &env), Some(Fields::At(fields_args.clone())));
    assert_eq!(Star.eval(true, &env), Some(Fields::Star(fields_args.clone())));

    let fields_foo_bar = Fields::Split(fields_args.clone());

    assert_eq!(Positional(1).eval(true, &env), Some(fields_foo_bar.clone()));
    assert_eq!(Positional(2).eval(true, &env), Some(Fields::Zero));

    assert_eq!(Var("var1".to_owned()).eval(true, &env), Some(fields_foo_bar.clone()));
    assert_eq!(Var("var2".to_owned()).eval(true, &env), Some(Fields::Zero));

    // Without splitting
    assert_eq!(At.eval(false, &env), Some(Fields::At(args.clone())));
    assert_eq!(Star.eval(false, &env), Some(Fields::Star(args.clone())));

    assert_eq!(Positional(1).eval(false, &env), Some(Fields::Single(val1.clone())));
    assert_eq!(Positional(2).eval(false, &env), Some(Fields::Single(val2.clone())));

    assert_eq!(Var("var1".to_owned()).eval(false, &env), Some(Fields::Single(val1)));
    assert_eq!(Var("var2".to_owned()).eval(false, &env), Some(Fields::Single(val2)));
}

#[test]
fn test_eval_parameter_splitting_with_custom_ifs() {
    let val1 = "   foo000bar   ".to_owned();
    let val2 = "  00 0 00  0 ".to_owned();
    let val3 = "".to_owned();

    let args = vec!(
        val1.clone(),
        val2.clone(),
        val3.clone(),
    );

    let lp = Core::new().unwrap();
    let mut env = Env::with_config(EnvConfig {
        args_env: ArgsEnv::with_name_and_args("shell name".to_owned(), args.clone()),
        .. EnvConfig::new(lp.remote(), Some(1)).expect("failed to create env")
    });

    env.set_var("IFS".to_owned(), "0 ".to_owned());

    env.set_var("var1".to_owned(), val1.clone());
    env.set_var("var2".to_owned(), val2.clone());
    env.set_var("var3".to_owned(), val3.clone());

    // Splitting SHOULD keep empty fields between IFS chars which are NOT whitespace
    let fields_args = vec!(
        "foo".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "bar".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "".to_owned(),
        // Already empty word should result in Zero fields
    );

    // With splitting
    assert_eq!(At.eval(true, &env), Some(Fields::At(fields_args.clone())));
    assert_eq!(Star.eval(true, &env), Some(Fields::Star(fields_args.clone())));

    let fields_foo_bar = Fields::Split(vec!(
        "foo".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "bar".to_owned(),
    ));

    let fields_all_blanks = Fields::Split(vec!(
        "".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "".to_owned(),
        "".to_owned(),
    ));

    assert_eq!(Positional(1).eval(true, &env), Some(fields_foo_bar.clone()));
    assert_eq!(Positional(2).eval(true, &env), Some(fields_all_blanks.clone()));
    assert_eq!(Positional(3).eval(true, &env), Some(Fields::Zero));

    assert_eq!(Var("var1".to_owned()).eval(true, &env), Some(fields_foo_bar));
    assert_eq!(Var("var2".to_owned()).eval(true, &env), Some(fields_all_blanks));
    assert_eq!(Var("var3".to_owned()).eval(true, &env), Some(Fields::Zero));

    // FIXME: test these
    //assert_eq!(Dash.eval(false, &env), ...);
    //assert_eq!(Bang.eval(false, &env), ...);

    assert_eq!(Question.eval(true, &env), Some(Fields::Single("".to_owned())));

    // Without splitting
    assert_eq!(At.eval(false, &env), Some(Fields::At(args.clone())));
    assert_eq!(Star.eval(false, &env), Some(Fields::Star(args.clone())));

    assert_eq!(Positional(1).eval(false, &env), Some(Fields::Single(val1.clone())));
    assert_eq!(Positional(2).eval(false, &env), Some(Fields::Single(val2.clone())));
    assert_eq!(Positional(3).eval(false, &env), Some(Fields::Single(val3.clone())));

    assert_eq!(Var("var1".to_owned()).eval(false, &env), Some(Fields::Single(val1)));
    assert_eq!(Var("var2".to_owned()).eval(false, &env), Some(Fields::Single(val2)));
    assert_eq!(Var("var3".to_owned()).eval(false, &env), Some(Fields::Single(val3)));

    // FIXME: test these
    //assert_eq!(Dash.eval(false, &env), ...);
    //assert_eq!(Bang.eval(false, &env), ...);

    assert_eq!(Question.eval(false, &env), Some(Fields::Single("0".to_owned())));
}

#[test]
fn test_eval_parameter_splitting_with_empty_ifs() {
    let val1 = " \t\nfoo\n\n\nbar \t\n".to_owned();
    let val2 = "".to_owned();

    let args = vec!(
        val1.clone(),
        val2.clone(),
    );

    let lp = Core::new().unwrap();
    let mut env = Env::with_config(EnvConfig {
        args_env: ArgsEnv::with_name_and_args("shell name".to_owned(), args.clone()),
        .. EnvConfig::new(lp.remote(), Some(1)).expect("failed to create env")
    });

    env.set_var("IFS".to_owned(), "".to_owned());
    env.set_var("var1".to_owned(), val1.clone());
    env.set_var("var2".to_owned(), val2.clone());

    // Splitting with empty IFS should keep fields as they are
    let field_args = args;
    let field1 = Fields::Single(val1);
    let field2 = Fields::Single(val2);

    // With splitting
    assert_eq!(At.eval(true, &env), Some(Fields::At(field_args.clone())));
    assert_eq!(Star.eval(true, &env), Some(Fields::Star(field_args.clone())));

    assert_eq!(Positional(1).eval(true, &env), Some(field1.clone()));
    assert_eq!(Positional(2).eval(true, &env), Some(field2.clone()));

    assert_eq!(Var("var1".to_owned()).eval(true, &env), Some(field1.clone()));
    assert_eq!(Var("var2".to_owned()).eval(true, &env), Some(field2.clone()));

    // Without splitting
    assert_eq!(At.eval(false, &env), Some(Fields::At(field_args.clone())));
    assert_eq!(Star.eval(false, &env), Some(Fields::Star(field_args.clone())));

    assert_eq!(Positional(1).eval(false, &env), Some(field1.clone()));
    assert_eq!(Positional(2).eval(false, &env), Some(field2.clone()));

    assert_eq!(Var("var1".to_owned()).eval(false, &env), Some(field1.clone()));
    assert_eq!(Var("var2".to_owned()).eval(false, &env), Some(field2.clone()));
}