StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.
| 1 | use super::*; |
| 2 | use crate::image_cache::test_utils::make_static_image; |
| 3 | |
| 4 | #[test] |
| 5 | fn test_end_frame_evicts_when_asset_dropped() { |
| 6 | let mut cache = TextureCache::<()>::new(); |
| 7 | let asset = make_static_image(4, 4); |
| 8 | let weak = Arc::downgrade(&asset); |
| 9 | |
| 10 | cache.get_or_insert_by_asset(&asset, |_| ()); |
| 11 | |
| 12 | // Dropping the only strong reference makes strong_count == 0. |
| 13 | drop(asset); |
| 14 | assert_eq!(weak.strong_count(), 0); |
| 15 | |
| 16 | // end_frame should detect strong_count == 0 and evict the entry. |
| 17 | cache.end_frame(); |
| 18 | assert_eq!( |
| 19 | cache.textures.len(), |
| 20 | 0, |
| 21 | "TextureCache should evict entries whose backing asset has been dropped (cascade invariant)" |
| 22 | ); |
| 23 | } |
| 24 | |
| 25 | #[test] |
| 26 | fn test_end_frame_retains_asset_in_use() { |
| 27 | let mut cache = TextureCache::<()>::new(); |
| 28 | let asset = make_static_image(4, 4); |
| 29 | |
| 30 | cache.get_or_insert_by_asset(&asset, |_| ()); |
| 31 | |
| 32 | // The Arc is still alive; the texture should be retained. |
| 33 | cache.end_frame(); |
| 34 | assert_eq!( |
| 35 | cache.textures.len(), |
| 36 | 1, |
| 37 | "TextureCache should retain entries whose backing asset is still alive" |
| 38 | ); |
| 39 | |
| 40 | drop(asset); |
| 41 | } |
| 42 | |
| 43 | #[test] |
| 44 | fn test_end_frame_evicts_after_max_unused_frames() { |
| 45 | let mut cache = TextureCache::<()>::new(); |
| 46 | let asset = make_static_image(4, 4); |
| 47 | |
| 48 | cache.get_or_insert_by_asset(&asset, |_| ()); |
| 49 | |
| 50 | // Advance past MAX_UNUSED_FRAMES without re-accessing the texture. |
| 51 | // The asset is still alive (strong_count > 0), but the texture goes stale. |
| 52 | for _ in 0..TextureCache::<()>::MAX_UNUSED_FRAMES { |
| 53 | cache.end_frame(); |
| 54 | assert_eq!( |
| 55 | cache.textures.len(), |
| 56 | 1, |
| 57 | "Texture should still be present before MAX_UNUSED_FRAMES is exceeded" |
| 58 | ); |
| 59 | } |
| 60 | |
| 61 | // One more end_frame tips it over the threshold. |
| 62 | cache.end_frame(); |
| 63 | assert_eq!( |
| 64 | cache.textures.len(), |
| 65 | 0, |
| 66 | "TextureCache should evict stale entries after MAX_UNUSED_FRAMES unused frames" |
| 67 | ); |
| 68 | |
| 69 | drop(asset); |
| 70 | } |
| 71 | |
| 72 | #[test] |
| 73 | fn test_end_frame_retains_recently_used_entry() { |
| 74 | let mut cache = TextureCache::<()>::new(); |
| 75 | let asset = make_static_image(4, 4); |
| 76 | |
| 77 | cache.get_or_insert_by_asset(&asset, |_| ()); |
| 78 | |
| 79 | // Re-access the texture each frame to keep it fresh. |
| 80 | for _ in 0..=TextureCache::<()>::MAX_UNUSED_FRAMES { |
| 81 | cache.get_or_insert_by_asset(&asset, |_| ()); |
| 82 | cache.end_frame(); |
| 83 | } |
| 84 | |
| 85 | assert_eq!( |
| 86 | cache.textures.len(), |
| 87 | 1, |
| 88 | "Texture accessed every frame should never be evicted by the frame-count check" |
| 89 | ); |
| 90 | |
| 91 | drop(asset); |
| 92 | } |
| 93 |