embeddenator_testkit/
harness.rs1use std::collections::HashMap;
10use std::fs;
11use std::path::{Path, PathBuf};
12use std::sync::{Arc, Mutex};
13use std::time::Duration;
14use tempfile::TempDir;
15
16#[derive(Clone, Debug, Default)]
18pub struct PerformanceMetrics {
19 pub operation_times: HashMap<String, Vec<Duration>>,
20 pub memory_usage: HashMap<String, Vec<usize>>,
21 pub throughput: HashMap<String, Vec<f64>>,
22}
23
24impl PerformanceMetrics {
25 pub fn new() -> Self {
26 Self::default()
27 }
28
29 pub fn record(
31 &mut self,
32 operation: &str,
33 duration: Duration,
34 memory_kb: usize,
35 throughput_mbps: f64,
36 ) {
37 self.operation_times
38 .entry(operation.to_string())
39 .or_default()
40 .push(duration);
41 self.memory_usage
42 .entry(operation.to_string())
43 .or_default()
44 .push(memory_kb);
45 self.throughput
46 .entry(operation.to_string())
47 .or_default()
48 .push(throughput_mbps);
49 }
50
51 pub fn avg_time(&self, operation: &str) -> Option<Duration> {
53 self.operation_times.get(operation).map(|times| {
54 let sum: Duration = times.iter().sum();
55 sum / times.len() as u32
56 })
57 }
58
59 pub fn avg_throughput(&self, operation: &str) -> Option<f64> {
61 self.throughput
62 .get(operation)
63 .map(|throughputs| throughputs.iter().sum::<f64>() / throughputs.len() as f64)
64 }
65}
66
67pub struct TestHarness {
72 temp_dir: TempDir,
73 metrics: Arc<Mutex<PerformanceMetrics>>,
74}
75
76impl TestHarness {
77 pub fn new() -> Self {
79 TestHarness {
80 temp_dir: TempDir::new().expect("Failed to create temp directory"),
81 metrics: Arc::new(Mutex::new(PerformanceMetrics::default())),
82 }
83 }
84
85 pub fn temp_dir(&self) -> &Path {
87 self.temp_dir.path()
88 }
89
90 pub fn record_metric(
92 &self,
93 operation: &str,
94 duration: Duration,
95 memory_kb: usize,
96 throughput_mbps: f64,
97 ) {
98 let mut metrics = self.metrics.lock().unwrap();
99 metrics.record(operation, duration, memory_kb, throughput_mbps);
100 }
101
102 pub fn metrics(&self) -> PerformanceMetrics {
104 self.metrics.lock().unwrap().clone()
105 }
106
107 pub fn create_dataset(&self, size_mb: usize) -> PathBuf {
111 let dataset_dir = self.temp_dir.path().join(format!("dataset_{}mb", size_mb));
112 fs::create_dir_all(&dataset_dir).expect("Failed to create dataset directory");
113
114 let patterns: Vec<(&str, &str, Vec<u8>)> = vec![
116 (
117 "text",
118 "txt",
119 b"This is a text file with some content.\n".to_vec(),
120 ),
121 (
122 "json",
123 "json",
124 br#"{"key": "value", "number": 42}"#.to_vec(),
125 ),
126 ("binary", "bin", (0..=255).collect::<Vec<u8>>()),
127 ];
128
129 let mut total_size = 0;
130 let mut file_count = 0;
131
132 while total_size < size_mb * 1024 * 1024 {
133 for (content_type, ext, base_content) in &patterns {
134 let filename = format!("{}_{:04}.{}", content_type, file_count, ext);
135 let filepath = dataset_dir.join(&filename);
136
137 let multiplier = (file_count % 10) + 1;
139 let content = base_content.repeat(multiplier);
140
141 fs::write(&filepath, &content).expect("Failed to write test file");
142 total_size += content.len();
143 file_count += 1;
144
145 if total_size >= size_mb * 1024 * 1024 {
146 break;
147 }
148 }
149 }
150
151 dataset_dir
152 }
153
154 pub fn create_file(&self, name: &str, content: &[u8]) -> PathBuf {
156 let filepath = self.temp_dir.path().join(name);
157 fs::write(&filepath, content).expect("Failed to write test file");
158 filepath
159 }
160
161 pub fn create_directory_structure(&self, name: &str) -> PathBuf {
163 let base = self.temp_dir.path().join(name);
164
165 fs::create_dir_all(base.join("dir1")).unwrap();
167 fs::create_dir_all(base.join("dir2/nested")).unwrap();
168 fs::create_dir_all(base.join("empty_dir")).unwrap();
169
170 fs::write(base.join("file1.txt"), b"Hello, world!").unwrap();
172 fs::write(base.join("file2.log"), b"Log entry 1\nLog entry 2\n").unwrap();
173 fs::write(
174 base.join("dir1/file3.dat"),
175 b"Binary data: \x00\x01\x02\xFF",
176 )
177 .unwrap();
178 fs::write(
179 base.join("dir2/file4.md"),
180 b"# Markdown\n\n## Section\n\nContent here.",
181 )
182 .unwrap();
183 fs::write(
184 base.join("dir2/nested/file5.json"),
185 br#"{"key": "value", "number": 42}"#,
186 )
187 .unwrap();
188
189 base
190 }
191
192 pub fn create_large_file(
194 &self,
195 name: &str,
196 size_mb: usize,
197 pattern: crate::fixtures::TestDataPattern,
198 ) -> PathBuf {
199 let filepath = self.temp_dir.path().join(name);
200 let data = crate::fixtures::create_test_data(size_mb, pattern);
201 fs::write(&filepath, data).expect("Failed to write large file");
202 filepath
203 }
204}
205
206impl Default for TestHarness {
207 fn default() -> Self {
208 Self::new()
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_harness_creation() {
218 let harness = TestHarness::new();
219 assert!(harness.temp_dir().exists());
220 }
221
222 #[test]
223 fn test_create_file() {
224 let harness = TestHarness::new();
225 let path = harness.create_file("test.txt", b"hello");
226 assert!(path.exists());
227 assert_eq!(fs::read(&path).unwrap(), b"hello");
228 }
229
230 #[test]
231 fn test_metrics_recording() {
232 let harness = TestHarness::new();
233 harness.record_metric("test_op", Duration::from_millis(100), 1024, 10.0);
234
235 let metrics = harness.metrics();
236 assert_eq!(metrics.operation_times.get("test_op").unwrap().len(), 1);
237 }
238
239 #[test]
240 fn test_create_dataset() {
241 let harness = TestHarness::new();
242 let dataset = harness.create_dataset(1); assert!(dataset.exists());
244
245 let entries: Vec<_> = fs::read_dir(&dataset).unwrap().collect();
247 assert!(!entries.is_empty());
248 }
249}