#[cfg(test)]
mod tests {
use super::super::*;
use crate::bash_parser::parser_arith::ArithToken;
#[test]
fn test_parse_comment() {
let input = "# This is a comment\necho hello";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(ast
.statements
.iter()
.any(|s| matches!(s, BashStmt::Comment { .. })));
}
#[test]
fn test_parse_shebang() {
let input = "#!/bin/bash\necho hello";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_parse_here_document() {
let input = "cat <<EOF\nhello world\nEOF";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_parse_array_assignment() {
let input = "arr=(a b c)";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
match &ast.statements[0] {
BashStmt::Assignment { value, .. } => {
assert!(matches!(value, BashExpr::Array(_)));
}
_ => panic!("Expected Assignment with Array"),
}
}
#[test]
fn test_parser_with_tracer() {
let tracer = crate::tracing::TraceManager::new();
let parser = BashParser::new("echo hello").unwrap().with_tracer(tracer);
assert!(parser.tracer.is_some());
}
#[test]
fn test_parse_multiple_newlines() {
let input = "\n\n\necho hello\n\n\n";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_parse_semicolon_separated() {
let input = "echo a\necho b\necho c";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert_eq!(ast.statements.len(), 3);
}
#[test]
fn test_parse_if_elif_else() {
let input = r#"
if [ $x -eq 1 ]; then
echo one
elif [ $x -eq 2 ]; then
echo two
else
echo other
fi
"#;
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(matches!(&ast.statements[0], BashStmt::If { .. }));
}
#[test]
fn test_parse_if_no_else() {
let input = "if [ $x -eq 1 ]; then echo one; fi";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
match &ast.statements[0] {
BashStmt::If { else_block, .. } => {
assert!(else_block.is_none());
}
_ => panic!("Expected If statement"),
}
}
#[test]
fn test_parse_variable_in_double_quotes() {
let input = r#"echo "Hello $name""#;
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(matches!(&ast.statements[0], BashStmt::Command { .. }));
}
#[test]
fn test_parse_command_with_args() {
let input = "echo hello world";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
match &ast.statements[0] {
BashStmt::Command { name, args, .. } => {
assert_eq!(name, "echo");
assert_eq!(args.len(), 2);
}
_ => panic!("Expected Command"),
}
}
#[test]
fn test_parse_command_with_path() {
let input = "ls /tmp";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
match &ast.statements[0] {
BashStmt::Command { name, args, .. } => {
assert_eq!(name, "ls");
assert_eq!(args.len(), 1);
}
_ => panic!("Expected Command"),
}
}
#[test]
fn test_coverage_empty_input() {
let mut parser = BashParser::new("").unwrap();
let ast = parser.parse().unwrap();
assert!(ast.statements.is_empty());
}
#[test]
fn test_coverage_whitespace_only() {
let mut parser = BashParser::new(" \n\t \n").unwrap();
let ast = parser.parse().unwrap();
assert!(ast.statements.is_empty());
}
#[test]
fn test_coverage_comments_only() {
let mut parser = BashParser::new("# comment\n# another").unwrap();
let ast = parser.parse().unwrap();
assert!(ast
.statements
.iter()
.all(|s| matches!(s, BashStmt::Comment { .. })));
}
#[test]
fn test_coverage_multiline_string() {
let input = r#"echo "line1
line2
line3""#;
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_escaped_quotes() {
let input = r#"echo "hello \"world\"""#;
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_single_quoted_string() {
let input = "echo 'hello $world'";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_heredoc_simple() {
let input = r#"cat <<EOF
hello world
EOF"#;
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_heredoc_quoted_delimiter() {
let input = r#"cat <<'EOF'
hello $world
EOF"#;
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_herestring() {
let input = r#"cat <<< "hello world""#;
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_array_declaration() {
let input = "arr=(one two three)";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_array_access() {
let input = "echo ${arr[0]}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_array_all_elements() {
let input = "echo ${arr[@]}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_arithmetic_expansion() {
let input = "echo $((1 + 2 * 3))";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_complex_arithmetic() {
let input = "result=$((a + b * c / d % e))";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_parameter_default_value() {
let input = "echo ${var:-default}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_parameter_assign_default() {
let input = "echo ${var:=default}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_parameter_error_if_unset() {
let input = "echo ${var:?error message}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_parameter_alternative_value() {
let input = "echo ${var:+alternative}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_substring_extraction() {
let input = "echo ${var:0:5}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_pattern_removal_prefix() {
let input = "echo ${var#pattern}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_pattern_removal_suffix() {
let input = "echo ${var%pattern}";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_command_substitution_backticks_unsupported() {
let input = "echo `date`";
let parser_result = BashParser::new(input);
assert!(parser_result.is_err() || parser_result.unwrap().parse().is_err());
}
#[test]
fn test_coverage_command_substitution_dollar() {
let input = "echo $(date)";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_process_substitution_input() {
let input = "diff <(sort file1) <(sort file2)";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_pipeline_simple() {
let input = "cat file | grep pattern";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(ast
.statements
.iter()
.any(|s| matches!(s, BashStmt::Pipeline { .. })));
}
#[test]
fn test_coverage_pipeline_long() {
let input = "cat file | grep pattern | sort | uniq";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
match &ast.statements[0] {
BashStmt::Pipeline { commands, .. } => {
assert_eq!(commands.len(), 4);
}
_ => panic!("Expected Pipeline"),
}
}
#[test]
fn test_coverage_redirect_fd_duplicate() {
let input = "cmd 2>&1";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_coverage_background_job_supported() {
let input = "sleep 10 &";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().expect("should parse background command");
assert_eq!(ast.statements.len(), 1);
assert!(matches!(&ast.statements[0], BashStmt::Command { name, .. } if name == "sleep"));
}
#[test]
fn test_coverage_mixed_and_or() {
let input = "cmd1 && cmd2 || cmd3";
let mut parser = BashParser::new(input).unwrap();
let ast = parser.parse().unwrap();
assert!(!ast.statements.is_empty());
}
#[test]
fn test_SUBSHELL_001_basic() {
let input = "(cd /tmp && ls)";
let mut parser = BashParser::new(input).expect("parser");
let ast = parser.parse().expect("should parse subshell");
match &ast.statements[0] {
BashStmt::BraceGroup { subshell, body, .. } => {
assert!(subshell, "should be marked as subshell");
assert!(!body.is_empty(), "subshell should have body");
}
other => panic!("Expected BraceGroup(subshell), got {other:?}"),
}
}
#[test]
fn test_SUBSHELL_002_simple_echo() {
let input = "(echo hello)";
let mut parser = BashParser::new(input).expect("parser");
let ast = parser.parse().expect("should parse subshell");
match &ast.statements[0] {
BashStmt::BraceGroup { subshell, .. } => {
assert!(subshell, "should be marked as subshell");
}
other => panic!("Expected BraceGroup(subshell), got {other:?}"),
}
}
#[test]
fn test_LOCAL_FLAG_001_local_dash_i() {
let input = r#"foo() {
local -i num=5
echo $num
}"#;
let mut parser = BashParser::new(input).expect("parser");
let ast = parser.parse().expect("should parse local -i");
match &ast.statements[0] {
BashStmt::Function { body, .. } => {
assert!(
body.len() >= 2,
"function should have at least 2 statements: {:?}",
body
);
}
other => panic!("Expected Function, got {other:?}"),
}
}
}