1use super::types::CacheTier;
6
7#[cfg(test)]
8mod tests {
9 use super::*;
10 use crate::expr_cache::*;
11 #[test]
12 fn test_interner_new() {
13 let interner = StringInterner::new();
14 assert!(interner.is_empty());
15 assert_eq!(interner.len(), 0);
16 }
17 #[test]
18 fn test_intern_dedup() {
19 let mut interner = StringInterner::new();
20 let id1 = interner.intern("Nat");
21 let id2 = interner.intern("Nat");
22 let id3 = interner.intern("Type");
23 assert_eq!(id1, id2);
24 assert_ne!(id1, id3);
25 assert_eq!(interner.len(), 2);
26 }
27 #[test]
28 fn test_intern_lookup() {
29 let mut interner = StringInterner::new();
30 let id = interner.intern("Prop");
31 assert_eq!(interner.get(id), Some("Prop"));
32 assert!(interner.contains("Prop"));
33 assert!(!interner.contains("Bool"));
34 }
35 #[test]
36 fn test_decl_hash_compute() {
37 let h = DeclHash::compute("def foo : Nat := 0");
38 assert_ne!(h.value(), 0);
39 }
40 #[test]
41 fn test_decl_hash_same() {
42 let h1 = DeclHash::compute("theorem bar : True := trivial");
43 let h2 = DeclHash::compute("theorem bar : True := trivial");
44 let h3 = DeclHash::compute("theorem baz : True := trivial");
45 assert_eq!(h1, h2);
46 assert_ne!(h1, h3);
47 }
48 #[test]
49 fn test_parse_cache_lookup() {
50 let mut cache = ParseCache::new(10);
51 cache.insert("def foo : Nat := 0", Some("foo".to_string()));
52 assert!(cache.lookup("def foo : Nat := 0").is_some());
53 assert!(cache.lookup("def bar : Nat := 1").is_none());
54 assert!((cache.hit_rate() - 0.5).abs() < 1e-9);
55 }
56 #[test]
57 fn test_parse_cache_insert_evict() {
58 let mut cache = ParseCache::new(2);
59 cache.insert("def a := 1", Some("a".to_string()));
60 cache.insert("def b := 2", Some("b".to_string()));
61 assert_eq!(cache.len(), 2);
62 cache.insert("def c := 3", Some("c".to_string()));
63 assert_eq!(cache.len(), 2);
64 }
65 #[test]
66 fn test_hit_rate() {
67 let mut cache = ParseCache::new(10);
68 cache.insert("def x := 0", None);
69 cache.lookup("def x := 0");
70 cache.lookup("def x := 0");
71 cache.lookup("def y := 1");
72 let rate = cache.hit_rate();
73 assert!((rate - 2.0 / 3.0).abs() < 1e-9);
74 }
75}
76#[allow(dead_code)]
78pub fn fnv1a_hash(data: &[u8]) -> u64 {
79 let mut h: u64 = 14695981039346656037;
80 for &b in data {
81 h = h.wrapping_mul(1099511628211) ^ b as u64;
82 }
83 h
84}
85#[allow(dead_code)]
87pub fn mix_hashes(a: u64, b: u64) -> u64 {
88 let mut x = a ^ b.rotate_left(17);
89 x = x.wrapping_add(b);
90 x ^= x >> 31;
91 x = x.wrapping_mul(0x9e3779b97f4a7c15);
92 x ^= x >> 27;
93 x
94}
95#[allow(dead_code)]
97pub fn compute_checksum(data: &str) -> u32 {
98 let mut sum: u32 = 0;
99 for (i, b) in data.bytes().enumerate() {
100 sum = sum.wrapping_add((b as u32).wrapping_mul(i as u32 + 1));
101 }
102 sum
103}
104#[allow(dead_code)]
106pub fn validate_cache_integrity(data: &str, stored: u32) -> bool {
107 compute_checksum(data) == stored
108}
109#[allow(dead_code)]
111pub fn classify_cache_entry(access_count: u64, age_ticks: u64) -> CacheTier {
112 let score = if age_ticks == 0 {
113 access_count as f64
114 } else {
115 access_count as f64 / age_ticks as f64
116 };
117 if score >= 1.0 {
118 CacheTier::Hot
119 } else if score >= 0.1 {
120 CacheTier::Warm
121 } else if score >= 0.01 {
122 CacheTier::Cold
123 } else {
124 CacheTier::Dead
125 }
126}
127#[cfg(test)]
128mod extended_expr_cache_tests {
129 use super::*;
130 use crate::expr_cache::*;
131 #[test]
132 fn test_fnv1a_hash_stable() {
133 let h1 = fnv1a_hash(b"hello");
134 let h2 = fnv1a_hash(b"hello");
135 assert_eq!(h1, h2);
136 assert_ne!(fnv1a_hash(b"hello"), fnv1a_hash(b"world"));
137 }
138 #[test]
139 fn test_mix_hashes() {
140 let a = fnv1a_hash(b"alpha");
141 let b = fnv1a_hash(b"beta");
142 let m = mix_hashes(a, b);
143 assert_ne!(m, a);
144 assert_ne!(m, b);
145 }
146 #[test]
147 fn test_parse_result_cache() {
148 let mut cache = ParseResultCache::new(10);
149 cache.store("x + y", "App".to_string(), 42);
150 assert!(cache.lookup("x + y").is_some());
151 assert!(cache.lookup("unknown").is_none());
152 assert_eq!(cache.stats().0, 1);
153 }
154 #[test]
155 fn test_segment_table() {
156 let src = "fun x -> x";
157 let mut table = SegmentTable::new();
158 let seg = ExprSegment::from_slice(src, 0, 5, SegmentKind::Lambda);
159 table.add(seg);
160 assert_eq!(table.count(), 1);
161 table.invalidate_range(0, 5);
162 assert_eq!(table.count(), 0);
163 }
164 #[test]
165 fn test_subexpr_frequency_map() {
166 let mut map = SubexprFrequencyMap::new();
167 map.record(42);
168 map.record(42);
169 map.record(7);
170 assert_eq!(map.frequency(42), 2);
171 assert_eq!(map.total_unique(), 2);
172 }
173 #[test]
174 fn test_alpha_eq_cache() {
175 let mut c = AlphaEqCache::new();
176 c.mark_equal(1, 2);
177 assert_eq!(c.query(2, 1), Some(true));
178 c.mark_inequal(3, 4);
179 assert_eq!(c.query(3, 4), Some(false));
180 }
181 #[test]
182 fn test_cache_pressure_monitor() {
183 let mut m = CachePressureMonitor::new();
184 m.record_insert(10);
185 m.record_lookup(true);
186 m.record_lookup(false);
187 m.record_eviction();
188 assert!((m.hit_rate() - 0.5).abs() < 1e-9);
189 }
190 #[test]
191 fn test_string_pool() {
192 let mut p = StringPool::new();
193 p.intern("hello");
194 p.intern("hello");
195 p.intern("world");
196 assert_eq!(p.count(), 2);
197 assert_eq!(p.saved_bytes(), 5);
198 }
199 #[test]
200 fn test_window_cache() {
201 let mut c: WindowCache<i32, &str> = WindowCache::new(2);
202 c.insert(1, "a");
203 c.insert(2, "b");
204 c.insert(3, "c");
205 assert_eq!(c.get(&1), None);
206 assert_eq!(c.get(&3), Some(&"c"));
207 }
208 #[test]
209 fn test_cache_integrity() {
210 let d = "theorem foo";
211 let cs = compute_checksum(d);
212 assert!(validate_cache_integrity(d, cs));
213 assert!(!validate_cache_integrity(d, cs.wrapping_add(1)));
214 }
215 #[test]
216 fn test_classify_entry() {
217 assert_eq!(classify_cache_entry(10, 1), CacheTier::Hot);
218 assert_eq!(classify_cache_entry(0, 1000), CacheTier::Dead);
219 }
220 #[test]
221 fn test_versioned_cache() {
222 let mut c: VersionedCache<&str, i32> = VersionedCache::new();
223 c.insert("x", 10);
224 assert_eq!(c.get(&"x"), Some(&10));
225 c.bump_version();
226 assert_eq!(c.get(&"x"), None);
227 }
228 #[test]
229 fn test_bloom_filter() {
230 let mut bf = BloomFilter::new(1024, 3);
231 bf.insert(42);
232 assert!(bf.may_contain(42));
233 bf.clear();
234 }
235 #[test]
236 fn test_nesting_depth() {
237 let mut t = NestingDepthTracker::new(3);
238 assert!(t.enter().is_ok());
239 assert!(t.enter().is_ok());
240 assert!(t.enter().is_ok());
241 assert!(t.enter().is_err());
242 t.exit();
243 assert!(t.enter().is_ok());
244 }
245 #[test]
246 fn test_rolling_hash() {
247 let mut rh = RollingHash::new(3);
248 rh.push(b'a');
249 rh.push(b'b');
250 rh.push(b'c');
251 assert!(rh.window_full());
252 let h1 = rh.current_hash();
253 rh.push(b'd');
254 assert_ne!(rh.current_hash(), h1);
255 }
256 #[test]
257 fn test_windowed_metrics() {
258 let mut m = WindowedCacheMetrics::new(100);
259 m.record_hit();
260 m.record_miss();
261 assert!((m.hit_rate() - 0.5).abs() < 1e-9);
262 m.reset();
263 assert_eq!(m.window_hits, 0);
264 }
265 #[test]
266 fn test_cache_health_report() {
267 let r = CacheHealthReport {
268 total_entries: 100,
269 hot_entries: 60,
270 warm_entries: 20,
271 cold_entries: 10,
272 dead_entries: 10,
273 estimated_waste_pct: 10.0,
274 };
275 assert!(r.is_healthy());
276 assert!(r.summary().contains("total=100"));
277 }
278}
279#[cfg(test)]
280mod extended_expr_cache_tests_2 {
281 use super::*;
282 use crate::expr_cache::*;
283 #[test]
284 fn test_expr_location_index() {
285 let mut idx = ExprLocationIndex::new();
286 idx.record(42, 10, 20);
287 idx.record(42, 30, 40);
288 assert_eq!(idx.count_occurrences(42), 2);
289 assert_eq!(idx.total_tracked(), 2);
290 }
291 #[test]
292 fn test_cache_coverage_report() {
293 let mut r = CacheCoverageReport::new();
294 r.record_cached(1000);
295 r.record_uncached(500);
296 assert!((r.coverage_pct() - 66.666).abs() < 0.01);
297 }
298 #[test]
299 fn test_namespaced_cache() {
300 let mut c: NamespacedCache<&str, i32> = NamespacedCache::new();
301 c.insert("math", "pi", 314);
302 assert_eq!(c.get("math", &"pi"), Some(&314));
303 c.invalidate_namespace("math");
304 assert_eq!(c.get("math", &"pi"), None);
305 }
306 #[test]
307 fn test_type_check_cache() {
308 let mut tc = TypeCheckCache::new(5);
309 let r = TypeCheckResult {
310 expr_hash: 99,
311 inferred_type: "Nat".into(),
312 is_valid: true,
313 check_time_us: 10,
314 };
315 tc.store(r);
316 assert!(tc.lookup(99).is_some());
317 tc.invalidate(99);
318 assert!(tc.lookup(99).is_none());
319 }
320 #[test]
321 fn test_cache_prewarmer() {
322 let sources = vec!["x".into(), "y".into()];
323 let mut w = CachePrewarmer::new(sources);
324 let mut c = ParseResultCache::new(100);
325 let n = w.prewarm_all(&mut c);
326 assert_eq!(n, 2);
327 let n2 = w.prewarm_all(&mut c);
328 assert_eq!(n2, 0);
329 }
330 #[test]
331 fn test_hash_set_64() {
332 let mut hs = HashSet64::new();
333 assert!(hs.insert(42));
334 assert!(!hs.insert(42));
335 assert!(hs.contains(42));
336 hs.clear();
337 assert!(hs.is_empty());
338 }
339 #[test]
340 fn test_two_queue_cache() {
341 let mut c: TwoQueueCache<String, i32> = TwoQueueCache::new(4);
342 c.insert("a".into(), 1);
343 c.insert("b".into(), 2);
344 assert_eq!(c.get(&"a".into()), Some(&1));
345 assert_eq!(c.get(&"z".into()), None);
346 }
347}
348#[cfg(test)]
349mod extended_expr_cache_tests_3 {
350 use super::*;
351 use crate::expr_cache::*;
352 #[test]
353 fn test_lfu_eviction() {
354 let p = LfuEviction::new(1, 0.0);
355 assert!(!p.should_evict(2, 0, 100));
356 assert!(p.should_evict(0, 0, 100));
357 assert_eq!(p.policy_name(), "LFU-Age");
358 }
359 #[test]
360 fn test_ttl_eviction() {
361 let p = TtlEviction::new(5);
362 assert!(!p.should_evict(0, 10, 14));
363 assert!(p.should_evict(0, 10, 16));
364 assert_eq!(p.policy_name(), "TTL");
365 }
366 #[test]
367 fn test_macro_expansion_cache() {
368 let mut c = MacroExpansionCache::new(10);
369 c.store(MacroExpansionEntry {
370 macro_hash: 1,
371 arg_hash: 2,
372 expansion: "exp".into(),
373 expansion_depth: 1,
374 use_count: 0,
375 });
376 assert!(c.lookup(1, 2).is_some());
377 assert_eq!(c.total_stored(), 1);
378 }
379 #[test]
380 fn test_lru_cache() {
381 let mut c: LruCache<&str> = LruCache::new(3);
382 c.insert(1, "a");
383 c.insert(2, "b");
384 c.insert(3, "c");
385 c.insert(4, "d");
386 assert!(!c.contains(1));
387 assert_eq!(c.get(4), Some(&"d"));
388 }
389 #[test]
390 fn test_expr_pool() {
391 let mut p = ExprPool::new();
392 let h = p.intern("Nat".to_string());
393 let h2 = p.intern("Nat".to_string());
394 assert_eq!(h, h2);
395 assert_eq!(p.total_refs(), 2);
396 p.release(h);
397 assert_eq!(p.total_refs(), 1);
398 p.release(h);
399 assert_eq!(p.total_exprs(), 0);
400 }
401 #[test]
402 fn test_memo_table() {
403 let mut t = MemoTable::new();
404 t.store(
405 0,
406 "expr",
407 MemoEntry {
408 end_pos: 5,
409 result: "x".into(),
410 success: true,
411 },
412 );
413 let found = t.lookup(0, "expr");
414 assert!(found.is_some());
415 assert_eq!(found.expect("test operation should succeed").end_pos, 5);
416 }
417 #[test]
418 fn test_global_expr_table() {
419 let mut t = GlobalExprTable::new();
420 let id1 = t.intern("Nat");
421 let id2 = t.intern("Nat");
422 assert_eq!(id1, id2);
423 let id3 = t.intern("Bool");
424 assert_ne!(id1, id3);
425 assert_eq!(t.lookup_repr(id1), Some("Nat"));
426 assert_eq!(t.table_size(), 2);
427 }
428 #[test]
429 fn test_symbol_interner() {
430 let mut si = SymbolInterner::new();
431 let id1 = si.intern("foo");
432 let id2 = si.intern("foo");
433 assert_eq!(id1, id2);
434 let id3 = si.intern("bar");
435 assert_ne!(id1, id3);
436 assert_eq!(si.lookup(id1), Some("foo"));
437 assert_eq!(si.size(), 2);
438 assert!(si.contains("foo"));
439 assert!(!si.contains("baz"));
440 }
441}
442#[allow(dead_code)]
444pub fn djb2_hash(s: &str) -> u64 {
445 let mut hash: u64 = 5381;
446 for b in s.bytes() {
447 hash = hash.wrapping_mul(33).wrapping_add(b as u64);
448 }
449 hash
450}
451#[allow(dead_code)]
453pub fn combined_hash(s: &str) -> u64 {
454 let fnv = fnv1a_hash(s.as_bytes());
455 let djb = djb2_hash(s);
456 mix_hashes(fnv, djb)
457}
458#[allow(dead_code)]
460pub fn hash_alpha_equiv(a: &str, b: &str) -> bool {
461 combined_hash(a) == combined_hash(b)
462}
463#[allow(dead_code)]
465pub fn estimate_string_memory(s: &str) -> usize {
466 s.len() + std::mem::size_of::<String>() + 8
467}
468#[allow(dead_code)]
470pub fn build_cache_key(parts: &[&str]) -> u64 {
471 let mut h: u64 = 0;
472 for part in parts {
473 h = mix_hashes(h, fnv1a_hash(part.as_bytes()));
474 }
475 h
476}
477#[cfg(test)]
478mod extended_expr_cache_tests_4 {
479 use super::*;
480 use crate::expr_cache::*;
481 #[test]
482 fn test_bump_allocator() {
483 let mut alloc = BumpAllocator::new(100);
484 let pos = alloc
485 .alloc_str("hello")
486 .expect("test operation should succeed");
487 assert_eq!(alloc.get_str(pos, 5), Some("hello"));
488 assert_eq!(alloc.used(), 5);
489 alloc.reset();
490 assert_eq!(alloc.used(), 0);
491 }
492 #[test]
493 fn test_token_frequency_table() {
494 let mut t = TokenFrequencyTable::new();
495 t.record("def");
496 t.record("def");
497 t.record("fun");
498 assert_eq!(t.count("def"), 2);
499 assert_eq!(t.unique_tokens(), 2);
500 assert_eq!(t.total_tokens(), 3);
501 let top = t.top_n(1);
502 assert_eq!(top[0].0, "def");
503 }
504 #[test]
505 fn test_cache_warmup() {
506 let w = CacheWarmup::new(vec!["x".into(), "y".into()]).with_priority(CachePriority::High);
507 assert_eq!(w.source_count(), 2);
508 assert_eq!(w.priority, CachePriority::High);
509 }
510 #[test]
511 fn test_cache_report() {
512 let r = CacheReport::new(100, 80, 20, 5, 1024);
513 assert!((r.hit_rate() - 0.8).abs() < 1e-9);
514 let s = r.summary();
515 assert!(s.contains("hit_rate=80.0%"));
516 }
517 #[test]
518 fn test_token_window() {
519 let mut w = TokenWindow::new(3);
520 w.push("a");
521 w.push("b");
522 w.push("c");
523 assert!(w.is_full());
524 w.push("d");
525 assert!(!w.contains("a"));
526 assert!(w.contains("d"));
527 }
528 #[test]
529 fn test_djb2_hash() {
530 let h1 = djb2_hash("hello");
531 let h2 = djb2_hash("hello");
532 assert_eq!(h1, h2);
533 assert_ne!(djb2_hash("hello"), djb2_hash("world"));
534 }
535 #[test]
536 fn test_combined_hash() {
537 let h = combined_hash("theorem foo : Nat");
538 assert!(h > 0);
539 assert_eq!(combined_hash("x"), combined_hash("x"));
540 assert_ne!(combined_hash("x"), combined_hash("y"));
541 }
542 #[test]
543 fn test_build_cache_key() {
544 let k1 = build_cache_key(&["rule1", "pos:5"]);
545 let k2 = build_cache_key(&["rule1", "pos:5"]);
546 assert_eq!(k1, k2);
547 assert_ne!(build_cache_key(&["a"]), build_cache_key(&["b"]));
548 }
549 #[test]
550 fn test_estimate_string_memory() {
551 let m = estimate_string_memory("hello");
552 assert!(m >= 5);
553 }
554 #[test]
555 fn test_interning_stats() {
556 let mut stats = InterningStats::new();
557 stats.record_new();
558 stats.record_new();
559 stats.record_hit(10);
560 assert_eq!(stats.unique_strings, 2);
561 assert_eq!(stats.bytes_saved, 10);
562 assert!((stats.dedup_ratio() - 1.5).abs() < 1e-9);
563 }
564 #[test]
565 fn test_hash_alpha_equiv() {
566 assert!(hash_alpha_equiv("hello", "hello"));
567 assert!(!hash_alpha_equiv("foo", "bar"));
568 }
569}
570#[cfg(test)]
571mod extended_expr_cache_tests_5 {
572 use super::*;
573 use crate::expr_cache::*;
574 #[test]
575 fn test_cache_key_builder() {
576 let k1 = CacheKeyBuilder::new()
577 .with_str("expr")
578 .with_usize(5)
579 .build();
580 let k2 = CacheKeyBuilder::new()
581 .with_str("expr")
582 .with_usize(5)
583 .build();
584 assert_eq!(k1, k2);
585 let k3 = CacheKeyBuilder::new().with_str("ty").with_usize(5).build();
586 assert_ne!(k1, k3);
587 }
588 #[test]
589 fn test_adaptive_lru_cache() {
590 let mut c: AdaptiveLruCache<i32> = AdaptiveLruCache::new(10, 5, 100);
591 c.insert(1, 42);
592 assert_eq!(c.get(1), Some(&42));
593 assert_eq!(c.get(99), None);
594 assert_eq!(c.hit_rate(), 0.5);
595 }
596 #[test]
597 fn test_expr_diff_cache() {
598 let mut c = ExprDiffCache::new(10);
599 c.store(1, 2, "diff text");
600 assert_eq!(c.lookup(1, 2), Some("diff text"));
601 assert_eq!(c.lookup(2, 1), Some("diff text"));
602 assert_eq!(c.size(), 1);
603 }
604 #[test]
605 fn test_multi_level_cache() {
606 let mut c: MultiLevelCache<String> = MultiLevelCache::new(2, 10);
607 c.insert(1, "a".to_string());
608 c.insert(2, "b".to_string());
609 assert_eq!(c.get(&1), Some("a".to_string()));
610 assert_eq!(c.get(&99), None);
611 }
612 #[test]
613 fn test_persistent_cache() {
614 let mut c = PersistentCache::new();
615 c.insert(42, "hello");
616 c.insert(99, "world");
617 let s = c.serialize();
618 let c2 = PersistentCache::deserialize(&s);
619 assert_eq!(c2.lookup(42), Some("hello"));
620 assert_eq!(c2.lookup(99), Some("world"));
621 assert_eq!(c2.entry_count(), 2);
622 }
623}