use decy_core::DependencyGraph;
use std::path::PathBuf;
use tempfile::TempDir;
fn create_temp_c_file(dir: &TempDir, name: &str, content: &str) -> PathBuf {
let path = dir.path().join(name);
std::fs::write(&path, content).expect("Failed to write temp file");
path
}
#[test]
fn test_dependency_graph_empty() {
let graph = DependencyGraph::new();
assert_eq!(graph.file_count(), 0, "Empty graph should have 0 files");
assert!(graph.is_empty(), "Empty graph should report as empty");
}
#[test]
fn test_dependency_graph_add_file() {
let mut graph = DependencyGraph::new();
let path = PathBuf::from("/tmp/test.c");
graph.add_file(&path);
assert_eq!(graph.file_count(), 1, "Should have 1 file");
assert!(graph.contains_file(&path), "Should contain the added file");
}
#[test]
fn test_dependency_graph_add_dependency() {
let mut graph = DependencyGraph::new();
let main_c = PathBuf::from("/tmp/main.c");
let utils_h = PathBuf::from("/tmp/utils.h");
graph.add_file(&main_c);
graph.add_file(&utils_h);
graph.add_dependency(&main_c, &utils_h);
assert!(graph.has_dependency(&main_c, &utils_h), "Should have dependency");
assert!(!graph.has_dependency(&utils_h, &main_c), "Dependency is directional");
}
#[test]
fn test_build_dependency_graph_from_files() {
let temp = TempDir::new().unwrap();
let utils_h = create_temp_c_file(&temp, "utils.h", "int helper(int x);");
let main_c = create_temp_c_file(
&temp,
"main.c",
r#"#include "utils.h"
int main() { return helper(5); }"#,
);
let files = vec![main_c.clone(), utils_h.clone()];
let graph = DependencyGraph::from_files(&files).expect("Should build graph");
assert_eq!(graph.file_count(), 2, "Should have 2 files");
assert!(graph.has_dependency(&main_c, &utils_h), "main.c should depend on utils.h");
}
#[test]
fn test_topological_sort_simple() {
let mut graph = DependencyGraph::new();
let main_c = PathBuf::from("main.c");
let utils_h = PathBuf::from("utils.h");
graph.add_file(&main_c);
graph.add_file(&utils_h);
graph.add_dependency(&main_c, &utils_h);
let build_order = graph.topological_sort().expect("Should compute build order");
assert_eq!(build_order.len(), 2, "Should have 2 files in order");
let utils_pos = build_order.iter().position(|p| p == &utils_h).unwrap();
let main_pos = build_order.iter().position(|p| p == &main_c).unwrap();
assert!(utils_pos < main_pos, "utils.h should be transpiled before main.c");
}
#[test]
fn test_topological_sort_complex() {
let mut graph = DependencyGraph::new();
let a = PathBuf::from("a.h");
let b = PathBuf::from("b.h");
let c = PathBuf::from("c.h");
graph.add_file(&a);
graph.add_file(&b);
graph.add_file(&c);
graph.add_dependency(&a, &b);
graph.add_dependency(&b, &c);
let build_order = graph.topological_sort().expect("Should compute build order");
let c_pos = build_order.iter().position(|p| p == &c).unwrap();
let b_pos = build_order.iter().position(|p| p == &b).unwrap();
let a_pos = build_order.iter().position(|p| p == &a).unwrap();
assert!(c_pos < b_pos, "c should come before b");
assert!(b_pos < a_pos, "b should come before a");
}
#[test]
fn test_detect_circular_dependency() {
let mut graph = DependencyGraph::new();
let a = PathBuf::from("a.h");
let b = PathBuf::from("b.h");
graph.add_file(&a);
graph.add_file(&b);
graph.add_dependency(&a, &b);
graph.add_dependency(&b, &a);
let result = graph.topological_sort();
assert!(result.is_err(), "Should detect circular dependency");
let error_msg = result.unwrap_err().to_string();
let error_msg_lower = error_msg.to_lowercase();
assert!(
error_msg_lower.contains("circular") || error_msg_lower.contains("cycle"),
"Error should mention circular dependency"
);
}
#[test]
fn test_detect_self_dependency() {
let mut graph = DependencyGraph::new();
let a = PathBuf::from("a.h");
graph.add_file(&a);
graph.add_dependency(&a, &a);
let result = graph.topological_sort();
assert!(result.is_err(), "Should detect self-dependency as circular");
}
#[test]
fn test_header_guard_detection() {
let temp = TempDir::new().unwrap();
let header = create_temp_c_file(
&temp,
"config.h",
r#"#ifndef CONFIG_H
#define CONFIG_H
int MAX_SIZE = 100;
#endif"#,
);
let has_guard = DependencyGraph::has_header_guard(&header).expect("Should check guard");
assert!(has_guard, "Should detect header guard");
}
#[test]
fn test_parse_include_directive() {
let includes =
[r#"#include "utils.h""#, r#"#include <stdio.h>"#, r#" #include "config.h" "#];
let parsed = DependencyGraph::parse_include_directives(&includes.join("\n"));
assert_eq!(parsed.len(), 3, "Should find 3 includes");
assert!(parsed.contains(&"utils.h".to_string()));
assert!(parsed.contains(&"stdio.h".to_string()));
assert!(parsed.contains(&"config.h".to_string()));
}
#[test]
fn test_build_order_integration() {
let temp = TempDir::new().unwrap();
let types_h = create_temp_c_file(&temp, "types.h", "typedef struct { int x; } Point;");
let utils_h = create_temp_c_file(
&temp,
"utils.h",
r#"#include "types.h"
Point create_point(int x);"#,
);
let main_c = create_temp_c_file(
&temp,
"main.c",
r#"#include "utils.h"
int main() { return 0; }"#,
);
let files = vec![main_c.clone(), utils_h.clone(), types_h.clone()];
let graph = DependencyGraph::from_files(&files).expect("Should build graph");
let build_order = graph.topological_sort().expect("Should compute order");
let types_pos = build_order.iter().position(|p| p == &types_h).unwrap();
let utils_pos = build_order.iter().position(|p| p == &utils_h).unwrap();
let main_pos = build_order.iter().position(|p| p == &main_c).unwrap();
assert!(types_pos < utils_pos, "types.h before utils.h");
assert!(utils_pos < main_pos, "utils.h before main.c");
}