#[test]
fn test_bh_mod_039_hunt_with_ticket_nonexistent() {
let config = HuntConfig::default();
let result = hunt_with_ticket(Path::new("/tmp"), "CB-999", config);
assert!(result.is_err(), "Non-existent ticket should fail");
}
#[test]
fn test_bh_mod_040_quick_mode_runs_patterns() {
let temp = std::env::temp_dir().join(format!("test_bh_mod_040_quick_{}", std::process::id()));
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"fn code() { let x = val.unwrap(); }\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Quick, config.clone());
run_quick_mode(&temp, &config, &mut result);
let found_unwrap = result.findings.iter().any(|f| f.title.contains("unwrap()"));
assert!(found_unwrap, "Quick mode should find unwrap pattern");
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_041_hunt_mode_with_crash_log() {
let temp = std::env::temp_dir().join("test_bh_mod_041_crash");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(&temp);
std::fs::write(
temp.join("crash.log"),
"thread 'main' panicked\n at src/parser.rs:55\n",
)
.unwrap();
let config = HuntConfig {
targets: vec![PathBuf::from("src")],
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Hunt, config.clone());
run_hunt_mode(&temp, &config, &mut result);
let stack = result
.findings
.iter()
.any(|f| f.id.starts_with("BH-STACK-"));
assert!(stack, "Should find stack trace from crash.log");
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_042_coverage_hotspots_standard_path() {
let temp = std::env::temp_dir().join("test_bh_mod_042_stdpath");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("target/coverage"));
std::fs::write(
temp.join("target/coverage/lcov.info"),
"SF:src/lib.rs\nDA:1,0\nDA:2,0\nDA:3,0\nDA:4,0\nDA:5,0\nDA:6,0\nDA:7,0\nend_of_record\n",
)
.unwrap();
let config = HuntConfig::default();
let mut result = HuntResult::new(&temp, HuntMode::Hunt, config.clone());
analyze_coverage_hotspots(&temp, &config, &mut result);
let cov = result.findings.iter().any(|f| f.id.starts_with("BH-COV-"));
assert!(cov, "Should find coverage hotspot from standard path");
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_043_hunt_cache_hit_path() {
let temp = std::env::temp_dir().join("test_bh_mod_043_cache");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
let _ = std::fs::create_dir_all(temp.join(".pmat/bug-hunter-cache"));
std::fs::write(
temp.join("src/lib.rs"),
"fn code() { let x = val.unwrap(); }\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
..Default::default()
};
let result1 = hunt(&temp, config.clone());
let result2 = hunt(&temp, config);
assert_eq!(result1.mode, HuntMode::Quick);
assert_eq!(result2.mode, HuntMode::Quick);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_044_hunt_pmat_quality_enabled() {
let temp = std::env::temp_dir().join("test_bh_mod_044_pmat");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"fn code() { let x = val.unwrap(); }\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
use_pmat_quality: true,
pmat_query: Some("*".to_string()),
..Default::default()
};
let result = hunt(&temp, config);
assert_eq!(result.mode, HuntMode::Quick);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_045_hunt_coverage_weight() {
let temp = std::env::temp_dir().join("test_bh_mod_045_covwt");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"fn code() { let x = val.unwrap(); }\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
coverage_weight: 1.0,
coverage_path: Some(PathBuf::from("/nonexistent/lcov.info")),
..Default::default()
};
let result = hunt(&temp, config);
assert_eq!(result.mode, HuntMode::Quick);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_045_hunt_coverage_weight_with_file() {
let temp = std::env::temp_dir().join("test_bh_mod_045_covwt_file");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"fn code() { let x = val.unwrap(); }\n",
)
.unwrap();
let lcov_path = temp.join("lcov.info");
std::fs::write(
&lcov_path,
"SF:src/lib.rs\nDA:1,0\nDA:2,0\nDA:3,0\nDA:4,0\nDA:5,0\nDA:6,0\nDA:7,0\nend_of_record\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
coverage_weight: 1.0,
coverage_path: Some(lcov_path),
..Default::default()
};
let result = hunt(&temp, config);
assert_eq!(result.mode, HuntMode::Quick);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_046_apply_spec_quality_gate_no_pmat() {
let mut parsed_spec = ParsedSpec {
claims: vec![],
original_content: String::new(),
path: PathBuf::new(),
};
let mut result = HuntResult::new("/tmp", HuntMode::Analyze, HuntConfig::default());
apply_spec_quality_gate(&mut parsed_spec, Path::new("/tmp"), &mut result, "*");
assert!(result.findings.is_empty());
}
#[test]
fn test_bh_mod_047_falsify_mode_mutants_unavailable() {
let temp = std::env::temp_dir().join(format!("test_bh_mod_047_falsify_unavail_{}", std::process::id()));
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"fn check(v: &[u8]) -> usize {\n\
if v.len() > 0 {\n\
let idx = v.len() - 1 as usize;\n\
idx\n\
} else {\n\
0\n\
}\n\
}\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Falsify,
targets: vec![PathBuf::from("src")],
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Falsify, config.clone());
run_falsify_mode(&temp, &config, &mut result);
let has_unavail = result.findings.iter().any(|f| f.id == "BH-FALSIFY-UNAVAIL");
let has_mutations = result.findings.iter().any(|f| f.id.starts_with("BH-MUT-"));
assert!(
has_unavail || has_mutations,
"Should have either UNAVAIL or mutation findings, got {} findings: {:?}",
result.findings.len(),
result.findings.iter().map(|f| &f.id).collect::<Vec<_>>()
);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_048_deep_hunt_mode_combines_deep_and_hunt() {
let temp = std::env::temp_dir().join("test_bh_mod_048_deep_combined");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/complex.rs"),
"fn complex(a: i32, b: bool, c: bool) {\n\
if a > 0 {\n\
match a {\n\
1 => if b && c || !b {\n\
println!(\"mixed\");\n\
},\n\
_ => if a > 10 {\n\
println!(\"deep\");\n\
},\n\
}\n\
}\n\
}\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::DeepHunt,
targets: vec![PathBuf::from("src")],
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::DeepHunt, config.clone());
run_deep_hunt_mode(&temp, &config, &mut result);
assert!(
!result.findings.is_empty(),
"Deep hunt should produce findings from both deep analysis and hunt mode"
);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_049_common_patterns_python_file() {
let temp = std::env::temp_dir().join("test_bh_mod_049_python");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/script.py"),
"import os\n# TODO: refactor this function\ndef process():\n pass\n",
)
.unwrap();
let config = HuntConfig {
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
pmat_satd: false,
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Analyze, config.clone());
analyze_common_patterns(&temp, &config, &mut result);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_049_common_patterns_typescript_file() {
let temp = std::env::temp_dir().join("test_bh_mod_049_ts");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/app.ts"),
"// HACK: temporary workaround\nexport function handler() {}\n",
)
.unwrap();
let config = HuntConfig {
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
pmat_satd: false,
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Analyze, config.clone());
analyze_common_patterns(&temp, &config, &mut result);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_050_common_patterns_pmat_satd_with_pmat_query() {
let temp = std::env::temp_dir().join("test_bh_mod_050_satd_query");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"fn code() { let x = val.unwrap(); }\n",
)
.unwrap();
let config = HuntConfig {
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
pmat_satd: true,
pmat_query: Some("error handling".to_string()),
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Analyze, config.clone());
analyze_common_patterns(&temp, &config, &mut result);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_051_hunt_with_spec_pmat_quality() {
let temp = std::env::temp_dir().join("test_bh_mod_051_spec_pmat");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
let spec_content =
"# Test Spec\n\n## Section 1\n\n### TST-01: Test Claim\n\nThis claim tests something.\n";
std::fs::write(temp.join("spec.md"), spec_content).unwrap();
std::fs::write(
temp.join("src/lib.rs"),
"// TST-01: implements test claim\nfn test_impl() {}\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
use_pmat_quality: true,
pmat_query: Some("*".to_string()),
..Default::default()
};
let result = hunt_with_spec(&temp, &temp.join("spec.md"), None, config);
assert!(result.is_ok());
let (hunt_result, _parsed_spec) = result.unwrap();
assert_eq!(hunt_result.mode, HuntMode::Quick);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_052_hunt_mode_with_junit_xml() {
let temp = std::env::temp_dir().join("test_bh_mod_052_junit");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("target/nextest/ci"));
std::fs::write(
temp.join("target/nextest/ci/junit.xml"),
"<testsuite><testcase><failure>thread 'test' panicked at src/lib.rs:10</failure></testcase></testsuite>\n",
)
.unwrap();
let config = HuntConfig {
targets: vec![PathBuf::from("src")],
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Hunt, config.clone());
run_hunt_mode(&temp, &config, &mut result);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_053_hunt_pmat_quality_on_real_project() {
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
use_pmat_quality: true,
pmat_query: Some("hunt".to_string()),
quality_weight: 0.5,
..Default::default()
};
let result = hunt(Path::new("."), config);
assert_eq!(result.mode, HuntMode::Quick);
}
#[test]
fn test_bh_mod_054_apply_spec_quality_gate_real_project() {
use super::spec::{ClaimStatus, CodeLocation, SpecClaim};
let mut parsed_spec = ParsedSpec {
path: PathBuf::from("test_spec.md"),
claims: vec![SpecClaim {
id: "CLAIM-01".to_string(),
title: "Test Claim".to_string(),
line: 1,
section_path: vec!["Section 1".to_string()],
implementations: vec![CodeLocation {
file: PathBuf::from("src/bug_hunter/mod.rs"),
line: 66,
context: "hunt function".to_string(),
}],
findings: Vec::new(),
status: ClaimStatus::Pending,
}],
original_content: "# Spec\n## Section 1\n### CLAIM-01: Test\n".to_string(),
};
let mut result = HuntResult::new(".", HuntMode::Analyze, HuntConfig::default());
let initial_count = result.findings.len();
apply_spec_quality_gate(&mut parsed_spec, Path::new("."), &mut result, "hunt");
let _ = result.findings.len() >= initial_count; }
#[test]
fn test_bh_mod_054_apply_spec_quality_gate_low_quality_finding() {
use super::spec::{ClaimStatus, CodeLocation, SpecClaim};
let mut parsed_spec = ParsedSpec {
path: PathBuf::from("test_spec.md"),
claims: vec![SpecClaim {
id: "LQ-01".to_string(),
title: "Low Quality Claim".to_string(),
line: 1,
section_path: vec!["Quality".to_string()],
implementations: vec![
CodeLocation {
file: PathBuf::from("src/bug_hunter/mod.rs"),
line: 990,
context: "analyze_common_patterns".to_string(),
},
CodeLocation {
file: PathBuf::from("src/nonexistent.rs"),
line: 1,
context: "missing file".to_string(),
},
],
findings: Vec::new(),
status: ClaimStatus::Pending,
}],
original_content: "# Spec\n## Quality\n### LQ-01: Low Quality\n".to_string(),
};
let mut result = HuntResult::new(".", HuntMode::Analyze, HuntConfig::default());
apply_spec_quality_gate(&mut parsed_spec, Path::new("."), &mut result, "*");
}
#[test]
fn test_bh_mod_054_apply_spec_quality_gate_no_pmat() {
use super::spec::{ClaimStatus, CodeLocation, SpecClaim};
let mut parsed_spec = ParsedSpec {
path: PathBuf::from("test_spec.md"),
claims: vec![SpecClaim {
id: "NP-01".to_string(),
title: "No Pmat".to_string(),
line: 1,
section_path: vec![],
implementations: vec![CodeLocation {
file: PathBuf::from("src/lib.rs"),
line: 1,
context: "main".to_string(),
}],
findings: Vec::new(),
status: ClaimStatus::Pending,
}],
original_content: String::new(),
};
let mut result = HuntResult::new("/nonexistent", HuntMode::Analyze, HuntConfig::default());
let before = result.findings.len();
apply_spec_quality_gate(
&mut parsed_spec,
Path::new("/nonexistent/project"),
&mut result,
"*",
);
assert_eq!(result.findings.len(), before);
}
#[test]
fn test_bh_mod_055_hunt_with_spec_pmat_quality_real_project() {
let temp = std::env::temp_dir().join("test_bh_mod_055_spec_real");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(&temp);
let spec_content = "\
# Bug Hunter Spec
## Section 1: Hunting
### BH-01: Hunt Function
The hunt function should support all modes.
";
let spec_path = temp.join("spec.md");
std::fs::write(&spec_path, spec_content).unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
use_pmat_quality: true,
pmat_query: Some("hunt".to_string()),
quality_weight: 0.5,
..Default::default()
};
let result = hunt_with_spec(Path::new("."), &spec_path, None, config);
assert!(result.is_ok());
let (hunt_result, parsed_spec) = result.unwrap();
assert!(!parsed_spec.claims.is_empty());
assert_eq!(hunt_result.mode, HuntMode::Quick);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_056_hunt_falsify_no_cache() {
let temp = std::env::temp_dir().join("test_bh_mod_056_falsify");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"pub fn add(a: usize, b: usize) -> usize { a + b }\n",
)
.unwrap();
std::fs::write(
temp.join("Cargo.toml"),
"[package]\nname=\"t\"\nversion=\"0.1.0\"\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Falsify,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt(&temp, config);
assert_eq!(result.mode, HuntMode::Falsify);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_056_hunt_fuzz_no_cache() {
let temp = std::env::temp_dir().join("test_bh_mod_056_fuzz");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"#![forbid(unsafe_code)]\npub fn safe() {}\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Fuzz,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt(&temp, config);
assert_eq!(result.mode, HuntMode::Fuzz);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_056_hunt_deephunt_no_cache() {
let temp = std::env::temp_dir().join("test_bh_mod_056_deep");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(temp.join("src/lib.rs"), "pub fn simple() -> i32 { 42 }\n").unwrap();
let config = HuntConfig {
mode: HuntMode::DeepHunt,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt(&temp, config);
assert_eq!(result.mode, HuntMode::DeepHunt);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_057_hunt_with_ticket_markdown() {
let temp = std::env::temp_dir().join("test_bh_mod_057_ticket");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
let _ = std::fs::create_dir_all(temp.join(".pmat/tickets"));
std::fs::write(temp.join("src/lib.rs"), "pub fn demo() {}\n").unwrap();
let ticket_content = "\
# PMAT-999: Test ticket
## Description
This is a test ticket for coverage.
## Affected Paths
- src/lib.rs
## Priority
high
";
let ticket_path = temp.join(".pmat/tickets/PMAT-999.md");
std::fs::write(&ticket_path, ticket_content).unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt_with_ticket(&temp, "PMAT-999", config);
assert!(result.is_ok());
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_057_hunt_with_ticket_github_issue() {
let temp = std::env::temp_dir().join("test_bh_mod_057_gh");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(temp.join("src/lib.rs"), "pub fn x() {}\n").unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt_with_ticket(&temp, "PMAT-123", config);
assert!(result.is_ok());
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_057_hunt_with_ticket_invalid_ref() {
let config = HuntConfig::default();
let temp = std::env::temp_dir().join("test_bh_mod_057_invalid");
let _ = std::fs::create_dir_all(&temp);
let result = hunt_with_ticket(&temp, "not_a_valid_ref", config);
assert!(result.is_err());
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
#[allow(unsafe_code)]
fn test_bh_mod_058_coverage_hotspots_cargo_target_dir() {
let temp = std::env::temp_dir().join("test_bh_mod_058_target_dir");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(&temp);
let custom_coverage = temp.join("custom_coverage");
let _ = std::fs::create_dir_all(&custom_coverage);
let lcov_path = custom_coverage.join("lcov.info");
std::fs::write(
&lcov_path,
"SF:src/lib.rs\nDA:1,0\nDA:2,0\nDA:3,0\nDA:4,0\nDA:5,0\nDA:6,0\nend_of_record\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Hunt,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
coverage_path: Some(lcov_path),
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Hunt, config.clone());
analyze_coverage_hotspots(&temp, &config, &mut result);
let has_cov = result.findings.iter().any(|f| {
f.title.contains("coverage") || f.title.contains("Low coverage") || f.id.contains("COV")
});
assert!(has_cov, "Should find coverage data from coverage_path");
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_058_coverage_hotspots_custom_path() {
let temp = std::env::temp_dir().join("test_bh_mod_058_custom");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(&temp);
let lcov = temp.join("my_coverage.info");
std::fs::write(
&lcov,
"SF:src/lib.rs\nDA:1,0\nDA:2,0\nDA:3,0\nDA:4,0\nDA:5,0\nDA:6,0\nend_of_record\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Hunt,
targets: vec![PathBuf::from("src")],
coverage_path: Some(lcov),
min_suspiciousness: 0.0,
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Hunt, config.clone());
analyze_coverage_hotspots(&temp, &config, &mut result);
let has_cov = result.findings.iter().any(|f| f.id.contains("COV"));
assert!(has_cov, "Should find coverage hotspots from custom path");
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_059_falsify_with_mutation_targets() {
let temp = std::env::temp_dir().join("test_bh_mod_059_mut");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/boundary.rs"),
"pub fn check(v: &[u8]) -> bool { v.len() < 10 }\n",
)
.unwrap();
std::fs::write(
temp.join("src/arith.rs"),
"pub fn calc(x: i64) -> usize { (x + 1) as usize }\n",
)
.unwrap();
std::fs::write(
temp.join("src/logic.rs"),
"pub fn test(a: bool, b: bool) -> bool { a && !b || is_valid() }\nfn is_valid() -> bool { true }\n",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Falsify,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Falsify, config.clone());
run_falsify_mode(&temp, &config, &mut result);
let mutants_available = std::process::Command::new("cargo")
.args(["mutants", "--version"])
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if mutants_available {
let has_boundary = result.findings.iter().any(|f| f.title.contains("Boundary"));
let has_arith = result
.findings
.iter()
.any(|f| f.title.contains("Arithmetic"));
let has_bool = result.findings.iter().any(|f| f.title.contains("Boolean"));
assert!(
has_boundary,
"Should detect boundary condition mutation target"
);
assert!(has_arith, "Should detect arithmetic mutation target");
assert!(has_bool, "Should detect boolean logic mutation target");
} else {
assert!(
result.findings.iter().any(|f| f.id == "BH-FALSIFY-UNAVAIL"),
"Should report cargo-mutants unavailable"
);
}
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_060_hunt_with_spec_section_filter() {
let temp = std::env::temp_dir().join("test_bh_mod_060_section");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(temp.join("src/lib.rs"), "pub fn target() {}\n").unwrap();
let spec_content = "\
# Test Spec
## Section A: Auth
### AUTH-01: Authentication
Users must authenticate.
## Section B: Data
### DATA-01: Storage
Data must be stored.
";
let spec_path = temp.join("spec.md");
std::fs::write(&spec_path, spec_content).unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt_with_spec(&temp, &spec_path, Some("Section A"), config);
assert!(result.is_ok());
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_060_hunt_with_spec_empty_targets_fallback() {
let temp = std::env::temp_dir().join("test_bh_mod_060_empty");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(temp.join("src/lib.rs"), "pub fn nothing() {}\n").unwrap();
let spec_content = "\
# Minimal Spec
## Section 1
### MIN-01: Minimal claim
No implementations here.
";
let spec_path = temp.join("spec.md");
std::fs::write(&spec_path, spec_content).unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt_with_spec(&temp, &spec_path, None, config);
assert!(result.is_ok());
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_061_fuzz_mode_with_unsafe_blocks() {
let temp = std::env::temp_dir().join("test_bh_mod_061_unsafe");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(
temp.join("src/lib.rs"),
"\
pub fn risky(ptr: *const u8) -> u8 {
// SAFETY: no actual unsafe code -- test string literal or variable containing 'unsafe'
unsafe {
let val = *ptr as *const u8;
std::mem::transmute::<u8, u8>(*ptr)
}
}
",
)
.unwrap();
let config = HuntConfig {
mode: HuntMode::Fuzz,
targets: vec![PathBuf::from("src")],
min_suspiciousness: 0.0,
..Default::default()
};
let mut result = HuntResult::new(&temp, HuntMode::Fuzz, config.clone());
run_fuzz_mode(&temp, &config, &mut result);
let has_ptr = result.findings.iter().any(|f| f.title.contains("Pointer"));
let has_transmute = result
.findings
.iter()
.any(|f| f.title.contains("Transmute"));
assert!(has_ptr, "Should detect pointer dereference in unsafe block");
assert!(has_transmute, "Should detect transmute in unsafe block");
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_062_hunt_coverage_weight_no_file() {
let temp = std::env::temp_dir().join("test_bh_mod_062_covweight");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(temp.join("src/lib.rs"), "pub fn f() {}\n").unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![PathBuf::from("src")],
coverage_weight: 1.0,
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt(&temp, config);
assert_eq!(result.mode, HuntMode::Quick);
let _ = std::fs::remove_dir_all(&temp);
}
#[test]
fn test_bh_mod_063_hunt_with_spec_config_empty_targets() {
let temp = std::env::temp_dir().join("test_bh_mod_063_emptytgt");
let _ = std::fs::remove_dir_all(&temp);
let _ = std::fs::create_dir_all(temp.join("src"));
std::fs::write(temp.join("src/lib.rs"), "pub fn z() {}\n").unwrap();
let spec_content = "# Spec\n## S1\n### C-01: Claim\nSome claim.\n";
let spec_path = temp.join("spec.md");
std::fs::write(&spec_path, spec_content).unwrap();
let config = HuntConfig {
mode: HuntMode::Quick,
targets: vec![],
min_suspiciousness: 0.99,
..Default::default()
};
let result = hunt_with_spec(&temp, &spec_path, None, config);
assert!(result.is_ok());
let _ = std::fs::remove_dir_all(&temp);
}