extern crate conch_parser; use conch_parser::ast::*; use conch_parser::ast::ComplexWord::*; use conch_parser::ast::PipeableCommand::*; use conch_parser::ast::SimpleWord::*; use conch_parser::parse::ParseError::*; use conch_parser::token::Token; mod parse_support; use parse_support::*; fn simple_command_with_redirect(cmd: &str, redirect: DefaultRedirect) -> DefaultPipeableCommand { Simple(Box::new(SimpleCommand { redirects_or_env_vars: vec!(), redirects_or_cmd_words: vec!( RedirectOrCmdWord::CmdWord(word(cmd)), RedirectOrCmdWord::Redirect(redirect), ), })) } #[test] fn test_redirect_valid_close_without_whitespace() { let mut p = make_parser(">&-"); assert_eq!(Some(Ok(Redirect::DupWrite(None, word("-")))), p.redirect().unwrap()); } #[test] fn test_redirect_valid_close_with_whitespace() { let mut p = make_parser("<& -"); assert_eq!(Some(Ok(Redirect::DupRead(None, word("-")))), p.redirect().unwrap()); } #[test] fn test_redirect_valid_start_with_dash_if_not_dup() { let path = word("-test"); let cases = vec!( ("4<-test", Redirect::Read(Some(4), path.clone())), ("4>-test", Redirect::Write(Some(4), path.clone())), ("4<>-test", Redirect::ReadWrite(Some(4), path.clone())), ("4>>-test", Redirect::Append(Some(4), path.clone())), ("4>|-test", Redirect::Clobber(Some(4), path.clone())), ); for (s, correct) in cases.into_iter() { match make_parser(s).redirect() { Ok(Some(Ok(ref r))) if *r == correct => {}, Ok(r) => { panic!("Unexpectedly parsed the source \"{}\" as\n{:?} instead of\n{:?}", s, r, correct) }, Err(err) => panic!("Failed to parse the source \"{}\": {}", s, err), } } } #[test] fn test_redirect_valid_return_word_if_no_redirect() { let mut p = make_parser("hello"); assert_eq!(Some(Err(word("hello"))), p.redirect().unwrap()); } #[test] fn test_redirect_valid_return_word_if_src_fd_is_definitely_non_numeric() { let mut p = make_parser("123$$'foo'>out"); let correct = TopLevelWord(Concat(vec!( lit("123"), Word::Simple(Param(Parameter::Dollar)), Word::SingleQuoted(String::from("foo")), ))); assert_eq!(Some(Err(correct)), p.redirect().unwrap()); } #[test] fn test_redirect_valid_return_word_if_src_fd_has_escaped_numerics() { let mut p = make_parser("\\2>"); let correct = word_escaped("2"); assert_eq!(Some(Err(correct)), p.redirect().unwrap()); } #[test] fn test_redirect_valid_dst_fd_can_have_escaped_numerics() { let mut p = make_parser(">\\2"); let correct = Redirect::Write(None, word_escaped("2")); assert_eq!(Some(Ok(correct)), p.redirect().unwrap()); } #[test] fn test_redirect_invalid_dup_if_dst_fd_is_definitely_non_numeric() { assert_eq!(Err(BadFd(src(2, 1, 3), src(12, 1, 13))), make_parser(">&123$$'foo'").redirect()); } #[test] fn test_redirect_valid_dup_return_redirect_if_dst_fd_is_possibly_numeric() { let mut p = make_parser(">&123$$$(echo 2)`echo bar`"); let correct = Redirect::DupWrite( None, TopLevelWord(Concat(vec!( lit("123"), Word::Simple(Param(Parameter::Dollar)), subst(ParameterSubstitution::Command(vec!(cmd_args("echo", &["2"])))), subst(ParameterSubstitution::Command(vec!(cmd_args("echo", &["bar"])))), ))), ); assert_eq!(Some(Ok(correct)), p.redirect().unwrap()); } #[test] fn test_redirect_invalid_close_without_whitespace() { assert_eq!(Err(BadFd(src(2, 1, 3), src(7, 1, 8))), make_parser(">&-asdf").redirect()); } #[test] fn test_redirect_invalid_close_with_whitespace() { assert_eq!(Err(BadFd(src(9, 1, 10), src(14, 1, 15))), make_parser("<& -asdf").redirect()); } #[test] fn test_redirect_fd_immediately_preceeding_redirection() { let mut p = make_parser("foo 1>>out"); let cmd = p.simple_command().unwrap(); assert_eq!(cmd, simple_command_with_redirect("foo", Redirect::Append(Some(1), word("out")))); } #[test] fn test_redirect_fd_must_immediately_preceed_redirection() { let correct = Simple(Box::new(SimpleCommand { redirects_or_env_vars: vec!(), redirects_or_cmd_words: vec!( RedirectOrCmdWord::CmdWord(word("foo")), RedirectOrCmdWord::CmdWord(word("1")), RedirectOrCmdWord::Redirect(Redirect::ReadWrite(None, word("out"))), ), })); let mut p = make_parser("foo 1 <>out"); assert_eq!(p.simple_command().unwrap(), correct); } #[test] fn test_redirect_valid_dup_with_fd() { let mut p = make_parser("foo 1>&2"); let cmd = p.simple_command().unwrap(); assert_eq!(cmd, simple_command_with_redirect("foo", Redirect::DupWrite(Some(1), word("2")))); } #[test] fn test_redirect_valid_dup_without_fd() { let correct = Simple(Box::new(SimpleCommand { redirects_or_env_vars: vec!(), redirects_or_cmd_words: vec!( RedirectOrCmdWord::CmdWord(word("foo")), RedirectOrCmdWord::CmdWord(word("1")), RedirectOrCmdWord::Redirect(Redirect::DupRead(None, word("2"))), ), })); let mut p = make_parser("foo 1 <&2"); assert_eq!(p.simple_command().unwrap(), correct); } #[test] fn test_redirect_valid_dup_with_whitespace() { let mut p = make_parser("foo 1<& 2"); let cmd = p.simple_command().unwrap(); assert_eq!(cmd, simple_command_with_redirect("foo", Redirect::DupRead(Some(1), word("2")))); } #[test] fn test_redirect_valid_single_quoted_dup_fd() { let correct = Redirect::DupWrite(Some(1), single_quoted("2")); assert_eq!(Some(Ok(correct)), make_parser("1>&'2'").redirect().unwrap()); } #[test] fn test_redirect_valid_double_quoted_dup_fd() { let correct = Redirect::DupWrite(None, double_quoted("2")); assert_eq!(Some(Ok(correct)), make_parser(">&\"2\"").redirect().unwrap()); } #[test] fn test_redirect_src_fd_need_not_be_single_token() { let mut p = make_parser_from_tokens(vec!( Token::Literal(String::from("foo")), Token::Whitespace(String::from(" ")), Token::Literal(String::from("12")), Token::Literal(String::from("34")), Token::LessAnd, Token::Dash, )); let cmd = p.simple_command().unwrap(); assert_eq!(cmd, simple_command_with_redirect("foo", Redirect::DupRead(Some(1234), word("-")))); } #[test] fn test_redirect_dst_fd_need_not_be_single_token() { let mut p = make_parser_from_tokens(vec!( Token::GreatAnd, Token::Literal(String::from("12")), Token::Literal(String::from("34")), )); let correct = Redirect::DupWrite(None, word("1234")); assert_eq!(Some(Ok(correct)), p.redirect().unwrap()); } #[test] fn test_redirect_accept_literal_and_name_tokens() { let mut p = make_parser_from_tokens(vec!( Token::GreatAnd, Token::Literal(String::from("12")), )); assert_eq!(Some(Ok(Redirect::DupWrite(None, word("12")))), p.redirect().unwrap()); let mut p = make_parser_from_tokens(vec!( Token::GreatAnd, Token::Name(String::from("12")), )); assert_eq!(Some(Ok(Redirect::DupWrite(None, word("12")))), p.redirect().unwrap()); } #[test] fn test_redirect_list_valid() { let mut p = make_parser("1>>out <& 2 2>&-"); let io = p.redirect_list().unwrap(); assert_eq!(io, vec!( Redirect::Append(Some(1), word("out")), Redirect::DupRead(None, word("2")), Redirect::DupWrite(Some(2), word("-")), )); } #[test] fn test_redirect_list_rejects_non_fd_words() { assert_eq!(Err(BadFd(src(16, 1, 17), src(19, 1, 20))), make_parser("1>>out &- abc").redirect_list()); assert_eq!(Err(BadFd(src(7, 1, 8), src(10, 1, 11))), make_parser("1>>out abc&-").redirect_list()); assert_eq!(Err(BadFd(src(7, 1, 8), src(10, 1, 11))), make_parser("1>>out abc &-").redirect_list()); }