#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod dead_code_format_tests {
use super::*;
use crate::models::dead_code::{
ConfidenceLevel, DeadCodeItem, DeadCodeResult, DeadCodeSummary, DeadCodeType,
FileDeadCodeMetrics,
};
fn create_test_dead_code_result_with_items() -> DeadCodeResult {
DeadCodeResult {
summary: DeadCodeSummary {
total_files_analyzed: 10,
files_with_dead_code: 3,
total_dead_lines: 150,
dead_percentage: 15.0,
dead_functions: 8,
dead_classes: 2,
dead_modules: 1,
unreachable_blocks: 3,
},
files: vec![
FileDeadCodeMetrics {
path: "src/module_a.rs".to_string(),
dead_lines: 50,
total_lines: 200,
dead_percentage: 25.0,
dead_functions: 3,
dead_classes: 1,
dead_modules: 0,
unreachable_blocks: 1,
dead_score: 35.5,
confidence: ConfidenceLevel::High,
items: vec![
DeadCodeItem {
name: "unused_helper".to_string(),
item_type: DeadCodeType::Function,
line: 42,
reason: "Never called".to_string(),
},
DeadCodeItem {
name: "OldClass".to_string(),
item_type: DeadCodeType::Class,
line: 100,
reason: "Never instantiated".to_string(),
},
],
},
FileDeadCodeMetrics {
path: "src/module_b.rs".to_string(),
dead_lines: 60,
total_lines: 300,
dead_percentage: 20.0,
dead_functions: 4,
dead_classes: 0,
dead_modules: 1,
unreachable_blocks: 2,
dead_score: 28.0,
confidence: ConfidenceLevel::Medium,
items: vec![DeadCodeItem {
name: "legacy_code".to_string(),
item_type: DeadCodeType::UnreachableCode,
line: 50,
reason: "Unreachable after return".to_string(),
}],
},
FileDeadCodeMetrics {
path: "src/module_c.rs".to_string(),
dead_lines: 40,
total_lines: 250,
dead_percentage: 16.0,
dead_functions: 1,
dead_classes: 1,
dead_modules: 0,
unreachable_blocks: 0,
dead_score: 18.0,
confidence: ConfidenceLevel::Low,
items: vec![DeadCodeItem {
name: "dynamic_var".to_string(),
item_type: DeadCodeType::Variable,
line: 25,
reason: "Possibly unused".to_string(),
}],
},
],
total_files: 10,
analyzed_files: 10,
}
}
#[test]
fn test_format_dead_code_as_json() {
let result = create_test_dead_code_result_with_items();
let json_output = format_dead_code_as_json(&result).expect("JSON formatting should work");
let parsed: serde_json::Value =
serde_json::from_str(&json_output).expect("Should be valid JSON");
assert!(parsed.get("summary").is_some());
assert!(parsed.get("files").is_some());
assert!(parsed.get("total_files").is_some());
let summary = parsed.get("summary").unwrap();
assert_eq!(summary.get("total_files_analyzed").unwrap(), 10);
assert_eq!(summary.get("files_with_dead_code").unwrap(), 3);
assert_eq!(summary.get("dead_functions").unwrap(), 8);
}
#[test]
fn test_format_dead_code_as_sarif() {
let result = create_test_dead_code_result_with_items();
let sarif_output =
format_dead_code_as_sarif(&result).expect("SARIF formatting should work");
let parsed: serde_json::Value =
serde_json::from_str(&sarif_output).expect("Should be valid JSON");
assert_eq!(parsed.get("version").unwrap(), "2.1.0");
let runs = parsed.get("runs").unwrap().as_array().unwrap();
assert!(!runs.is_empty());
let tool = runs[0].get("tool").unwrap();
let driver = tool.get("driver").unwrap();
assert_eq!(driver.get("name").unwrap(), "pmat");
let rules = driver.get("rules").unwrap().as_array().unwrap();
assert!(!rules.is_empty());
assert_eq!(rules[0].get("id").unwrap(), "dead-code");
}
#[test]
fn test_format_dead_code_as_markdown() {
let result = create_test_dead_code_result_with_items();
let markdown_output =
format_dead_code_as_markdown(&result).expect("Markdown formatting should work");
assert!(markdown_output.contains("# Dead Code Analysis Report"));
assert!(markdown_output.contains("## Summary"));
assert!(markdown_output.contains("## Dead Code Breakdown"));
assert!(markdown_output.contains("## File Details"));
assert!(markdown_output.contains("## Recommendations"));
assert!(markdown_output.contains("Files Analyzed | 10"));
assert!(markdown_output.contains("Files with Dead Code | 3"));
assert!(markdown_output.contains("src/module_a.rs"));
}
#[test]
fn test_format_dead_code_file_details_section() {
let files = vec![
FileDeadCodeMetrics {
path: "src/test.rs".to_string(),
dead_lines: 30,
total_lines: 100,
dead_percentage: 30.0,
dead_functions: 2,
dead_classes: 1,
dead_modules: 0,
unreachable_blocks: 0,
dead_score: 25.0,
confidence: ConfidenceLevel::High,
items: vec![
DeadCodeItem {
name: "fn1".to_string(),
item_type: DeadCodeType::Function,
line: 10,
reason: "unused".to_string(),
},
DeadCodeItem {
name: "fn2".to_string(),
item_type: DeadCodeType::Function,
line: 20,
reason: "unused".to_string(),
},
],
},
];
let section = format_dead_code_file_details_section(&files);
assert!(section.contains("## File Details"));
assert!(section.contains("| File | Dead % | Dead Lines | Confidence | Items |"));
assert!(section.contains("src/test.rs"));
assert!(section.contains("30.0%"));
assert!(section.contains("High"));
assert!(section.contains("| 2 |")); }
#[test]
fn test_format_dead_code_header() {
let result = create_test_dead_code_result_with_items();
let mut output = String::new();
write_dead_code_header(&mut output, &result).expect("Header writing should work");
assert!(output.contains("# Dead Code Analysis Summary"));
assert!(output.contains("**Files analyzed**: 10"));
assert!(output.contains("**Files with dead code**: 3"));
assert!(output.contains("**Total dead lines**: 150"));
assert!(output.contains("**Dead code percentage**: 15.00%"));
}
#[test]
fn test_format_dead_code_sarif_levels() {
let result = DeadCodeResult {
summary: DeadCodeSummary {
total_files_analyzed: 3,
files_with_dead_code: 3,
total_dead_lines: 30,
dead_percentage: 10.0,
dead_functions: 3,
dead_classes: 0,
dead_modules: 0,
unreachable_blocks: 0,
},
files: vec![
FileDeadCodeMetrics {
path: "high.rs".to_string(),
dead_lines: 10,
total_lines: 100,
dead_percentage: 10.0,
dead_functions: 1,
dead_classes: 0,
dead_modules: 0,
unreachable_blocks: 0,
dead_score: 10.0,
confidence: ConfidenceLevel::High,
items: vec![DeadCodeItem {
name: "fn_high".to_string(),
item_type: DeadCodeType::Function,
line: 5,
reason: "unused".to_string(),
}],
},
FileDeadCodeMetrics {
path: "medium.rs".to_string(),
dead_lines: 10,
total_lines: 100,
dead_percentage: 10.0,
dead_functions: 1,
dead_classes: 0,
dead_modules: 0,
unreachable_blocks: 0,
dead_score: 10.0,
confidence: ConfidenceLevel::Medium,
items: vec![DeadCodeItem {
name: "fn_medium".to_string(),
item_type: DeadCodeType::Function,
line: 5,
reason: "unused".to_string(),
}],
},
FileDeadCodeMetrics {
path: "low.rs".to_string(),
dead_lines: 10,
total_lines: 100,
dead_percentage: 10.0,
dead_functions: 1,
dead_classes: 0,
dead_modules: 0,
unreachable_blocks: 0,
dead_score: 10.0,
confidence: ConfidenceLevel::Low,
items: vec![DeadCodeItem {
name: "fn_low".to_string(),
item_type: DeadCodeType::Function,
line: 5,
reason: "unused".to_string(),
}],
},
],
total_files: 3,
analyzed_files: 3,
};
let sarif_output = format_dead_code_as_sarif(&result).expect("SARIF should work");
let parsed: serde_json::Value = serde_json::from_str(&sarif_output).unwrap();
let results = parsed["runs"][0]["results"].as_array().unwrap();
assert_eq!(results.len(), 3);
let levels: Vec<&str> = results
.iter()
.map(|r| r["level"].as_str().unwrap())
.collect();
assert!(levels.contains(&"error")); assert!(levels.contains(&"warning")); assert!(levels.contains(&"note")); }
}