use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::bug_hunter::types::{ChannelWeights, LocalizationStrategy};
use super::*;
#[test]
fn test_compute_semantic_score_no_error_message() {
let localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
let score = localizer.compute_semantic_score(
Path::new("test.rs"),
1,
"fn main() { println!(\"hello\"); }",
);
assert_eq!(score, 0.0, "Should return 0.0 when no error message is set");
}
#[test]
fn test_compute_semantic_score_matching_keywords() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.set_error_message("index overflow detected in array");
let content = "fn process_array() { index overflow check }";
let score = localizer.compute_semantic_score(Path::new("test.rs"), 1, content);
assert!(score > 0.0, "Should match keywords from error message");
assert!(score <= 1.0);
}
#[test]
fn test_compute_semantic_score_no_matching_keywords() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.set_error_message("index overflow detected in array");
let content = "fn hello_world() { println!(\"hi\"); }";
let score = localizer.compute_semantic_score(Path::new("test.rs"), 1, content);
assert_eq!(score, 0.0, "Should return 0.0 when no keywords match");
}
#[test]
fn test_compute_semantic_score_partial_match() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.set_error_message("buffer overflow detected in memory allocation");
let content = "fn check() { handle overflow here }";
let score = localizer.compute_semantic_score(Path::new("test.rs"), 1, content);
assert!(score > 0.0, "Should have partial match");
assert!(score < 1.0, "Should not be perfect match");
}
#[test]
fn test_compute_semantic_score_all_keywords_match() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.set_error_message("buffer overflow detected");
let content = "buffer overflow detected here";
let score = localizer.compute_semantic_score(Path::new("test.rs"), 1, content);
assert!(
(score - 1.0).abs() < f64::EPSILON,
"Should return 1.0 when all keywords match, got {}",
score
);
}
#[test]
fn test_compute_semantic_score_short_words_filtered() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.set_error_message("a be on if");
let content = "a be on if match";
let score = localizer.compute_semantic_score(Path::new("test.rs"), 1, content);
assert_eq!(score, 0.0, "Should return 0.0 when all error words are <= 3 chars");
}
#[test]
fn test_compute_semantic_score_multiline_content() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.set_error_message("panic detected crash");
let content = "fn safe() {}\nfn dangerous() { panic detected crash }\nfn other() {}";
let score = localizer.compute_semantic_score(Path::new("test.rs"), 2, content);
assert!(score > 0.0, "Should match keywords on the target line");
}
#[test]
fn test_compute_semantic_score_line_beyond_content() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.set_error_message("panic detected");
let content = "fn main() {}";
let score = localizer.compute_semantic_score(Path::new("test.rs"), 100, content);
assert_eq!(score, 0.0, "Should return 0.0 for line beyond content length");
}
#[test]
fn test_compute_semantic_score_case_insensitive() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.set_error_message("BUFFER OVERFLOW");
let content = "fn check() { buffer overflow here }";
let score = localizer.compute_semantic_score(Path::new("test.rs"), 1, content);
assert!(score > 0.0, "Should be case-insensitive when matching keywords");
}
#[test]
fn test_add_static_finding() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
localizer.add_static_finding(Path::new("src/lib.rs"), 42, 0.75);
localizer.add_static_finding(Path::new("src/main.rs"), 10, 0.3);
assert_eq!(*localizer.static_findings.get(&(PathBuf::from("src/lib.rs"), 42)).unwrap(), 0.75);
assert_eq!(*localizer.static_findings.get(&(PathBuf::from("src/main.rs"), 10)).unwrap(), 0.3);
}
#[test]
fn test_set_error_message() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
assert!(localizer.error_message.is_none());
localizer.set_error_message("test error");
assert_eq!(localizer.error_message.as_deref(), Some("test error"));
}
#[test]
fn test_localize_with_error_message() {
let temp = std::env::temp_dir().join("test_localize_semantic");
let _ = std::fs::create_dir_all(&temp);
let file_name = "semantic_test.rs";
std::fs::write(
temp.join(file_name),
"fn process() {\n panic!(\"buffer overflow detected\");\n}\n",
)
.unwrap();
let mut localizer = MultiChannelLocalizer::new(
LocalizationStrategy::MultiChannel,
ChannelWeights {
spectrum: 0.0,
mutation: 0.0,
static_analysis: 0.0,
semantic: 1.0,
quality: 0.0,
},
);
localizer.set_error_message("buffer overflow detected");
localizer.static_findings.insert((PathBuf::from(file_name), 2), 0.5);
let results = localizer.localize(&temp);
assert_eq!(results.len(), 1);
}
#[test]
fn test_localize_with_error_message_file_not_found() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::MultiChannel, ChannelWeights::default());
localizer.set_error_message("some error");
localizer.static_findings.insert((PathBuf::from("nonexistent_file.rs"), 10), 0.5);
let results = localizer.localize(Path::new("/nonexistent_project"));
assert_eq!(results.len(), 1);
assert_eq!(results[0].semantic_score, 0.0);
}
#[test]
fn test_localize_after_add_coverage() {
let mut localizer =
MultiChannelLocalizer::new(LocalizationStrategy::Sbfl, ChannelWeights::default());
let mut pass_lines = HashMap::new();
pass_lines.insert((PathBuf::from("lib.rs"), 10), 1);
let mut fail_lines = HashMap::new();
fail_lines.insert((PathBuf::from("lib.rs"), 10), 1);
fail_lines.insert((PathBuf::from("lib.rs"), 20), 1);
localizer.add_coverage(&[
TestCoverage { test_name: "pass".to_string(), passed: true, executed_lines: pass_lines },
TestCoverage { test_name: "fail".to_string(), passed: false, executed_lines: fail_lines },
]);
let results = localizer.localize(Path::new("/tmp"));
assert_eq!(results.len(), 2);
let line20 = results.iter().find(|r| r.line == 20).unwrap();
let line10 = results.iter().find(|r| r.line == 10).unwrap();
assert!(
line20.final_score >= line10.final_score,
"Line only in failing tests should have higher score"
);
}