use super::*;
#[test]
fn test_get_builtins_contains_posix() {
let builtins = get_builtins();
assert!(builtins.contains("HOME"));
assert!(builtins.contains("PATH"));
assert!(builtins.contains("PWD"));
assert!(builtins.contains("USER"));
}
#[test]
fn test_get_builtins_contains_bash() {
let builtins = get_builtins();
assert!(builtins.contains("BASH_VERSION"));
assert!(builtins.contains("RANDOM"));
assert!(builtins.contains("LINENO"));
}
#[test]
fn test_get_builtins_not_empty() {
let builtins = get_builtins();
assert!(builtins.len() > 50);
}
#[test]
fn test_is_special_or_builtin_builtin() {
let builtins = get_builtins();
assert!(is_special_or_builtin("HOME", &builtins));
assert!(is_special_or_builtin("PATH", &builtins));
}
#[test]
fn test_is_special_or_builtin_numeric() {
let builtins = get_builtins();
assert!(is_special_or_builtin("1", &builtins));
assert!(is_special_or_builtin("2", &builtins));
assert!(is_special_or_builtin("10", &builtins));
}
#[test]
fn test_is_special_or_builtin_special() {
let builtins = get_builtins();
assert!(is_special_or_builtin("@", &builtins));
assert!(is_special_or_builtin("*", &builtins));
assert!(is_special_or_builtin("#", &builtins));
assert!(is_special_or_builtin("?", &builtins));
}
#[test]
fn test_is_special_or_builtin_regular() {
let builtins = get_builtins();
assert!(!is_special_or_builtin("my_var", &builtins));
assert!(!is_special_or_builtin("foo", &builtins));
}
#[test]
fn test_has_source_commands_source() {
assert!(has_source_commands("source /etc/profile"));
assert!(has_source_commands("source ./env.sh"));
}
#[test]
fn test_has_source_commands_dot() {
assert!(has_source_commands(". /etc/profile"));
assert!(has_source_commands(". ./env.sh"));
}
#[test]
fn test_has_source_commands_in_line() {
assert!(has_source_commands("cd /tmp; source ./env.sh"));
assert!(has_source_commands("test && source ./env.sh"));
}
#[test]
fn test_has_source_commands_none() {
assert!(!has_source_commands("echo hello"));
assert!(!has_source_commands("# source comment"));
}
#[test]
fn test_is_comment_line_true() {
assert!(is_comment_line("# comment"));
assert!(is_comment_line(" # indented"));
}
#[test]
fn test_is_comment_line_false() {
assert!(!is_comment_line("echo hello"));
assert!(!is_comment_line(""));
}
#[test]
fn test_is_uppercase_var_true() {
assert!(is_uppercase_var("HOME"));
assert!(is_uppercase_var("MY_VAR"));
assert!(is_uppercase_var("FOO_BAR_BAZ"));
}
#[test]
fn test_is_uppercase_var_false() {
assert!(!is_uppercase_var("home"));
assert!(!is_uppercase_var("myVar"));
assert!(!is_uppercase_var("Home"));
}
#[test]
fn test_is_case_start_true() {
assert!(is_case_start("case $x in"));
assert!(is_case_start(" case \"$var\" in"));
}
#[test]
fn test_is_case_start_false() {
assert!(!is_case_start("esac"));
assert!(!is_case_start("echo case"));
}
#[test]
fn test_is_case_end_true() {
assert!(is_case_end("esac"));
assert!(is_case_end("esac;"));
assert!(is_case_end("esac "));
}
#[test]
fn test_is_case_end_false() {
assert!(!is_case_end("case $x in"));
}
#[test]
fn test_case_has_default_true() {
let block = vec!["case $x in", "a) echo a;;", "*) echo default;;", "esac"];
assert!(case_has_default(&block));
}
#[test]
fn test_case_has_default_false() {
let block = vec!["case $x in", "a) echo a;;", "b) echo b;;", "esac"];
assert!(!case_has_default(&block));
}
#[test]
fn test_is_case_pattern_line() {
assert!(is_case_pattern_line("a)"));
assert!(is_case_pattern_line("*)"));
assert!(is_case_pattern_line(" a | b)"));
assert!(!is_case_pattern_line("var=value)"));
}
#[test]
fn test_collect_case_statement_variables_with_default() {
let source = "case $x in\na) foo=1;;\n*) bar=2;;\nesac";
let vars = collect_case_statement_variables(source);
assert!(vars.contains("foo"));
assert!(vars.contains("bar"));
}
#[test]
fn test_collect_case_statement_variables_no_default() {
let source = "case $x in\na) foo=1;;\nb) bar=2;;\nesac";
let vars = collect_case_statement_variables(source);
assert!(vars.is_empty());
}
#[test]
fn test_extract_read_variables_simple() {
let vars = extract_read_variables("read foo");
assert_eq!(vars, vec!["foo"]);
}
#[test]
fn test_extract_read_variables_multiple() {
let vars = extract_read_variables("read a b c");
assert_eq!(vars, vec!["a", "b", "c"]);
}
#[test]
fn test_extract_read_variables_with_flags() {
let vars = extract_read_variables("read -r foo");
assert_eq!(vars, vec!["foo"]);
}
#[test]
fn test_extract_read_variables_with_prompt() {
let vars = extract_read_variables("read -p prompt foo");
assert_eq!(vars, vec!["foo"]);
}
#[test]
fn test_extract_read_variables_none() {
let vars = extract_read_variables("echo hello");
assert!(vars.is_empty());
}
#[test]
fn test_extract_read_variables_with_array_flag() {
let vars = extract_read_variables("read -a arr line");
assert_eq!(vars, vec!["line"]);
}
#[test]
fn test_extract_read_variables_with_delimiter_flag() {
let vars = extract_read_variables("read -d : foo");
assert_eq!(vars, vec!["foo"]);
}
#[test]
fn test_has_source_commands_or_chain() {
assert!(has_source_commands("test -f x || source default.sh"));
assert!(has_source_commands("test -f x || . default.sh"));
}
#[test]
fn test_create_patterns_valid() {
let p = create_patterns();
assert!(p.assign.is_match("local foo="));
assert!(p.use_.is_match("$var"));
assert!(p.for_loop.is_match("for x in"));
}
#[test]
fn test_collect_variable_info_basic() {
let p = create_patterns();
let source = "foo=bar\necho $foo";
let (assigned, used) = collect_variable_info(source, &p);
assert!(assigned.contains("foo"));
assert!(used.iter().any(|(v, _, _)| v == "foo"));
}
#[test]
fn test_collect_variable_info_for_loop() {
let p = create_patterns();
let source = "for x in *.txt; do echo $x; done";
let (assigned, _) = collect_variable_info(source, &p);
assert!(assigned.contains("x"));
}
#[test]
fn test_collect_variable_info_c_style_for() {
let p = create_patterns();
let source = "for ((i=0; i<10; i++)); do echo $i; done";
let (assigned, _) = collect_variable_info(source, &p);
assert!(assigned.contains("i"));
}
#[test]
fn test_collect_variable_info_case_expr() {
let p = create_patterns();
let source = "case $mode in a) ;; esac";
let (assigned, _) = collect_variable_info(source, &p);
assert!(assigned.contains("mode"));
}
#[test]
fn test_collect_variable_info_read_command() {
let p = create_patterns();
let source = "read line\necho $line";
let (assigned, _) = collect_variable_info(source, &p);
assert!(assigned.contains("line"));
}
#[test]
fn test_collect_variable_info_skips_comments() {
let p = create_patterns();
let source = "# foo=bar\necho $foo";
let (assigned, used) = collect_variable_info(source, &p);
assert!(!assigned.contains("foo"));
assert!(used.iter().any(|(v, _, _)| v == "foo"));
}
#[test]
fn test_collect_variable_info_with_source_skips_uppercase() {
let p = create_patterns();
let source = "source config.sh\necho $CONFIG_VAR";
let (_, used) = collect_variable_info(source, &p);
assert!(!used.iter().any(|(v, _, _)| v == "CONFIG_VAR"));
}
#[test]
fn test_find_undefined_variables_none() {
let builtins = get_builtins();
let assigned: HashSet<String> = ["foo".to_string()].into_iter().collect();
let used = vec![("foo".to_string(), 1, 1)];
let undef = find_undefined_variables(&assigned, &used, &builtins);
assert!(undef.is_empty());
}
#[test]
fn test_find_undefined_variables_found() {
let builtins = get_builtins();
let assigned: HashSet<String> = HashSet::new();
let used = vec![("undefined_var".to_string(), 1, 1)];
let undef = find_undefined_variables(&assigned, &used, &builtins);
assert_eq!(undef.len(), 1);
assert_eq!(undef[0].0, "undefined_var");
}
#[test]
fn test_find_undefined_variables_skips_builtins() {
let builtins = get_builtins();
let assigned: HashSet<String> = HashSet::new();
let used = vec![("HOME".to_string(), 1, 1), ("PATH".to_string(), 1, 5)];
let undef = find_undefined_variables(&assigned, &used, &builtins);
assert!(undef.is_empty());
}
#[test]
fn test_find_undefined_variables_skips_positional() {
let builtins = get_builtins();
let assigned: HashSet<String> = HashSet::new();
let used = vec![("1".to_string(), 1, 1), ("2".to_string(), 1, 3)];
let undef = find_undefined_variables(&assigned, &used, &builtins);
assert!(undef.is_empty());
}