#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod cli_dead_code_handler_tests {
use super::super::*;
use crate::services::cargo_dead_code_analyzer::CargoDeadCodeAnalyzer;
use std::path::PathBuf;
use tempfile::TempDir;
use std::fs;
#[tokio::test]
async fn test_cli_handler_uses_cargo_analyzer() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_test_rust_project(project_path);
let result = handle_analyze_dead_code_with_cargo(
project_path.to_path_buf(),
DeadCodeOutputFormat::Json,
None,
false,
5,
false,
None,
false,
100.0,
60,
8, ).await;
assert!(result.is_ok(), "Handler should succeed");
}
#[tokio::test]
async fn test_dead_code_percentage_accuracy() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_rust_project_no_dead_code(project_path);
let analyzer = CargoDeadCodeAnalyzer::new(project_path);
let report = analyzer.analyze().await.unwrap();
assert!(
report.dead_code_percentage < 1.0,
"Dead code should be <1% for project with no unused code, got {}%",
report.dead_code_percentage
);
}
#[tokio::test]
async fn test_public_api_not_dead() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_library_project(project_path);
let analyzer = CargoDeadCodeAnalyzer::new(project_path);
let report = analyzer.analyze().await.unwrap();
for file in &report.files_with_dead_code {
for item in &file.dead_items {
assert!(
!item.name.starts_with("pub"),
"Public item {} should not be marked as dead",
item.name
);
}
}
}
#[tokio::test]
async fn test_cli_output_formats() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_test_rust_project(project_path);
let json_result = handle_analyze_dead_code_with_cargo(
project_path.to_path_buf(),
DeadCodeOutputFormat::Json,
None,
false,
5,
false,
None,
false,
100.0,
60,
8, ).await;
assert!(json_result.is_ok());
let human_result = handle_analyze_dead_code_with_cargo(
project_path.to_path_buf(),
DeadCodeOutputFormat::Human,
None,
false,
5,
false,
None,
false,
100.0,
60,
8, ).await;
assert!(human_result.is_ok());
}
#[tokio::test]
async fn test_handler_timeout() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path();
create_large_project(project_path);
let result = handle_analyze_dead_code_with_cargo(
project_path.to_path_buf(),
DeadCodeOutputFormat::Json,
None,
false,
5,
false,
None,
false,
100.0,
1, 8, ).await;
assert!(result.is_ok() || result.is_err());
}
fn create_test_rust_project(path: &std::path::Path) {
let src_dir = path.join("src");
fs::create_dir_all(&src_dir).unwrap();
fs::write(src_dir.join("main.rs"), r#"
fn unused_helper() -> i32 {
42
}
fn used_function() -> i32 {
100
}
fn main() {
println!("Result: {}", used_function());
}
"#).unwrap();
fs::write(path.join("Cargo.toml"), r#"
[package]
name = "test_project"
version = "0.1.0"
edition = "2021"
"#).unwrap();
}
fn create_rust_project_no_dead_code(path: &std::path::Path) {
let src_dir = path.join("src");
fs::create_dir_all(&src_dir).unwrap();
fs::write(src_dir.join("main.rs"), r#"
fn helper() -> i32 {
42
}
fn calculate() -> i32 {
helper() * 2
}
fn main() {
println!("Result: {}", calculate());
}
"#).unwrap();
fs::write(path.join("Cargo.toml"), r#"
[package]
name = "test_project"
version = "0.1.0"
edition = "2021"
"#).unwrap();
}
fn create_library_project(path: &std::path::Path) {
let src_dir = path.join("src");
fs::create_dir_all(&src_dir).unwrap();
fs::write(src_dir.join("lib.rs"), r#"
/// Public API function
pub fn public_api() -> String {
"Public".to_string()
}
/// Another public function
pub fn another_api() -> i32 {
42
}
// Private unused function
fn private_unused() -> i32 {
100
}
"#).unwrap();
fs::write(path.join("Cargo.toml"), r#"
[package]
name = "test_library"
version = "0.1.0"
edition = "2021"
[lib]
name = "test_library"
"#).unwrap();
}
fn create_large_project(path: &std::path::Path) {
let src_dir = path.join("src");
fs::create_dir_all(&src_dir).unwrap();
for i in 0..20 {
fs::write(src_dir.join(format!("mod{}.rs", i)), format!(r#"
pub fn func_{}() -> i32 {{
{}
}}
"#, i, i)).unwrap();
}
fs::write(src_dir.join("main.rs"), r#"
fn main() {
println!("Large project");
}
"#).unwrap();
fs::write(path.join("Cargo.toml"), r#"
[package]
name = "large_project"
version = "0.1.0"
edition = "2021"
"#).unwrap();
}
}
async fn handle_analyze_dead_code_with_cargo(
path: PathBuf,
_format: DeadCodeOutputFormat,
_top_files: Option<usize>,
_include_unreachable: bool,
_min_dead_lines: usize,
_include_tests: bool,
_output: Option<PathBuf>,
_fail_on_violation: bool,
_max_percentage: f64,
_timeout: u64,
max_depth: usize,
) -> Result<(), anyhow::Error> {
use crate::services::cargo_dead_code_analyzer::CargoDeadCodeAnalyzer;
let analyzer = CargoDeadCodeAnalyzer::new(&path).with_max_depth(max_depth);
let report = analyzer.analyze().await?;
println!("Dead code: {:.2}%", report.dead_code_percentage);
Ok(())
}
#[derive(Debug, Clone)]
enum DeadCodeOutputFormat {
Json,
Human,
Yaml,
}