use decy_core::{ProjectContext, TranspilationCache};
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_cache_stores_transpiled_file() {
let temp = TempDir::new().unwrap();
let c_file = create_temp_c_file(&temp, "test.c", "int add(int a, int b) { return a + b; }");
let mut cache = TranspilationCache::new();
let context = ProjectContext::new();
let transpiled = decy_core::transpile_file(&c_file, &context).expect("Should transpile");
cache.insert(&c_file, &transpiled);
let cached = cache.get(&c_file);
assert!(cached.is_some(), "Should find cached file");
let cached_file = cached.unwrap();
assert_eq!(cached_file.source_path, transpiled.source_path);
assert_eq!(cached_file.rust_code, transpiled.rust_code);
}
#[test]
fn test_cache_hit_on_unchanged_file() {
let temp = TempDir::new().unwrap();
let c_file = create_temp_c_file(
&temp,
"test.c",
"int multiply(int x, int y) { return x * y; }",
);
let mut cache = TranspilationCache::new();
let context = ProjectContext::new();
let transpiled1 = decy_core::transpile_file(&c_file, &context).expect("Should transpile");
cache.insert(&c_file, &transpiled1);
let cached = cache.get(&c_file);
assert!(cached.is_some(), "Should hit cache");
let cached_file = cached.unwrap();
assert_eq!(cached_file.rust_code, transpiled1.rust_code);
}
#[test]
fn test_cache_miss_on_changed_file() {
let temp = TempDir::new().unwrap();
let c_file = create_temp_c_file(&temp, "test.c", "int foo() { return 1; }");
let mut cache = TranspilationCache::new();
let context = ProjectContext::new();
let transpiled1 = decy_core::transpile_file(&c_file, &context).expect("Should transpile");
cache.insert(&c_file, &transpiled1);
std::fs::write(&c_file, "int foo() { return 2; }").expect("Should write");
let cached = cache.get(&c_file);
assert!(
cached.is_none(),
"Should detect file change and invalidate cache"
);
}
#[test]
fn test_cache_invalidation_on_dependency_change() {
let temp = TempDir::new().unwrap();
let header = create_temp_c_file(&temp, "lib.h", "int helper();");
let impl_file = create_temp_c_file(&temp, "main.c", "int main() { return 0; }");
let mut cache = TranspilationCache::new();
let context = ProjectContext::new();
let mut transpiled = decy_core::transpile_file(&impl_file, &context).expect("Should transpile");
transpiled.dependencies = vec![header.clone()]; cache.insert(&impl_file, &transpiled);
let cached = cache.get(&impl_file);
assert!(
cached.is_some(),
"Should have cached entry before dependency change"
);
std::fs::write(&header, "int helper(); // changed").expect("Should write");
let cached = cache.get(&impl_file);
assert!(
cached.is_none(),
"Should invalidate cache when dependency changes"
);
}
#[test]
fn test_cache_persistence_to_disk() {
let temp = TempDir::new().unwrap();
let cache_dir = temp.path().join(".decy/cache");
let c_file = create_temp_c_file(&temp, "test.c", "int value() { return 42; }");
let context = ProjectContext::new();
{
let mut cache = TranspilationCache::with_directory(&cache_dir);
let transpiled = decy_core::transpile_file(&c_file, &context).expect("Should transpile");
cache.insert(&c_file, &transpiled);
cache.save().expect("Should save cache to disk");
}
{
let mut cache = TranspilationCache::load(&cache_dir).expect("Should load cache from disk");
let cached = cache.get(&c_file);
assert!(cached.is_some(), "Should load cached file from disk");
}
}
#[test]
fn test_cache_statistics() {
let temp = TempDir::new().unwrap();
let file1 = create_temp_c_file(&temp, "file1.c", "int a() { return 1; }");
let file2 = create_temp_c_file(&temp, "file2.c", "int b() { return 2; }");
let mut cache = TranspilationCache::new();
let context = ProjectContext::new();
let transpiled1 = decy_core::transpile_file(&file1, &context).expect("Should transpile");
cache.insert(&file1, &transpiled1);
let _ = cache.get(&file1);
let transpiled2 = decy_core::transpile_file(&file2, &context).expect("Should transpile");
cache.insert(&file2, &transpiled2);
let _ = cache.get(&file2);
let _ = cache.get(&file1);
let stats = cache.statistics();
assert_eq!(stats.hits, 3, "Should count 3 cache hits");
assert_eq!(
stats.misses, 0,
"Should count 0 misses (insert doesn't count)"
);
assert_eq!(stats.total_files, 2, "Should track 2 files");
}
#[test]
fn test_cache_sha256_hash_computation() {
let temp = TempDir::new().unwrap();
let c_file = create_temp_c_file(&temp, "test.c", "int hash_test() { return 1; }");
let cache = TranspilationCache::new();
let hash1 = cache.compute_hash(&c_file).expect("Should compute hash");
assert_eq!(hash1.len(), 64, "SHA-256 hash should be 64 hex characters");
let hash2 = cache.compute_hash(&c_file).expect("Should compute hash");
assert_eq!(hash1, hash2, "Hash should be deterministic");
std::fs::write(&c_file, "int hash_test() { return 2; }").expect("Should write");
let hash3 = cache.compute_hash(&c_file).expect("Should compute hash");
assert_ne!(hash1, hash3, "Hash should change when file content changes");
}
#[test]
fn test_cache_clears_all_entries() {
let temp = TempDir::new().unwrap();
let c_file = create_temp_c_file(&temp, "test.c", "int clear_test() { return 0; }");
let mut cache = TranspilationCache::new();
let context = ProjectContext::new();
let transpiled = decy_core::transpile_file(&c_file, &context).expect("Should transpile");
cache.insert(&c_file, &transpiled);
assert!(cache.get(&c_file).is_some(), "Should have cached entry");
cache.clear();
assert!(
cache.get(&c_file).is_none(),
"Cache should be empty after clear"
);
let stats = cache.statistics();
assert_eq!(stats.total_files, 0, "Should have 0 files after clear");
}
#[test]
fn test_cache_with_multiple_files() {
let temp = TempDir::new().unwrap();
let context = ProjectContext::new();
let mut cache = TranspilationCache::new();
let mut files = Vec::new();
for i in 0..10 {
let content = format!("int file{}() {{ return {}; }}", i, i);
let file = create_temp_c_file(&temp, &format!("file{}.c", i), &content);
let transpiled = decy_core::transpile_file(&file, &context).expect("Should transpile");
cache.insert(&file, &transpiled);
files.push(file);
}
for file in &files {
assert!(cache.get(file).is_some(), "Should find cached file");
}
let stats = cache.statistics();
assert_eq!(stats.total_files, 10, "Should track 10 files");
}