use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tempfile::TempDir;
#[derive(Clone, Debug, Default)]
pub struct PerformanceMetrics {
pub operation_times: HashMap<String, Vec<Duration>>,
pub memory_usage: HashMap<String, Vec<usize>>,
pub throughput: HashMap<String, Vec<f64>>,
}
impl PerformanceMetrics {
pub fn new() -> Self {
Self::default()
}
pub fn record(
&mut self,
operation: &str,
duration: Duration,
memory_kb: usize,
throughput_mbps: f64,
) {
self.operation_times
.entry(operation.to_string())
.or_default()
.push(duration);
self.memory_usage
.entry(operation.to_string())
.or_default()
.push(memory_kb);
self.throughput
.entry(operation.to_string())
.or_default()
.push(throughput_mbps);
}
pub fn avg_time(&self, operation: &str) -> Option<Duration> {
self.operation_times.get(operation).map(|times| {
let sum: Duration = times.iter().sum();
sum / times.len() as u32
})
}
pub fn avg_throughput(&self, operation: &str) -> Option<f64> {
self.throughput
.get(operation)
.map(|throughputs| throughputs.iter().sum::<f64>() / throughputs.len() as f64)
}
}
pub struct TestHarness {
temp_dir: TempDir,
metrics: Arc<Mutex<PerformanceMetrics>>,
}
impl TestHarness {
pub fn new() -> Self {
TestHarness {
temp_dir: TempDir::new().expect("Failed to create temp directory"),
metrics: Arc::new(Mutex::new(PerformanceMetrics::default())),
}
}
pub fn temp_dir(&self) -> &Path {
self.temp_dir.path()
}
pub fn record_metric(
&self,
operation: &str,
duration: Duration,
memory_kb: usize,
throughput_mbps: f64,
) {
let mut metrics = self.metrics.lock().unwrap();
metrics.record(operation, duration, memory_kb, throughput_mbps);
}
pub fn metrics(&self) -> PerformanceMetrics {
self.metrics.lock().unwrap().clone()
}
pub fn create_dataset(&self, size_mb: usize) -> PathBuf {
let dataset_dir = self.temp_dir.path().join(format!("dataset_{}mb", size_mb));
fs::create_dir_all(&dataset_dir).expect("Failed to create dataset directory");
let patterns: Vec<(&str, &str, Vec<u8>)> = vec![
(
"text",
"txt",
b"This is a text file with some content.\n".to_vec(),
),
(
"json",
"json",
br#"{"key": "value", "number": 42}"#.to_vec(),
),
("binary", "bin", (0..=255).collect::<Vec<u8>>()),
];
let mut total_size = 0;
let mut file_count = 0;
while total_size < size_mb * 1024 * 1024 {
for (content_type, ext, base_content) in &patterns {
let filename = format!("{}_{:04}.{}", content_type, file_count, ext);
let filepath = dataset_dir.join(&filename);
let multiplier = (file_count % 10) + 1;
let content = base_content.repeat(multiplier);
fs::write(&filepath, &content).expect("Failed to write test file");
total_size += content.len();
file_count += 1;
if total_size >= size_mb * 1024 * 1024 {
break;
}
}
}
dataset_dir
}
pub fn create_file(&self, name: &str, content: &[u8]) -> PathBuf {
let filepath = self.temp_dir.path().join(name);
fs::write(&filepath, content).expect("Failed to write test file");
filepath
}
pub fn create_directory_structure(&self, name: &str) -> PathBuf {
let base = self.temp_dir.path().join(name);
fs::create_dir_all(base.join("dir1")).unwrap();
fs::create_dir_all(base.join("dir2/nested")).unwrap();
fs::create_dir_all(base.join("empty_dir")).unwrap();
fs::write(base.join("file1.txt"), b"Hello, world!").unwrap();
fs::write(base.join("file2.log"), b"Log entry 1\nLog entry 2\n").unwrap();
fs::write(
base.join("dir1/file3.dat"),
b"Binary data: \x00\x01\x02\xFF",
)
.unwrap();
fs::write(
base.join("dir2/file4.md"),
b"# Markdown\n\n## Section\n\nContent here.",
)
.unwrap();
fs::write(
base.join("dir2/nested/file5.json"),
br#"{"key": "value", "number": 42}"#,
)
.unwrap();
base
}
pub fn create_large_file(
&self,
name: &str,
size_mb: usize,
pattern: crate::fixtures::TestDataPattern,
) -> PathBuf {
let filepath = self.temp_dir.path().join(name);
let data = crate::fixtures::create_test_data(size_mb, pattern);
fs::write(&filepath, data).expect("Failed to write large file");
filepath
}
}
impl Default for TestHarness {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_harness_creation() {
let harness = TestHarness::new();
assert!(harness.temp_dir().exists());
}
#[test]
fn test_create_file() {
let harness = TestHarness::new();
let path = harness.create_file("test.txt", b"hello");
assert!(path.exists());
assert_eq!(fs::read(&path).unwrap(), b"hello");
}
#[test]
fn test_metrics_recording() {
let harness = TestHarness::new();
harness.record_metric("test_op", Duration::from_millis(100), 1024, 10.0);
let metrics = harness.metrics();
assert_eq!(metrics.operation_times.get("test_op").unwrap().len(), 1);
}
#[test]
fn test_create_dataset() {
let harness = TestHarness::new();
let dataset = harness.create_dataset(1); assert!(dataset.exists());
let entries: Vec<_> = fs::read_dir(&dataset).unwrap().collect();
assert!(!entries.is_empty());
}
}