use std::time::Instant;
use tempfile::tempdir;
use synadb::{close_db, open_db, with_db, Atom, DbConfig, SynaDB};
const OPEN_CLOSE_ITERATIONS: usize = 100;
const TENSOR_ITERATIONS: usize = 1000;
const TENSOR_SIZE: usize = 10_000;
#[test]
fn test_open_close_no_leak() {
let dir = tempdir().expect("Failed to create temp dir");
println!("\n=== Memory Leak Test: Open/Close Cycles ===");
println!("Running {} open/close cycles...", OPEN_CLOSE_ITERATIONS);
let start = Instant::now();
for i in 0..OPEN_CLOSE_ITERATIONS {
let db_path = dir.path().join(format!("leak_test_{}.db", i % 10));
let path_str = db_path.to_str().unwrap();
open_db(path_str).expect("Failed to open database");
with_db(path_str, |db| {
for j in 0..100 {
let key = format!("key_{}", j);
db.append(&key, Atom::Float(j as f64))?;
}
Ok(())
})
.expect("Failed to write data");
close_db(path_str).expect("Failed to close database");
if (i + 1) % 20 == 0 {
println!(" Completed {} cycles", i + 1);
}
}
let duration = start.elapsed();
println!(
"Completed {} cycles in {:.2}s",
OPEN_CLOSE_ITERATIONS,
duration.as_secs_f64()
);
println!("=== Test Passed (no crash = no obvious leak) ===\n");
}
#[test]
fn test_tensor_alloc_free_no_leak() {
let dir = tempdir().expect("Failed to create temp dir");
let db_path = dir.path().join("tensor_leak_test.db");
let config = DbConfig {
enable_compression: false,
enable_delta: false,
sync_on_write: false,
};
let mut db = SynaDB::with_config(&db_path, config).expect("Failed to create database");
println!("\n=== Memory Leak Test: Tensor Alloc/Free Cycles ===");
println!(
"Writing {} float entries for tensor extraction...",
TENSOR_SIZE
);
let key = "tensor_data";
for i in 0..TENSOR_SIZE {
db.append(key, Atom::Float(i as f64 * 0.1))
.expect("Failed to append");
}
println!("Running {} tensor alloc/free cycles...", TENSOR_ITERATIONS);
let start = Instant::now();
for i in 0..TENSOR_ITERATIONS {
let (ptr, len) = db
.get_history_tensor(key)
.expect("Failed to get history tensor");
assert_eq!(len, TENSOR_SIZE, "Tensor length mismatch");
assert!(!ptr.is_null(), "Tensor pointer is null");
let sample = unsafe { *ptr.add(len / 2) };
assert!(
(sample - (len / 2) as f64 * 0.1).abs() < 1e-10,
"Sample value mismatch"
);
unsafe {
synadb::free_tensor(ptr, len);
}
if (i + 1) % 200 == 0 {
println!(" Completed {} cycles", i + 1);
}
}
let duration = start.elapsed();
println!(
"Completed {} cycles in {:.2}s ({:.0} alloc/free per sec)",
TENSOR_ITERATIONS,
duration.as_secs_f64(),
TENSOR_ITERATIONS as f64 / duration.as_secs_f64()
);
db.close().expect("Failed to close database");
println!("=== Test Passed (no crash = no obvious leak) ===\n");
}
#[test]
fn test_registry_management() {
let dir = tempdir().expect("Failed to create temp dir");
println!("\n=== Memory Leak Test: Registry Management ===");
println!("Testing registry with multiple databases...");
let num_dbs = 10;
let iterations = 50;
let start = Instant::now();
for iter in 0..iterations {
for i in 0..num_dbs {
let db_path = dir.path().join(format!("registry_test_{}.db", i));
let path_str = db_path.to_str().unwrap();
open_db(path_str).expect("Failed to open database");
with_db(path_str, |db| {
let key = format!("iter_{}_key", iter);
db.append(&key, Atom::Int(iter as i64))?;
Ok(())
})
.expect("Failed to write data");
}
for i in 0..num_dbs {
let db_path = dir.path().join(format!("registry_test_{}.db", i));
let path_str = db_path.to_str().unwrap();
close_db(path_str).expect("Failed to close database");
}
if (iter + 1) % 10 == 0 {
println!(" Completed {} iterations", iter + 1);
}
}
let duration = start.elapsed();
println!(
"Completed {} iterations with {} databases each in {:.2}s",
iterations,
num_dbs,
duration.as_secs_f64()
);
println!("=== Test Passed ===\n");
}
#[test]
fn test_history_no_leak() {
let dir = tempdir().expect("Failed to create temp dir");
let db_path = dir.path().join("history_leak_test.db");
let config = DbConfig {
enable_compression: false,
enable_delta: false,
sync_on_write: false,
};
let mut db = SynaDB::with_config(&db_path, config).expect("Failed to create database");
println!("\n=== Memory Leak Test: History Retrieval ===");
let key = "history_key";
let num_entries = 1000;
for i in 0..num_entries {
db.append(key, Atom::Text(format!("Entry number {}", i)))
.expect("Failed to append");
}
println!("Running {} history retrieval cycles...", TENSOR_ITERATIONS);
let start = Instant::now();
for i in 0..TENSOR_ITERATIONS {
let history = db.get_history(key).expect("Failed to get history");
assert_eq!(history.len(), num_entries, "History length mismatch");
if (i + 1) % 200 == 0 {
println!(" Completed {} cycles", i + 1);
}
}
let duration = start.elapsed();
println!(
"Completed {} cycles in {:.2}s",
TENSOR_ITERATIONS,
duration.as_secs_f64()
);
db.close().expect("Failed to close database");
println!("=== Test Passed ===\n");
}
#[test]
fn test_mixed_operations_stability() {
let dir = tempdir().expect("Failed to create temp dir");
let db_path = dir.path().join("mixed_stability_test.db");
let config = DbConfig {
enable_compression: true,
enable_delta: false,
sync_on_write: false,
};
let mut db = SynaDB::with_config(&db_path, config).expect("Failed to create database");
println!("\n=== Memory Leak Test: Mixed Operations ===");
let iterations = 100;
let ops_per_iter = 100;
println!(
"Running {} iterations with {} operations each...",
iterations, ops_per_iter
);
let start = Instant::now();
for iter in 0..iterations {
for j in 0..ops_per_iter {
let key = format!("key_{}_{}", iter, j);
match j % 4 {
0 => {
db.append(&key, Atom::Float(j as f64))
.expect("Failed to append float");
}
1 => {
db.append(&key, Atom::Int(j as i64))
.expect("Failed to append int");
}
2 => {
db.append(&key, Atom::Text(format!("Text value {}", j)))
.expect("Failed to append text");
}
_ => {
let bytes: Vec<u8> = (0..64).map(|k| ((j + k) % 256) as u8).collect();
db.append(&key, Atom::Bytes(bytes))
.expect("Failed to append bytes");
}
}
}
for j in 0..ops_per_iter / 10 {
let key = format!("key_{}_{}", iter, j * 10);
let _ = db.get(&key).expect("Failed to get");
}
for j in 0..ops_per_iter / 20 {
let key = format!("key_{}_{}", iter, j * 20);
let _ = db.delete(&key);
}
if (iter + 1) % 20 == 0 {
println!(" Completed {} iterations", iter + 1);
}
}
let duration = start.elapsed();
let total_ops = iterations * ops_per_iter;
println!(
"Completed {} total operations in {:.2}s ({:.0} ops/sec)",
total_ops,
duration.as_secs_f64(),
total_ops as f64 / duration.as_secs_f64()
);
let keys = db.keys();
println!("Database has {} active keys", keys.len());
db.close().expect("Failed to close database");
println!("=== Test Passed ===\n");
}