extern crate conch_runtime; extern crate futures; extern crate tokio_core; use conch_runtime::io::FileDesc; use conch_runtime::spawn::{BoxSpawnEnvFuture, BoxStatusFuture, function}; use futures::future::poll_fn; use std::rc::Rc; use tokio_core::reactor::Core; #[macro_use] mod support; pub use self::support::*; type TestEnv = Env< ArgsEnv, PlatformSpecificAsyncIoEnv, FileDescEnv>, LastStatusEnv, VarEnv, ExecEnv, VirtualWorkingDirEnv, String, MockErr, >; fn new_test_env() -> (Core, TestEnv) { let lp = Core::new().expect("failed to create Core loop"); let env = Env::with_config(DefaultEnvConfig::new(lp.remote(), Some(1)) .expect("failed to create test env") .change_file_desc_env(FileDescEnv::new()) .change_var_env(VarEnv::new()) .change_fn_error::() ); (lp, env) } #[test] fn should_restore_args_after_completion() { let (mut lp, mut env) = new_test_env(); let exit = ExitStatus::Code(42); let fn_name = "fn_name".to_owned(); assert!(function(&fn_name, vec!(), &env).is_none()); env.set_function(fn_name.clone(), Rc::new(mock_status(exit))); let args = Rc::new(vec!("foo".to_owned(), "bar".to_owned())); env.set_args(args.clone()); let mut future = function(&fn_name, vec!("qux".to_owned()), &env) .expect("failed to find function"); let next = lp.run(poll_fn(|| future.poll(&mut env))).expect("env future failed"); assert_eq!(lp.run(next), Ok(exit)); assert_eq!(env.args(), &**args); } #[test] fn should_propagate_errors_and_restore_args() { let (mut lp, mut env) = new_test_env(); let fn_name = "fn_name".to_owned(); env.set_function(fn_name.clone(), Rc::new(mock_error(false))); let args = Rc::new(vec!("foo".to_owned(), "bar".to_owned())); env.set_args(args.clone()); let mut future = function(&fn_name, vec!("qux".to_owned()), &env) .expect("failed to find function"); match lp.run(poll_fn(|| future.poll(&mut env))) { Ok(_) => panic!("unexpected success"), Err(e) => assert_eq!(e, MockErr::Fatal(false)), } assert_eq!(env.args(), &**args); } #[test] fn should_propagate_cancel_and_restore_args() { let (_lp, mut env) = new_test_env(); let fn_name = "fn_name".to_owned(); env.set_function(fn_name.clone(), Rc::new(mock_must_cancel())); let args = Rc::new(vec!("foo".to_owned(), "bar".to_owned())); env.set_args(args.clone()); let future = function(&fn_name, vec!("qux".to_owned()), &env) .expect("failed to find function"); test_cancel!(future, env); assert_eq!(env.args(), &**args); } struct MockFnRecursive { callback: F, } impl MockFnRecursive { fn new(f: F) -> Rc where F: Fn(&TestEnv) -> BoxSpawnEnvFuture<'static, TestEnv, MockErr> { Rc::new(MockFnRecursive { callback: f }) } } impl<'a, F> Spawn for &'a MockFnRecursive where F: Fn(&TestEnv) -> BoxSpawnEnvFuture<'static, TestEnv, MockErr> { type EnvFuture = BoxSpawnEnvFuture<'static, TestEnv, Self::Error>; type Future = BoxStatusFuture<'static, Self::Error>; type Error = MockErr; fn spawn(self, env: &TestEnv) -> Self::EnvFuture { (self.callback)(env) } } #[test] fn test_env_run_function_nested_calls_do_not_destroy_upper_args() { let exit = ExitStatus::Code(42); let fn_name = "fn name".to_owned(); let (mut lp, mut env) = new_test_env(); let depth = { let num_calls = 3usize; let depth = Rc::new(::std::cell::Cell::new(num_calls)); let depth_copy = depth.clone(); let fn_name = fn_name.clone(); env.set_function(fn_name.clone(), MockFnRecursive::new(move |env| { let num_calls = depth.get().saturating_sub(1); depth.set(num_calls); if num_calls <= 0 { Box::new(Rc::new(mock_status(exit)).spawn(env)) } else { let cur_args: Vec<_> = env.args().iter().cloned().collect(); let mut next_args = cur_args.clone(); next_args.reverse(); next_args.push(format!("arg{}", num_calls)); Box::new(function(&fn_name, next_args, env) .expect("failed to find function")) } })); depth_copy }; let args = Rc::new(vec!("foo".to_owned(), "bar".to_owned())); env.set_args(args.clone()); let mut future = function(&fn_name, vec!("qux".to_owned()), &env) .expect("failed to find function"); let next = lp.run(poll_fn(|| future.poll(&mut env))).expect("env future failed"); assert_eq!(lp.run(next), Ok(exit)); assert_eq!(env.args(), &**args); assert_eq!(depth.get(), 0); }