#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::process::Command;
use verificar::generator::BashEnumerator;
#[test]
fn test_verificar_programs_lint_without_panics() {
let bashrs = env!("CARGO_BIN_EXE_bashrs");
let enumerator = BashEnumerator::new(3);
let all_programs = enumerator.enumerate_programs();
let max_programs: usize = std::env::var("VERIFICAR_PROGRAMS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(100);
let programs: Vec<_> = all_programs.into_iter().take(max_programs).collect();
println!(
"Testing {} verificar-generated bash programs (limit: {})",
programs.len(),
max_programs
);
let mut passed = 0;
let mut panicked = 0;
let mut panic_details = Vec::new();
for (i, program) in programs.iter().enumerate() {
let temp_file = format!("/tmp/bashrs_verificar_test_{}.sh", i);
std::fs::write(&temp_file, &program.code).unwrap();
let output = Command::new(bashrs)
.args(["lint", &temp_file])
.output()
.expect("Failed to run bashrs");
let stderr = String::from_utf8_lossy(&output.stderr);
if stderr.contains("panic") || stderr.contains("RUST_BACKTRACE") {
panicked += 1;
panic_details.push((i, program.code.lines().next().unwrap_or("").to_string()));
} else {
passed += 1;
}
let _ = std::fs::remove_file(&temp_file);
}
if !panic_details.is_empty() {
eprintln!("\nPrograms that caused panics:");
for (i, first_line) in &panic_details {
eprintln!(" Test {}: {}", i, first_line);
}
}
assert!(
panicked == 0,
"bashrs panicked on {} out of {} programs",
panicked,
programs.len()
);
println!(
"Verificar integration: {}/{} programs tested, {} passed (set VERIFICAR_PROGRAMS for more)",
programs.len(),
max_programs,
passed
);
}
#[test]
fn test_verificar_programs_issue_detection() {
let bashrs = env!("CARGO_BIN_EXE_bashrs");
let programs_with_warnings = [
("echo $x", "SC2086"), ("for i in 1 2 3; do\n echo $i\ndone", "SC2086"), ];
for (program, expected_rule) in programs_with_warnings {
let temp_file = "/tmp/bashrs_verificar_warning_test.sh";
std::fs::write(temp_file, program).unwrap();
let output = Command::new(bashrs)
.args(["lint", temp_file])
.output()
.expect("Failed to run bashrs");
let combined = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert!(
combined.contains(expected_rule),
"Expected {} for program: {}\nGot: {}",
expected_rule,
program.lines().next().unwrap_or(""),
combined
);
let _ = std::fs::remove_file(temp_file);
}
}
#[test]
fn test_non_deterministic_detection() {
let bashrs = env!("CARGO_BIN_EXE_bashrs");
let non_deterministic_programs = [("result=$(date)", "DET002"), ("now=$(date +%s)", "DET002")];
for (program, expected_rule) in non_deterministic_programs {
let temp_file = "/tmp/bashrs_nondet_test.sh";
std::fs::write(temp_file, program).unwrap();
let output = Command::new(bashrs)
.args(["lint", temp_file])
.output()
.expect("Failed to run bashrs");
let combined = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert!(
combined.contains(expected_rule),
"Expected {} for non-deterministic program: {}\nGot: {}",
expected_rule,
program,
combined
);
let _ = std::fs::remove_file(temp_file);
}
}
#[test]
fn test_posix_modifiers_no_sc2299() {
let bashrs = env!("CARGO_BIN_EXE_bashrs");
let posix_modifier_programs = [
r#"echo "${var:-default}""#,
r#"echo "${var:+alternate}""#,
r#"echo "${var:=assigned}""#,
r#"echo "${var:?error}""#,
r#"x="${PROPTEST_THREADS:-$(nproc)}""#, ];
for program in posix_modifier_programs {
let temp_file = "/tmp/bashrs_posix_modifier_test.sh";
std::fs::write(temp_file, program).unwrap();
let output = Command::new(bashrs)
.args(["lint", temp_file])
.output()
.expect("Failed to run bashrs");
let combined = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert!(
!combined.contains("SC2299"),
"SC2299 should NOT trigger for POSIX modifier: {}\nGot: {}",
program,
combined
);
let _ = std::fs::remove_file(temp_file);
}
}
#[test]
fn test_verificar_coverage_stats() {
let enumerator = BashEnumerator::new(3);
let programs = enumerator.enumerate_programs();
let mut feature_counts: std::collections::HashMap<String, usize> =
std::collections::HashMap::new();
for program in &programs {
for feature in &program.features {
let key: String = feature.clone();
*feature_counts.entry(key).or_insert(0) += 1;
}
}
println!("\nVerificar coverage statistics:");
println!(" Total programs: {}", programs.len());
println!(" Unique features: {}", feature_counts.len());
let mut features: Vec<_> = feature_counts.iter().collect();
features.sort_by(|a, b| b.1.cmp(a.1));
println!(" Top features:");
for (feature, count) in features.iter().take(15) {
println!(" {}: {}", feature, count);
}
assert!(
feature_counts.len() >= 20,
"Expected at least 20 unique features, got {}",
feature_counts.len()
);
}