Skip to content
This repository was archived by the owner on May 5, 2026. It is now read-only.

Commit 8794aa9

Browse files
noahgiftclaude
andcommitted
Add comprehensive tests for selector, cache, and engine modules
- selector.rs: 43 tests (+34) covering all error types, edge cases, unicode - cache.rs: 25 tests (+17) covering eviction, collision resistance, stats - engine.rs: 30 tests (+19) covering complex trees, viewports, readonly Coverage improved from 89.36% to 89.51% line, 93.60% to 93.68% function. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8edf6d3 commit 8794aa9

3 files changed

Lines changed: 877 additions & 0 deletions

File tree

crates/presentar-layout/src/cache.rs

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,292 @@ mod tests {
242242
assert_eq!(cache.hits(), 0);
243243
assert_eq!(cache.misses(), 0);
244244
}
245+
246+
// =========================================================================
247+
// CacheKey Tests
248+
// =========================================================================
249+
250+
#[test]
251+
fn test_cache_key_equality() {
252+
let key1 = CacheKey {
253+
widget_id: 1,
254+
constraints_hash: 100,
255+
};
256+
let key2 = CacheKey {
257+
widget_id: 1,
258+
constraints_hash: 100,
259+
};
260+
assert_eq!(key1, key2);
261+
}
262+
263+
#[test]
264+
fn test_cache_key_inequality_widget_id() {
265+
let key1 = CacheKey {
266+
widget_id: 1,
267+
constraints_hash: 100,
268+
};
269+
let key2 = CacheKey {
270+
widget_id: 2,
271+
constraints_hash: 100,
272+
};
273+
assert_ne!(key1, key2);
274+
}
275+
276+
#[test]
277+
fn test_cache_key_inequality_constraints() {
278+
let key1 = CacheKey {
279+
widget_id: 1,
280+
constraints_hash: 100,
281+
};
282+
let key2 = CacheKey {
283+
widget_id: 1,
284+
constraints_hash: 200,
285+
};
286+
assert_ne!(key1, key2);
287+
}
288+
289+
#[test]
290+
fn test_cache_key_clone() {
291+
let key = CacheKey {
292+
widget_id: 42,
293+
constraints_hash: 999,
294+
};
295+
let cloned = key;
296+
assert_eq!(key, cloned);
297+
}
298+
299+
#[test]
300+
fn test_cache_key_debug() {
301+
let key = CacheKey {
302+
widget_id: 1,
303+
constraints_hash: 100,
304+
};
305+
let debug = format!("{:?}", key);
306+
assert!(debug.contains("widget_id"));
307+
assert!(debug.contains("constraints_hash"));
308+
}
309+
310+
// =========================================================================
311+
// Multiple Entries Tests
312+
// =========================================================================
313+
314+
#[test]
315+
fn test_cache_multiple_entries() {
316+
let mut cache = LayoutCache::new();
317+
318+
for i in 0..10 {
319+
let key = CacheKey {
320+
widget_id: i,
321+
constraints_hash: i * 100,
322+
};
323+
cache.insert(key, Size::new(i as f32, i as f32));
324+
}
325+
326+
assert_eq!(cache.len(), 10);
327+
328+
// Verify each entry
329+
for i in 0..10 {
330+
let key = CacheKey {
331+
widget_id: i,
332+
constraints_hash: i * 100,
333+
};
334+
assert_eq!(cache.get(key), Some(Size::new(i as f32, i as f32)));
335+
}
336+
}
337+
338+
#[test]
339+
fn test_cache_overwrite_entry() {
340+
let mut cache = LayoutCache::new();
341+
let key = CacheKey {
342+
widget_id: 1,
343+
constraints_hash: 100,
344+
};
345+
346+
cache.insert(key, Size::new(10.0, 10.0));
347+
assert_eq!(cache.get(key), Some(Size::new(10.0, 10.0)));
348+
349+
// Overwrite with new size
350+
cache.insert(key, Size::new(20.0, 20.0));
351+
assert_eq!(cache.get(key), Some(Size::new(20.0, 20.0)));
352+
assert_eq!(cache.len(), 1);
353+
}
354+
355+
// =========================================================================
356+
// Eviction Edge Cases
357+
// =========================================================================
358+
359+
#[test]
360+
fn test_cache_eviction_threshold() {
361+
let mut cache = LayoutCache::new();
362+
363+
let key1 = CacheKey {
364+
widget_id: 1,
365+
constraints_hash: 100,
366+
};
367+
let key2 = CacheKey {
368+
widget_id: 2,
369+
constraints_hash: 200,
370+
};
371+
372+
cache.insert(key1, Size::new(10.0, 10.0));
373+
cache.advance_frame();
374+
cache.insert(key2, Size::new(20.0, 20.0));
375+
cache.advance_frame();
376+
377+
// Both should still be present (threshold is 2 frames)
378+
assert_eq!(cache.len(), 2);
379+
380+
// One more frame without touching key1
381+
let _ = cache.get(key2);
382+
cache.advance_frame();
383+
384+
// key1 should be evicted (not used in last 2 frames)
385+
assert_eq!(cache.len(), 1);
386+
assert_eq!(cache.get(key2), Some(Size::new(20.0, 20.0)));
387+
}
388+
389+
#[test]
390+
fn test_cache_eviction_empty_cache() {
391+
let mut cache = LayoutCache::new();
392+
393+
// Advancing frames on empty cache should not panic
394+
for _ in 0..10 {
395+
cache.advance_frame();
396+
}
397+
398+
assert!(cache.is_empty());
399+
}
400+
401+
// =========================================================================
402+
// Default Implementation
403+
// =========================================================================
404+
405+
#[test]
406+
fn test_cache_default() {
407+
let cache = LayoutCache::default();
408+
assert!(cache.is_empty());
409+
assert_eq!(cache.hits(), 0);
410+
assert_eq!(cache.misses(), 0);
411+
}
412+
413+
// =========================================================================
414+
// Debug Format
415+
// =========================================================================
416+
417+
#[test]
418+
fn test_cache_debug() {
419+
let cache = LayoutCache::new();
420+
let debug = format!("{:?}", cache);
421+
assert!(debug.contains("LayoutCache"));
422+
}
423+
424+
// =========================================================================
425+
// Size Values
426+
// =========================================================================
427+
428+
#[test]
429+
fn test_cache_with_zero_size() {
430+
let mut cache = LayoutCache::new();
431+
let key = CacheKey {
432+
widget_id: 1,
433+
constraints_hash: 100,
434+
};
435+
436+
cache.insert(key, Size::new(0.0, 0.0));
437+
assert_eq!(cache.get(key), Some(Size::new(0.0, 0.0)));
438+
}
439+
440+
#[test]
441+
fn test_cache_with_large_size() {
442+
let mut cache = LayoutCache::new();
443+
let key = CacheKey {
444+
widget_id: 1,
445+
constraints_hash: 100,
446+
};
447+
448+
cache.insert(key, Size::new(10000.0, 10000.0));
449+
assert_eq!(cache.get(key), Some(Size::new(10000.0, 10000.0)));
450+
}
451+
452+
#[test]
453+
fn test_cache_with_fractional_size() {
454+
let mut cache = LayoutCache::new();
455+
let key = CacheKey {
456+
widget_id: 1,
457+
constraints_hash: 100,
458+
};
459+
460+
cache.insert(key, Size::new(10.5, 20.75));
461+
assert_eq!(cache.get(key), Some(Size::new(10.5, 20.75)));
462+
}
463+
464+
// =========================================================================
465+
// Hash Collision Resistance
466+
// =========================================================================
467+
468+
#[test]
469+
fn test_cache_different_widget_same_constraints() {
470+
let mut cache = LayoutCache::new();
471+
472+
let key1 = CacheKey {
473+
widget_id: 1,
474+
constraints_hash: 100,
475+
};
476+
let key2 = CacheKey {
477+
widget_id: 2,
478+
constraints_hash: 100,
479+
};
480+
481+
cache.insert(key1, Size::new(10.0, 10.0));
482+
cache.insert(key2, Size::new(20.0, 20.0));
483+
484+
assert_eq!(cache.get(key1), Some(Size::new(10.0, 10.0)));
485+
assert_eq!(cache.get(key2), Some(Size::new(20.0, 20.0)));
486+
assert_eq!(cache.len(), 2);
487+
}
488+
489+
#[test]
490+
fn test_cache_same_widget_different_constraints() {
491+
let mut cache = LayoutCache::new();
492+
493+
let key1 = CacheKey {
494+
widget_id: 1,
495+
constraints_hash: 100,
496+
};
497+
let key2 = CacheKey {
498+
widget_id: 1,
499+
constraints_hash: 200,
500+
};
501+
502+
cache.insert(key1, Size::new(10.0, 10.0));
503+
cache.insert(key2, Size::new(20.0, 20.0));
504+
505+
assert_eq!(cache.get(key1), Some(Size::new(10.0, 10.0)));
506+
assert_eq!(cache.get(key2), Some(Size::new(20.0, 20.0)));
507+
assert_eq!(cache.len(), 2);
508+
}
509+
510+
// =========================================================================
511+
// Frame Counter
512+
// =========================================================================
513+
514+
#[test]
515+
fn test_cache_frame_counter_overflow() {
516+
let mut cache = LayoutCache::new();
517+
let key = CacheKey {
518+
widget_id: 1,
519+
constraints_hash: 100,
520+
};
521+
522+
cache.insert(key, Size::new(10.0, 10.0));
523+
524+
// Advance many frames while keeping entry fresh
525+
for _ in 0..100 {
526+
let _ = cache.get(key);
527+
cache.advance_frame();
528+
}
529+
530+
// Entry should still exist
531+
assert_eq!(cache.get(key), Some(Size::new(10.0, 10.0)));
532+
}
245533
}

0 commit comments

Comments
 (0)