#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_default_config() {
let config = DeepContextConfig::default();
assert!(config.entry_points.is_empty());
assert!((config.dead_code_threshold - 0.15).abs() < f64::EPSILON);
assert!(!config.include_tests);
assert!(!config.include_benches);
assert!(config.cross_language_detection);
}
#[test]
fn test_default_config_validation() {
let config = DeepContextConfig::default();
let result = config.validate();
if let Err(errors) = result {
assert!(errors.iter().any(|e| e.contains("No entry points")));
}
}
#[test]
fn test_complexity_thresholds_default() {
let thresholds = ComplexityThresholds::default();
assert_eq!(thresholds.cyclomatic_warning, 10);
assert_eq!(thresholds.cyclomatic_error, 20);
assert_eq!(thresholds.cognitive_warning, 15);
assert_eq!(thresholds.cognitive_error, 30);
}
#[test]
fn test_complexity_thresholds_custom() {
let thresholds = ComplexityThresholds {
cyclomatic_warning: 5,
cyclomatic_error: 15,
cognitive_warning: 8,
cognitive_error: 20,
};
assert_eq!(thresholds.cyclomatic_warning, 5);
assert_eq!(thresholds.cyclomatic_error, 15);
assert_eq!(thresholds.cognitive_warning, 8);
assert_eq!(thresholds.cognitive_error, 20);
}
#[test]
fn test_complexity_thresholds_clone() {
let thresholds = ComplexityThresholds::default();
let cloned = thresholds.clone();
assert_eq!(cloned.cyclomatic_warning, thresholds.cyclomatic_warning);
assert_eq!(cloned.cyclomatic_error, thresholds.cyclomatic_error);
}
#[test]
fn test_complexity_thresholds_debug() {
let thresholds = ComplexityThresholds::default();
let debug = format!("{:?}", thresholds);
assert!(debug.contains("ComplexityThresholds"));
assert!(debug.contains("cyclomatic_warning: 10"));
}
#[test]
fn test_entry_point_validation() {
let mut config = DeepContextConfig {
entry_points: vec!["main".to_string()],
..Default::default()
};
assert!(config.validate().is_ok());
config.entry_points = vec!["lib".to_string()];
assert!(config.validate().is_ok());
config.entry_points = vec!["bin/pmat".to_string()];
assert!(config.validate().is_ok());
config.entry_points = vec!["custom_entry".to_string()];
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err()[0].contains("No standard entry point"));
}
#[test]
fn test_entry_point_validation_wasm_bindgen() {
let config = DeepContextConfig {
entry_points: vec!["my_func::wasm_bindgen_export".to_string()],
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_entry_point_validation_no_mangle() {
let config = DeepContextConfig {
entry_points: vec!["ffi::no_mangle_export".to_string()],
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_entry_point_validation_module_main() {
let config = DeepContextConfig {
entry_points: vec!["mymod::main".to_string()],
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_threshold_validation() {
let mut config = DeepContextConfig {
entry_points: vec!["main".to_string()],
dead_code_threshold: -0.1,
..Default::default()
};
assert!(config.validate().is_err());
config.dead_code_threshold = 1.5;
assert!(config.validate().is_err());
config.dead_code_threshold = 0.5;
assert!(config.validate().is_ok());
config.complexity_thresholds.cyclomatic_warning = 20;
config.complexity_thresholds.cyclomatic_error = 10;
assert!(config.validate().is_err());
}
#[test]
fn test_threshold_validation_boundary() {
let config = DeepContextConfig {
entry_points: vec!["main".to_string()],
dead_code_threshold: 0.0,
..Default::default()
};
assert!(config.validate().is_ok());
let config2 = DeepContextConfig {
entry_points: vec!["main".to_string()],
dead_code_threshold: 1.0,
..Default::default()
};
assert!(config2.validate().is_ok());
}
#[test]
fn test_cognitive_threshold_validation() {
let mut config = DeepContextConfig {
entry_points: vec!["main".to_string()],
..Default::default()
};
config.complexity_thresholds.cognitive_warning = 30;
config.complexity_thresholds.cognitive_error = 30;
let result = config.validate();
assert!(result.is_err());
assert!(result
.unwrap_err()
.iter()
.any(|e| e.contains("Cognitive warning threshold")));
}
#[test]
fn test_multiple_validation_errors() {
let config = DeepContextConfig {
entry_points: vec!["custom".to_string()],
dead_code_threshold: 2.0,
complexity_thresholds: ComplexityThresholds {
cyclomatic_warning: 20,
cyclomatic_error: 10,
cognitive_warning: 30,
cognitive_error: 15,
},
..Default::default()
};
let result = config.validate();
assert!(result.is_err());
let errors = result.unwrap_err();
assert!(errors.len() >= 3);
}
#[test]
#[ignore = "Flaky: depends on current working directory"]
fn test_entry_point_detection() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir(&src_dir).unwrap();
fs::write(src_dir.join("main.rs"), "fn main() {}").unwrap();
fs::write(src_dir.join("lib.rs"), "pub fn lib_func() {}").unwrap();
let bin_dir = src_dir.join("bin");
fs::create_dir(&bin_dir).unwrap();
fs::write(bin_dir.join("pmat.rs"), "fn main() {}").unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&temp_dir).unwrap();
let config = DeepContextConfig::default();
let detected = config.detect_entry_points();
std::env::set_current_dir(original_dir).unwrap();
assert!(detected.contains(&"main".to_string()));
assert!(detected.contains(&"lib".to_string()));
assert!(detected.contains(&"bin/pmat".to_string()));
}
#[test]
#[ignore] fn test_entry_point_detection_empty() {
let temp_dir = TempDir::new().unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&temp_dir).unwrap();
let config = DeepContextConfig::default();
let detected = config.detect_entry_points();
std::env::set_current_dir(original_dir).unwrap();
assert!(detected.is_empty());
}
#[test]
#[ignore] fn test_merge_with_detected_empty_entry_points() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir(&src_dir).unwrap();
fs::write(src_dir.join("main.rs"), "fn main() {}").unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&temp_dir).unwrap();
let mut config = DeepContextConfig::default();
assert!(config.entry_points.is_empty());
config.merge_with_detected();
std::env::set_current_dir(original_dir).unwrap();
assert!(config.entry_points.contains(&"main".to_string()));
}
#[test]
#[ignore] fn test_merge_with_detected_no_duplicates() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir(&src_dir).unwrap();
fs::write(src_dir.join("main.rs"), "fn main() {}").unwrap();
fs::write(src_dir.join("lib.rs"), "pub fn lib_func() {}").unwrap();
let original_dir = std::env::current_dir().unwrap();
struct DirGuard(std::path::PathBuf);
impl Drop for DirGuard {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.0);
}
}
let _guard = DirGuard(original_dir);
if std::env::set_current_dir(&temp_dir).is_err() {
eprintln!("Skipping: cannot change to temp dir");
return;
}
let mut config = DeepContextConfig {
entry_points: vec!["main".to_string()],
..Default::default()
};
config.merge_with_detected();
assert!(config.entry_points.contains(&"main".to_string()));
assert!(config.entry_points.contains(&"lib".to_string()));
let main_count = config.entry_points.iter().filter(|&e| e == "main").count();
assert_eq!(main_count, 1);
}
#[test]
fn test_config_serialization() {
let config = DeepContextConfig {
entry_points: vec!["main".to_string(), "lib".to_string()],
dead_code_threshold: 0.1,
complexity_thresholds: ComplexityThresholds {
cyclomatic_warning: 8,
cyclomatic_error: 15,
cognitive_warning: 12,
cognitive_error: 25,
},
include_tests: true,
include_benches: false,
cross_language_detection: true,
};
let toml_str = toml::to_string(&config).unwrap();
let deserialized: DeepContextConfig = toml::from_str(&toml_str).unwrap();
assert_eq!(config.entry_points, deserialized.entry_points);
assert_eq!(config.dead_code_threshold, deserialized.dead_code_threshold);
assert_eq!(config.include_tests, deserialized.include_tests);
}
#[test]
fn test_config_deserialization_with_defaults() {
let toml_str = r#"
entry_points = ["main"]
"#;
let config: DeepContextConfig = toml::from_str(toml_str).unwrap();
assert_eq!(config.entry_points, vec!["main"]);
assert!((config.dead_code_threshold - 0.15).abs() < f64::EPSILON);
assert_eq!(config.complexity_thresholds.cyclomatic_warning, 10);
assert_eq!(config.complexity_thresholds.cyclomatic_error, 20);
}
#[test]
fn test_config_json_serialization() {
let config = DeepContextConfig::default();
let json = serde_json::to_string(&config).unwrap();
let deserialized: DeepContextConfig = serde_json::from_str(&json).unwrap();
assert_eq!(config.dead_code_threshold, deserialized.dead_code_threshold);
}
#[test]
#[ignore] fn test_save_and_load_from_file() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("deep_context.toml");
let original = DeepContextConfig {
entry_points: vec!["main".to_string()],
dead_code_threshold: 0.2,
complexity_thresholds: ComplexityThresholds {
cyclomatic_warning: 5,
cyclomatic_error: 15,
cognitive_warning: 10,
cognitive_error: 25,
},
include_tests: true,
include_benches: true,
cross_language_detection: false,
};
original.save_to_file(&config_path).unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&temp_dir).unwrap();
let loaded = DeepContextConfig::load_from_file(&config_path).unwrap();
std::env::set_current_dir(original_dir).unwrap();
assert_eq!(original.dead_code_threshold, loaded.dead_code_threshold);
assert_eq!(original.include_tests, loaded.include_tests);
assert_eq!(original.include_benches, loaded.include_benches);
}
#[test]
fn test_load_from_nonexistent_file() {
let result = DeepContextConfig::load_from_file(Path::new("/nonexistent/path.toml"));
assert!(result.is_err());
}
#[test]
fn test_load_from_invalid_toml() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("invalid.toml");
fs::write(&config_path, "this is not: [valid: toml").unwrap();
let result = DeepContextConfig::load_from_file(&config_path);
assert!(result.is_err());
}
#[test]
fn test_load_from_file_with_validation_error() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join("invalid_config.toml");
let config_content = r#"
entry_points = ["custom_entry"]
dead_code_threshold = 2.0
"#;
fs::write(&config_path, config_content).unwrap();
let result = DeepContextConfig::load_from_file(&config_path);
assert!(result.is_err());
}
#[test]
fn test_deep_context_config_clone() {
let config = DeepContextConfig {
entry_points: vec!["main".to_string()],
dead_code_threshold: 0.3,
include_tests: true,
..Default::default()
};
let cloned = config.clone();
assert_eq!(cloned.entry_points, config.entry_points);
assert_eq!(cloned.dead_code_threshold, config.dead_code_threshold);
assert_eq!(cloned.include_tests, config.include_tests);
}
#[test]
fn test_deep_context_config_debug() {
let config = DeepContextConfig::default();
let debug = format!("{:?}", config);
assert!(debug.contains("DeepContextConfig"));
assert!(debug.contains("dead_code_threshold"));
assert!(debug.contains("complexity_thresholds"));
}
#[test]
fn test_default_functions() {
assert!((default_dead_code_threshold() - 0.15).abs() < f64::EPSILON);
assert_eq!(default_cyclomatic_warning(), 10);
assert_eq!(default_cyclomatic_error(), 20);
assert_eq!(default_cognitive_warning(), 15);
assert_eq!(default_cognitive_error(), 30);
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}