From c74cc71e265232d5a720cc87c60318f0ef834d20 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sat, 9 Dec 2023 19:05:42 +0100 Subject: [PATCH] Port rest of test_parser Most of this is already ported into the "test_parser" test. --- fish-rust/src/tests/parser.rs | 40 ++++++ src/fish_tests.cpp | 253 ---------------------------------- 2 files changed, 40 insertions(+), 253 deletions(-) diff --git a/fish-rust/src/tests/parser.rs b/fish-rust/src/tests/parser.rs index 224fedd32..4ce4dad8e 100644 --- a/fish-rust/src/tests/parser.rs +++ b/fish-rust/src/tests/parser.rs @@ -1,4 +1,5 @@ use crate::ast::{Ast, List, Node}; +use crate::expand::ExpandFlags; use crate::io::{IoBufferfill, IoChain}; use crate::parse_constants::{ParseTreeFlags, ParserTestErrorBits}; use crate::parse_util::{parse_util_detect_errors, parse_util_detect_errors_in_argument}; @@ -290,6 +291,45 @@ fn detect_argument_errors(src: &str) -> Result<(), ParserTestErrorBits> { ); }); +add_test!("test_eval_recursion_detection", || { + // Ensure that we don't crash on infinite self recursion and mutual recursion. These must use + // the principal parser because we cannot yet execute jobs on other parsers. + let parser = Parser::principal_parser().shared(); + parser.eval( + L!("function recursive ; recursive ; end ; recursive; "), + &IoChain::new(), + ); + + parser.eval( + L!(concat!( + "function recursive1 ; recursive2 ; end ; ", + "function recursive2 ; recursive1 ; end ; recursive1; ", + )), + &IoChain::new(), + ); +}); + +add_test!("test_eval_empty_function_name", || { + let parser = Parser::principal_parser().shared(); + parser.eval( + L!("function '' ; echo fail; exit 42 ; end ; ''"), + &IoChain::new(), + ); +}); + +add_test!("test_expand_argument_list", || { + let parser = Parser::principal_parser().shared(); + let comps: Vec = Parser::expand_argument_list( + L!("alpha 'beta gamma' delta"), + ExpandFlags::default(), + &parser.context(), + ) + .into_iter() + .map(|c| c.completion) + .collect(); + assert_eq!(comps, &[L!("alpha"), L!("beta gamma"), L!("delta"),]); +}); + fn test_1_cancellation(src: &wstr) { let filler = IoBufferfill::create().unwrap(); let delay = Duration::from_millis(500); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 0d2bffe61..30d287085 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -524,258 +524,6 @@ static void test_pthread() { do_test(val == 5); } -static parser_test_error_bits_t detect_argument_errors(const wcstring &src) { - using namespace ast; - auto ast = ast_parse_argument_list(src, parse_flag_none); - if (ast->errored()) { - return PARSER_TEST_ERROR; - } - const ast::argument_t *first_arg = - ast->top()->as_freestanding_argument_list().arguments().at(0); - if (!first_arg) { - err(L"Failed to parse an argument"); - return 0; - } - return parse_util_detect_errors_in_argument(*first_arg, *first_arg->source(src)); -} - -/// Test the parser. -// todo!("port this"); -static void test_parser() { - say(L"Testing parser"); - - auto detect_errors = [](const wcstring &s) { - return parse_util_detect_errors(s, nullptr, true /* accept incomplete */); - }; - - say(L"Testing block nesting"); - if (!detect_errors(L"if; end")) { - err(L"Incomplete if statement undetected"); - } - if (!detect_errors(L"if test; echo")) { - err(L"Missing end undetected"); - } - if (!detect_errors(L"if test; end; end")) { - err(L"Unbalanced end undetected"); - } - - say(L"Testing detection of invalid use of builtin commands"); - if (!detect_errors(L"case foo")) { - err(L"'case' command outside of block context undetected"); - } - if (!detect_errors(L"switch ggg; if true; case foo;end;end")) { - err(L"'case' command outside of switch block context undetected"); - } - if (!detect_errors(L"else")) { - err(L"'else' command outside of conditional block context undetected"); - } - if (!detect_errors(L"else if")) { - err(L"'else if' command outside of conditional block context undetected"); - } - if (!detect_errors(L"if false; else if; end")) { - err(L"'else if' missing command undetected"); - } - - if (!detect_errors(L"break")) { - err(L"'break' command outside of loop block context undetected"); - } - - if (detect_errors(L"break --help")) { - err(L"'break --help' incorrectly marked as error"); - } - - if (!detect_errors(L"while false ; function foo ; break ; end ; end ")) { - err(L"'break' command inside function allowed to break from loop outside it"); - } - - if (!detect_errors(L"exec ls|less") || !detect_errors(L"echo|return")) { - err(L"Invalid pipe command undetected"); - } - - if (detect_errors(L"for i in foo ; switch $i ; case blah ; break; end; end ")) { - err(L"'break' command inside switch falsely reported as error"); - } - - if (detect_errors(L"or cat | cat") || detect_errors(L"and cat | cat")) { - err(L"boolean command at beginning of pipeline falsely reported as error"); - } - - if (!detect_errors(L"cat | and cat")) { - err(L"'and' command in pipeline not reported as error"); - } - - if (!detect_errors(L"cat | or cat")) { - err(L"'or' command in pipeline not reported as error"); - } - - if (!detect_errors(L"cat | exec") || !detect_errors(L"exec | cat")) { - err(L"'exec' command in pipeline not reported as error"); - } - - if (!detect_errors(L"begin ; end arg")) { - err(L"argument to 'end' not reported as error"); - } - - if (!detect_errors(L"switch foo ; end arg")) { - err(L"argument to 'end' not reported as error"); - } - - if (!detect_errors(L"if true; else if false ; end arg")) { - err(L"argument to 'end' not reported as error"); - } - - if (!detect_errors(L"if true; else ; end arg")) { - err(L"argument to 'end' not reported as error"); - } - - if (detect_errors(L"begin ; end 2> /dev/null")) { - err(L"redirection after 'end' wrongly reported as error"); - } - - if (detect_errors(L"true | ") != PARSER_TEST_INCOMPLETE) { - err(L"unterminated pipe not reported properly"); - } - - if (detect_errors(L"echo (\nfoo\n bar") != PARSER_TEST_INCOMPLETE) { - err(L"unterminated multiline subshell not reported properly"); - } - - if (detect_errors(L"begin ; true ; end | ") != PARSER_TEST_INCOMPLETE) { - err(L"unterminated pipe not reported properly"); - } - - if (detect_errors(L" | true ") != PARSER_TEST_ERROR) { - err(L"leading pipe not reported properly"); - } - - if (detect_errors(L"true | # comment") != PARSER_TEST_INCOMPLETE) { - err(L"comment after pipe not reported as incomplete"); - } - - if (detect_errors(L"true | # comment \n false ")) { - err(L"comment and newline after pipe wrongly reported as error"); - } - - if (detect_errors(L"true | ; false ") != PARSER_TEST_ERROR) { - err(L"semicolon after pipe not detected as error"); - } - - if (detect_argument_errors(L"foo")) { - err(L"simple argument reported as error"); - } - - if (detect_argument_errors(L"''")) { - err(L"Empty string reported as error"); - } - - if (!(detect_argument_errors(L"foo$$") & PARSER_TEST_ERROR)) { - err(L"Bad variable expansion not reported as error"); - } - - if (!(detect_argument_errors(L"foo$@") & PARSER_TEST_ERROR)) { - err(L"Bad variable expansion not reported as error"); - } - - // Within command substitutions, we should be able to detect everything that - // parse_util_detect_errors can detect. - if (!(detect_argument_errors(L"foo(cat | or cat)") & PARSER_TEST_ERROR)) { - err(L"Bad command substitution not reported as error"); - } - - if (!detect_errors(L"false & ; and cat")) { - err(L"'and' command after background not reported as error"); - } - - if (!detect_errors(L"true & ; or cat")) { - err(L"'or' command after background not reported as error"); - } - - if (detect_errors(L"true & ; not cat")) { - err(L"'not' command after background falsely reported as error"); - } - - if (!detect_errors(L"if true & ; end")) { - err(L"backgrounded 'if' conditional not reported as error"); - } - - if (!detect_errors(L"if false; else if true & ; end")) { - err(L"backgrounded 'else if' conditional not reported as error"); - } - - if (!detect_errors(L"while true & ; end")) { - err(L"backgrounded 'while' conditional not reported as error"); - } - - if (!detect_errors(L"true | || false")) { - err(L"bogus boolean statement error not detected on line %d", __LINE__); - } - - if (!detect_errors(L"|| false")) { - err(L"bogus boolean statement error not detected on line %d", __LINE__); - } - - if (!detect_errors(L"&& false")) { - err(L"bogus boolean statement error not detected on line %d", __LINE__); - } - - if (!detect_errors(L"true ; && false")) { - err(L"bogus boolean statement error not detected on line %d", __LINE__); - } - - if (!detect_errors(L"true ; || false")) { - err(L"bogus boolean statement error not detected on line %d", __LINE__); - } - - if (!detect_errors(L"true || && false")) { - err(L"bogus boolean statement error not detected on line %d", __LINE__); - } - - if (!detect_errors(L"true && || false")) { - err(L"bogus boolean statement error not detected on line %d", __LINE__); - } - - if (!detect_errors(L"true && && false")) { - err(L"bogus boolean statement error not detected on line %d", __LINE__); - } - - if (detect_errors(L"true && ") != PARSER_TEST_INCOMPLETE) { - err(L"unterminated conjunction not reported properly"); - } - - if (detect_errors(L"true && \n true")) { - err(L"newline after && reported as error"); - } - - if (detect_errors(L"true || \n") != PARSER_TEST_INCOMPLETE) { - err(L"unterminated conjunction not reported properly"); - } - - say(L"Testing basic evaluation"); - - // Ensure that we don't crash on infinite self recursion and mutual recursion. These must use - // the principal parser because we cannot yet execute jobs on other parsers. - auto parser = parser_principal_parser()->deref().shared(); - say(L"Testing recursion detection"); - parser->deref().eval(L"function recursive ; recursive ; end ; recursive; ", *new_io_chain()); - - parser->deref().eval( - L"function recursive1 ; recursive2 ; end ; " - L"function recursive2 ; recursive1 ; end ; recursive1; ", - *new_io_chain()); - - say(L"Testing empty function name"); - parser->deref().eval(L"function '' ; echo fail; exit 42 ; end ; ''", *new_io_chain()); - - say(L"Testing eval_args"); - wcstring_list_ffi_t comps; - parser_expand_argument_list_ffi(L"alpha 'beta gamma' delta", expand_flags_t{}, - *parser_context(parser->deref()), comps); - do_test(comps.size() == 3); - do_test(comps.at(0) == L"alpha"); - do_test(comps.at(1) == L"beta gamma"); - do_test(comps.at(2) == L"delta"); -} - static void test_const_strlen() { do_test(const_strlen("") == 0); do_test(const_strlen(L"") == 0); @@ -2109,7 +1857,6 @@ static const test_t s_tests[]{ {TEST_GROUP("convert_nulls"), test_convert_nulls}, {TEST_GROUP("iothread"), test_iothread}, {TEST_GROUP("pthread"), test_pthread}, - {TEST_GROUP("parser"), test_parser}, {TEST_GROUP("lru"), test_lru}, {TEST_GROUP("wcstod"), test_wcstod}, {TEST_GROUP("word_motion"), test_word_motion},