extern crate conch_runtime; extern crate futures; use conch_runtime::RefCounted; use conch_runtime::spawn::{for_args, for_loop, for_with_args}; use futures::future::{FutureResult, ok}; use std::rc::Rc; #[macro_use] mod support; pub use self::support::*; macro_rules! run_env { ($future:expr, $env:expr, $lp:expr) => {{ $lp.run($future.pin_env($env.sub_env()).flatten()) }} } const MOCK_EXIT: ExitStatus = ExitStatus::Code(42); const VAR: &str = "var name"; const RESULT_VAR: &str = "resulting var name"; #[derive(Debug, Clone)] struct MockCmd2; impl<'a> Spawn for &'a MockCmd2 { type Error = MockErr; type EnvFuture = Self; type Future = FutureResult; fn spawn(self, _: &DefaultEnvRc) -> Self::EnvFuture { self } } impl<'a> EnvFuture for &'a MockCmd2 { type Item = FutureResult; type Error = MockErr; fn poll(&mut self, env: &mut DefaultEnvRc) -> Poll { let result_var = RESULT_VAR.to_owned(); let mut result_val = env.var(&result_var) .cloned() .unwrap_or_else(|| Rc::new(String::new())); if let Some(val) = env.var(&VAR.to_owned()) { result_val.make_mut().push_str(&**val); } env.set_var(Rc::new(result_var), result_val); Ok(Async::Ready(ok(MOCK_EXIT))) } fn cancel(&mut self, _env: &mut DefaultEnvRc) { unimplemented!() } } #[test] fn should_run_with_appropriate_args() { let (mut lp, mut env) = new_env(); env.set_args(Rc::new(vec!( Rc::new("arg_foo".to_owned()), Rc::new("arg_bar".to_owned()), ))); let result_var = Rc::new(RESULT_VAR.to_owned()); let name = Rc::new(VAR.to_owned()); let vars_raw = vec!("raw_foo".to_owned(), "raw_bar".to_owned()); let vars = mock_word_fields(Fields::Split(vars_raw.clone())); let cmd = MockCmd2; macro_rules! run_env_and_assert_var { ($future:expr, $env:expr, $value:expr) => {{ let mut env = $env; env.unset_var(&result_var); let mut future = $future; let mut next_future = future.poll(&mut env); while let Ok(Async::NotReady) = next_future { next_future = future.poll(&mut env); } let next_future = match next_future.expect("did not resolve successfully") { Async::Ready(n) => n, Async::NotReady => unreachable!(), }; let ret = lp.run(next_future); assert_eq!(ret, Ok(MOCK_EXIT)); assert_eq!(&**env.var(&result_var).unwrap(), $value); }} } { let env = env.sub_env(); let for_cmd = for_loop( name.clone(), Some(vec!(vars.clone())), vec!(&cmd), &env ); run_env_and_assert_var!(for_cmd, env, "raw_fooraw_bar"); } { let env = env.sub_env(); let no_word: Option> = None; let for_cmd = for_loop( name.clone(), no_word, vec!(&cmd), &env ); run_env_and_assert_var!(for_cmd, env, "arg_fooarg_bar"); } { let env = env.sub_env(); let for_cmd = for_args(name.clone(), vec!(&cmd), &env); run_env_and_assert_var!(for_cmd, env, "arg_fooarg_bar"); } { let vars_raw = vars_raw.into_iter().map(Rc::new); let for_cmd = for_with_args(name.clone(), vars_raw, vec!(&cmd)); run_env_and_assert_var!(for_cmd, env, "raw_fooraw_bar"); } } #[test] fn should_swallow_non_fatal_errors_in_body() { let (mut lp, mut env) = new_env(); env.set_args(Rc::new(vec!( Rc::new("arg_foo".to_owned()), Rc::new("arg_bar".to_owned()), ))); let name = Rc::new("name".to_owned()); let vars = mock_word_fields(Fields::Single((*name).clone())); let non_fatal = mock_error(false); let cmd = mock_status(MOCK_EXIT); let for_cmd = for_loop( name.clone(), Some(vec!(vars.clone())), vec!(&non_fatal, &cmd), &env ); assert_eq!(run_env!(for_cmd, env, lp), Ok(MOCK_EXIT)); let no_word: Option> = None; let for_cmd = for_loop( name.clone(), no_word, vec!(&non_fatal, &cmd), &env ); assert_eq!(run_env!(for_cmd, env, lp), Ok(MOCK_EXIT)); let for_cmd = for_args(name.clone(), vec!(&non_fatal, &cmd), &env); assert_eq!(run_env!(for_cmd, env, lp), Ok(MOCK_EXIT)); let for_cmd = for_with_args(name.clone(), vec!(name.clone()), vec!(&non_fatal, &cmd)); assert_eq!(run_env!(for_cmd, env, lp), Ok(MOCK_EXIT)); } #[test] fn should_not_run_body_args_are_empty() { let (mut lp, mut env) = new_env(); env.set_args(Rc::new(vec!())); let should_not_run = mock_panic("must not run"); let name = Rc::new("name".to_owned()); let vars = mock_word_fields(Fields::Zero); let for_cmd = for_loop( name.clone(), Some(vec!(vars.clone())), vec!(&should_not_run), &env ); assert_eq!(run_env!(for_cmd, env, lp), Ok(EXIT_SUCCESS)); let no_word: Option> = None; let for_cmd = for_loop( name.clone(), no_word, vec!(&should_not_run), &env ); assert_eq!(run_env!(for_cmd, env, lp), Ok(EXIT_SUCCESS)); let for_cmd = for_args(name.clone(), vec!(&should_not_run), &env); assert_eq!(run_env!(for_cmd, env, lp), Ok(EXIT_SUCCESS)); let for_cmd = for_with_args(name.clone(), vec!(), vec!(&should_not_run)); assert_eq!(run_env!(for_cmd, env, lp), Ok(EXIT_SUCCESS)); } #[test] fn should_propagate_all_word_errors() { let (mut lp, env) = new_env(); let should_not_run = mock_panic("must not run"); let name = Rc::new("name".to_owned()); let for_cmd = for_loop( name.clone(), Some(vec!(mock_word_error(true))), vec!(&should_not_run), &env ); assert_eq!(run_env!(for_cmd, env, lp), Err(MockErr::Fatal(true))); let for_cmd = for_loop( name.clone(), Some(vec!(mock_word_error(false))), vec!(&should_not_run), &env ); assert_eq!(run_env!(for_cmd, env, lp), Err(MockErr::Fatal(false))); } #[test] fn should_propagate_fatal_errors_in_body() { let (mut lp, mut env) = new_env(); env.set_args(Rc::new(vec!( Rc::new("foo".to_owned()), Rc::new("bar".to_owned()), ))); let name = Rc::new("name".to_owned()); let vars_raw = vec!("foo".to_owned(), "bar".to_owned()); let vars = mock_word_fields(Fields::Split(vars_raw.clone())); let fatal = mock_error(true); let for_cmd = for_loop( name.clone(), Some(vec!(vars.clone())), vec!(&fatal), &env ); assert_eq!(run_env!(for_cmd, env, lp), Err(MockErr::Fatal(true))); let no_word: Option> = None; let for_cmd = for_loop( name.clone(), no_word, vec!(&fatal), &env ); assert_eq!(run_env!(for_cmd, env, lp), Err(MockErr::Fatal(true))); let for_cmd = for_args(name.clone(), vec!(&fatal), &env); assert_eq!(run_env!(for_cmd, env, lp), Err(MockErr::Fatal(true))); let vars_raw = vars_raw.into_iter().map(Rc::new); let for_cmd = for_with_args(name.clone(), vars_raw, vec!(&fatal)); assert_eq!(run_env!(for_cmd, env, lp), Err(MockErr::Fatal(true))); } #[test] fn should_propagate_cancel() { let (_lp, mut env) = new_env(); env.set_args(Rc::new(vec!( Rc::new("foo".to_owned()), Rc::new("bar".to_owned()), ))); let name = Rc::new("name".to_owned()); let vars_raw = vec!("foo".to_owned(), "bar".to_owned()); let vars = mock_word_fields(Fields::Split(vars_raw.clone())); let should_not_run = mock_panic("must not run"); let must_cancel = mock_must_cancel(); let for_cmd = for_loop( name.clone(), Some(vec!(mock_word_must_cancel())), vec!(&should_not_run), &env ); test_cancel!(for_cmd, env); let for_cmd = for_loop( name.clone(), Some(vec!(vars.clone())), vec!(&must_cancel), &env ); test_cancel!(for_cmd, env); let for_cmd = for_args(name.clone(), vec!(&must_cancel), &env); test_cancel!(for_cmd, env); let vars_raw = vars_raw.into_iter().map(Rc::new); let for_cmd = for_with_args(name.clone(), vars_raw, vec!(&must_cancel)); test_cancel!(for_cmd, env); }