## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/trueno/src/brick/kv_cache.rs

    Line
    Count
    Source
    1
    //! KV Cache Management and Batch Ordering
    2
    //!
    3
    //! LCP-10: KV Cache Slot tracking for transformer inference.
    4
    //! LCP-14: Sequential Batch Ordering for cache-friendly processing.
    5
    6
    // ----------------------------------------------------------------------------
    7
    // LCP-10: KV Cache Slot Info
    8
    // ----------------------------------------------------------------------------
    9
    10
    /// Metadata for a KV cache slot in transformer inference.
    11
    ///
    12
    /// Tracks position, token info, and usage for cache management.
    13
    #[derive(Debug, Clone, Default)]
    14
    pub struct KvCacheSlotInfo {
    15
        /// Sequence position this slot represents
    16
        pub position: u32,
    17
        /// Token ID stored in this slot
    18
        pub token_id: u32,
    19
        /// Layer index
    20
        pub layer: u16,
    21
        /// Head index
    22
        pub head: u16,
    23
        /// Whether this slot is valid/filled
    24
        pub valid: bool,
    25
        /// Last access time (in steps)
    26
        pub last_access: u64,
    27
    }
    28
    29
    impl KvCacheSlotInfo {
    30
        /// Create a new slot info.
    31
    0
        pub fn new(position: u32, token_id: u32, layer: u16, head: u16) -> Self {
    32
    0
            Self {
    33
    0
                position,
    34
    0
                token_id,
    35
    0
                layer,
    36
    0
                head,
    37
    0
                valid: true,
    38
    0
                last_access: 0,
    39
    0
            }
    40
    0
        }
    41
    42
        /// Mark slot as accessed.
    43
    0
        pub fn touch(&mut self, step: u64) {
    44
    0
            self.last_access = step;
    45
    0
        }
    46
    47
        /// Invalidate the slot.
    48
    0
        pub fn invalidate(&mut self) {
    49
    0
            self.valid = false;
    50
    0
        }
    51
    52
        /// Check if slot can be evicted (LRU policy).
    53
        #[must_use]
    54
    0
        pub fn eviction_priority(&self, current_step: u64) -> u64 {
    55
    0
            if !self.valid {
    56
    0
                return u64::MAX; // Invalid slots have highest eviction priority
    57
    0
            }
    58
    0
            current_step.saturating_sub(self.last_access)
    59
    0
        }
    60
    }
    61
    62
    /// KV cache manager with slot tracking.
    63
    #[derive(Debug)]
    64
    pub struct KvCacheManager {
    65
        /// Slot metadata
    66
        slots: Vec<KvCacheSlotInfo>,
    67
        /// Current step counter
    68
        current_step: u64,
    69
        /// Number of valid slots
    70
        valid_count: usize,
    71
    }
    72
    73
    impl KvCacheManager {
    74
        /// Create manager with given capacity.
    75
    0
        pub fn new(capacity: usize) -> Self {
    76
    0
            Self {
    77
    0
                slots: vec![KvCacheSlotInfo::default(); capacity],
    78
    0
                current_step: 0,
    79
    0
                valid_count: 0,
    80
    0
            }
    81
    0
        }
    82
    83
        /// Allocate a slot.
    84
    0
        pub fn allocate(
    85
    0
            &mut self,
    86
    0
            position: u32,
    87
    0
            token_id: u32,
    88
    0
            layer: u16,
    89
    0
            head: u16,
    90
    0
        ) -> Option<usize> {
    91
            // Find first invalid slot
    92
    0
            for (i, slot) in self.slots.iter_mut().enumerate() {
    93
    0
                if !slot.valid {
    94
    0
                    *slot = KvCacheSlotInfo::new(position, token_id, layer, head);
    95
    0
                    slot.touch(self.current_step);
    96
    0
                    self.valid_count += 1;
    97
    0
                    return Some(i);
    98
    0
                }
    99
            }
    100
    0
            None // No free slots
    101
    0
        }
    102
    103
        /// Access a slot.
    104
    0
        pub fn access(&mut self, index: usize) -> Option<&KvCacheSlotInfo> {
    105
    0
            if index < self.slots.len() {
    106
    0
                self.slots[index].touch(self.current_step);
    107
    0
                Some(&self.slots[index])
    108
            } else {
    109
    0
                None
    110
            }
    111
    0
        }
    112
    113
        /// Evict LRU slot.
    114
    0
        pub fn evict_lru(&mut self) -> Option<usize> {
    115
    0
            let mut best_idx = None;
    116
    0
            let mut best_priority = 0u64;
    117
    118
    0
            for (i, slot) in self.slots.iter().enumerate() {
    119
    0
                if slot.valid {
    120
    0
                    let priority = slot.eviction_priority(self.current_step);
    121
                    // Use >= for first found slot (best_idx.is_none()), then > for ties
    122
    0
                    if best_idx.is_none() || priority > best_priority {
    123
    0
                        best_priority = priority;
    124
    0
                        best_idx = Some(i);
    125
    0
                    }
    126
    0
                }
    127
            }
    128
    129
    0
            if let Some(idx) = best_idx {
    130
    0
                self.slots[idx].invalidate();
    131
    0
                self.valid_count -= 1;
    132
    0
            }
    133
    0
            best_idx
    134
    0
        }
    135
    136
        /// Advance step counter.
    137
    0
        pub fn step(&mut self) {
    138
    0
            self.current_step += 1;
    139
    0
        }
    140
    141
        /// Get number of valid slots.
    142
        #[must_use]
    143
    0
        pub fn valid_count(&self) -> usize {
    144
    0
            self.valid_count
    145
    0
        }
    146
    147
        /// Get capacity.
    148
        #[must_use]
    149
    0
        pub fn capacity(&self) -> usize {
    150
    0
            self.slots.len()
    151
    0
        }
    152
    }
    153
    154
    // ----------------------------------------------------------------------------
    155
    // LCP-14: Sequential Batch Ordering
    156
    // ----------------------------------------------------------------------------
    157
    158
    /// Sequential batch orderer for cache-friendly processing.
    159
    ///
    160
    /// Ensures batches are processed in optimal order for memory access patterns.
    161
    #[derive(Debug, Clone)]
    162
    pub struct SequentialBatchOrderer {
    163
        /// Batch indices in processing order
    164
        order: Vec<usize>,
    165
        /// Current position in order
    166
        position: usize,
    167
    }
    168
    169
    impl SequentialBatchOrderer {
    170
        /// Create orderer for n batches.
    171
    0
        pub fn new(n_batches: usize) -> Self {
    172
    0
            Self {
    173
    0
                order: (0..n_batches).collect(),
    174
    0
                position: 0,
    175
    0
            }
    176
    0
        }
    177
    178
        /// Create orderer with reverse order (sometimes better for certain patterns).
    179
    0
        pub fn reversed(n_batches: usize) -> Self {
    180
    0
            Self {
    181
    0
                order: (0..n_batches).rev().collect(),
    182
    0
                position: 0,
    183
    0
            }
    184
    0
        }
    185
    186
        /// Create orderer with interleaved order (for better cache utilization).
    187
    0
        pub fn interleaved(n_batches: usize) -> Self {
    188
    0
            let mut order = Vec::with_capacity(n_batches);
    189
    0
            let mid = n_batches / 2;
    190
    191
            // Interleave: 0, mid, 1, mid+1, 2, mid+2, ...
    192
    0
            for i in 0..mid {
    193
    0
                order.push(i);
    194
    0
                if mid + i < n_batches {
    195
    0
                    order.push(mid + i);
    196
    0
                }
    197
            }
    198
            // Handle odd number of batches
    199
    0
            if !n_batches.is_multiple_of(2) {
    200
    0
                order.push(n_batches - 1);
    201
    0
            }
    202
    203
    0
            Self { order, position: 0 }
    204
    0
        }
    205
    206
        /// Get next batch index.
    207
    0
        pub fn next_batch(&mut self) -> Option<usize> {
    208
    0
            if self.position < self.order.len() {
    209
    0
                let idx = self.order[self.position];
    210
    0
                self.position += 1;
    211
    0
                Some(idx)
    212
            } else {
    213
    0
                None
    214
            }
    215
    0
        }
    216
    217
        /// Reset to beginning.
    218
    0
        pub fn reset(&mut self) {
    219
    0
            self.position = 0;
    220
    0
        }
    221
    222
        /// Check if all batches have been processed.
    223
        #[must_use]
    224
    0
        pub fn is_done(&self) -> bool {
    225
    0
            self.position >= self.order.len()
    226
    0
        }
    227
    228
        /// Get remaining count.
    229
        #[must_use]
    230
    0
        pub fn remaining(&self) -> usize {
    231
    0
            self.order.len().saturating_sub(self.position)
    232
    0
        }
    233
    }
    234
    235
    impl Iterator for SequentialBatchOrderer {
    236
        type Item = usize;
    237
    238
    0
        fn next(&mut self) -> Option<Self::Item> {
    239
    0
            self.next_batch()
    240
    0
        }
    241
    }
    242
    243
    #[cfg(test)]
    244
    mod tests {
    245
        use super::*;
    246
    247
        // =========================================================================
    248
        // KvCacheSlotInfo Tests
    249
        // =========================================================================
    250
    251
        #[test]
    252
        fn test_kv_cache_slot_info_new() {
    253
            let slot = KvCacheSlotInfo::new(5, 42, 2, 8);
    254
    255
            assert_eq!(slot.position, 5);
    256
            assert_eq!(slot.token_id, 42);
    257
            assert_eq!(slot.layer, 2);
    258
            assert_eq!(slot.head, 8);
    259
            assert!(slot.valid);
    260
            assert_eq!(slot.last_access, 0);
    261
        }
    262
    263
        #[test]
    264
        fn test_kv_cache_slot_info_default() {
    265
            let slot = KvCacheSlotInfo::default();
    266
    267
            assert_eq!(slot.position, 0);
    268
            assert_eq!(slot.token_id, 0);
    269
            assert_eq!(slot.layer, 0);
    270
            assert_eq!(slot.head, 0);
    271
            assert!(!slot.valid);
    272
            assert_eq!(slot.last_access, 0);
    273
        }
    274
    275
        #[test]
    276
        fn test_kv_cache_slot_info_touch() {
    277
            let mut slot = KvCacheSlotInfo::new(0, 0, 0, 0);
    278
    279
            slot.touch(10);
    280
            assert_eq!(slot.last_access, 10);
    281
    282
            slot.touch(50);
    283
            assert_eq!(slot.last_access, 50);
    284
        }
    285
    286
        #[test]
    287
        fn test_kv_cache_slot_info_invalidate() {
    288
            let mut slot = KvCacheSlotInfo::new(0, 0, 0, 0);
    289
            assert!(slot.valid);
    290
    291
            slot.invalidate();
    292
            assert!(!slot.valid);
    293
        }
    294
    295
        #[test]
    296
        fn test_kv_cache_slot_info_eviction_priority_valid() {
    297
            let mut slot = KvCacheSlotInfo::new(0, 0, 0, 0);
    298
            slot.touch(10);
    299
    300
            // Same step as last access = priority 0
    301
            assert_eq!(slot.eviction_priority(10), 0);
    302
    303
            // Steps since last access
    304
            assert_eq!(slot.eviction_priority(20), 10);
    305
            assert_eq!(slot.eviction_priority(100), 90);
    306
        }
    307
    308
        #[test]
    309
        fn test_kv_cache_slot_info_eviction_priority_invalid() {
    310
            let mut slot = KvCacheSlotInfo::new(0, 0, 0, 0);
    311
            slot.invalidate();
    312
    313
            // Invalid slots have MAX priority (should be evicted first)
    314
            assert_eq!(slot.eviction_priority(0), u64::MAX);
    315
            assert_eq!(slot.eviction_priority(100), u64::MAX);
    316
        }
    317
    318
        // =========================================================================
    319
        // KvCacheManager Tests
    320
        // =========================================================================
    321
    322
        #[test]
    323
        fn test_kv_cache_manager_new() {
    324
            let mgr = KvCacheManager::new(10);
    325
    326
            assert_eq!(mgr.capacity(), 10);
    327
            assert_eq!(mgr.valid_count(), 0);
    328
        }
    329
    330
        #[test]
    331
        fn test_kv_cache_manager_allocate() {
    332
            let mut mgr = KvCacheManager::new(3);
    333
    334
            let idx0 = mgr.allocate(0, 100, 0, 0);
    335
            assert_eq!(idx0, Some(0));
    336
            assert_eq!(mgr.valid_count(), 1);
    337
    338
            let idx1 = mgr.allocate(1, 101, 0, 0);
    339
            assert_eq!(idx1, Some(1));
    340
            assert_eq!(mgr.valid_count(), 2);
    341
    342
            let idx2 = mgr.allocate(2, 102, 0, 0);
    343
            assert_eq!(idx2, Some(2));
    344
            assert_eq!(mgr.valid_count(), 3);
    345
    346
            // Full - no more slots
    347
            let idx3 = mgr.allocate(3, 103, 0, 0);
    348
            assert_eq!(idx3, None);
    349
            assert_eq!(mgr.valid_count(), 3);
    350
        }
    351
    352
        #[test]
    353
        fn test_kv_cache_manager_access() {
    354
            let mut mgr = KvCacheManager::new(3);
    355
    356
            let idx = mgr.allocate(0, 42, 1, 2).unwrap();
    357
    358
            let slot = mgr.access(idx).unwrap();
    359
            assert_eq!(slot.token_id, 42);
    360
            assert_eq!(slot.layer, 1);
    361
            assert_eq!(slot.head, 2);
    362
    363
            // Out of bounds access
    364
            assert!(mgr.access(100).is_none());
    365
        }
    366
    367
        #[test]
    368
        fn test_kv_cache_manager_step() {
    369
            let mut mgr = KvCacheManager::new(2);
    370
            let idx = mgr.allocate(0, 0, 0, 0).unwrap();
    371
    372
            // Step advances counter
    373
            mgr.step();
    374
            mgr.step();
    375
    376
            // Access updates last_access to current step
    377
            let slot = mgr.access(idx).unwrap();
    378
            assert_eq!(slot.last_access, 2);
    379
        }
    380
    381
        #[test]
    382
        fn test_kv_cache_manager_evict_lru() {
    383
            let mut mgr = KvCacheManager::new(3);
    384
    385
            // Allocate with steps between to create LRU ordering
    386
            let idx0 = mgr.allocate(0, 100, 0, 0).unwrap();
    387
            mgr.step();
    388
            let idx1 = mgr.allocate(1, 101, 0, 0).unwrap();
    389
            mgr.step();
    390
            let _idx2 = mgr.allocate(2, 102, 0, 0).unwrap();
    391
            mgr.step();
    392
    393
            // Access idx0 to make it recently used
    394
            mgr.access(idx0);
    395
    396
            // LRU should be idx1 (oldest access without recent touch)
    397
            let evicted = mgr.evict_lru();
    398
            assert_eq!(evicted, Some(idx1));
    399
            assert_eq!(mgr.valid_count(), 2);
    400
        }
    401
    402
        #[test]
    403
        fn test_kv_cache_manager_evict_empty() {
    404
            let mut mgr = KvCacheManager::new(3);
    405
    406
            // No valid slots - nothing to evict
    407
            let evicted = mgr.evict_lru();
    408
            assert_eq!(evicted, None);
    409
        }
    410
    411
        #[test]
    412
        fn test_kv_cache_manager_allocate_after_evict() {
    413
            let mut mgr = KvCacheManager::new(2);
    414
    415
            // Fill up
    416
            mgr.allocate(0, 100, 0, 0).unwrap();
    417
            mgr.step();
    418
            mgr.allocate(1, 101, 0, 0).unwrap();
    419
            assert_eq!(mgr.valid_count(), 2);
    420
    421
            // Full
    422
            assert!(mgr.allocate(2, 102, 0, 0).is_none());
    423
    424
            // Evict one
    425
            mgr.evict_lru();
    426
            assert_eq!(mgr.valid_count(), 1);
    427
    428
            // Now can allocate
    429
            let idx = mgr.allocate(2, 102, 0, 0);
    430
            assert!(idx.is_some());
    431
            assert_eq!(mgr.valid_count(), 2);
    432
        }
    433
    434
        // =========================================================================
    435
        // SequentialBatchOrderer Tests
    436
        // =========================================================================
    437
    438
        #[test]
    439
        fn test_sequential_batch_orderer_new() {
    440
            let orderer = SequentialBatchOrderer::new(5);
    441
    442
            assert_eq!(orderer.remaining(), 5);
    443
            assert!(!orderer.is_done());
    444
        }
    445
    446
        #[test]
    447
        fn test_sequential_batch_orderer_sequential() {
    448
            let mut orderer = SequentialBatchOrderer::new(4);
    449
    450
            assert_eq!(orderer.next_batch(), Some(0));
    451
            assert_eq!(orderer.next_batch(), Some(1));
    452
            assert_eq!(orderer.next_batch(), Some(2));
    453
            assert_eq!(orderer.next_batch(), Some(3));
    454
            assert_eq!(orderer.next_batch(), None);
    455
            assert!(orderer.is_done());
    456
        }
    457
    458
        #[test]
    459
        fn test_sequential_batch_orderer_reversed() {
    460
            let mut orderer = SequentialBatchOrderer::reversed(4);
    461
    462
            assert_eq!(orderer.next_batch(), Some(3));
    463
            assert_eq!(orderer.next_batch(), Some(2));
    464
            assert_eq!(orderer.next_batch(), Some(1));
    465
            assert_eq!(orderer.next_batch(), Some(0));
    466
            assert_eq!(orderer.next_batch(), None);
    467
        }
    468
    469
        #[test]
    470
        fn test_sequential_batch_orderer_interleaved_even() {
    471
            let orderer = SequentialBatchOrderer::interleaved(4);
    472
            let order: Vec<_> = orderer.collect();
    473
    474
            // 4 batches: 0, 2, 1, 3
    475
            assert_eq!(order, vec![0, 2, 1, 3]);
    476
        }
    477
    478
        #[test]
    479
        fn test_sequential_batch_orderer_interleaved_odd() {
    480
            let orderer = SequentialBatchOrderer::interleaved(5);
    481
            let order: Vec<_> = orderer.collect();
    482
    483
            // All indices should be present
    484
            assert_eq!(order.len(), 5);
    485
            let mut sorted = order.clone();
    486
            sorted.sort();
    487
            assert_eq!(sorted, vec![0, 1, 2, 3, 4]);
    488
        }
    489
    490
        #[test]
    491
        fn test_sequential_batch_orderer_reset() {
    492
            let mut orderer = SequentialBatchOrderer::new(3);
    493
    494
            orderer.next_batch();
    495
            orderer.next_batch();
    496
            assert_eq!(orderer.remaining(), 1);
    497
    498
            orderer.reset();
    499
            assert_eq!(orderer.remaining(), 3);
    500
            assert!(!orderer.is_done());
    501
        }
    502
    503
        #[test]
    504
        fn test_sequential_batch_orderer_remaining() {
    505
            let mut orderer = SequentialBatchOrderer::new(5);
    506
    507
            assert_eq!(orderer.remaining(), 5);
    508
            orderer.next_batch();
    509
            assert_eq!(orderer.remaining(), 4);
    510
            orderer.next_batch();
    511
            assert_eq!(orderer.remaining(), 3);
    512
        }
    513
    514
        #[test]
    515
        fn test_sequential_batch_orderer_iterator() {
    516
            let orderer = SequentialBatchOrderer::new(3);
    517
            let batches: Vec<_> = orderer.collect();
    518
    519
            assert_eq!(batches, vec![0, 1, 2]);
    520
        }
    521
    522
        #[test]
    523
        fn test_sequential_batch_orderer_empty() {
    524
            let mut orderer = SequentialBatchOrderer::new(0);
    525
    526
            assert!(orderer.is_done());
    527
            assert_eq!(orderer.remaining(), 0);
    528
            assert_eq!(orderer.next_batch(), None);
    529
        }
    530
    531
        // =========================================================================
    532
        // FALSIFICATION TESTS
    533
        // =========================================================================
    534
    535
        /// FALSIFICATION TEST: KvCacheManager must maintain valid_count invariant
    536
        ///
    537
        /// valid_count must always equal the number of slots with valid=true
    538
        #[test]
    539
        fn test_falsify_kv_cache_valid_count_invariant() {
    540
            let mut mgr = KvCacheManager::new(5);
    541
    542
            // Allocate some
    543
            mgr.allocate(0, 0, 0, 0);
    544
            mgr.allocate(1, 0, 0, 0);
    545
            mgr.allocate(2, 0, 0, 0);
    546
    547
            assert_eq!(
    548
                mgr.valid_count(),
    549
                3,
    550
                "FALSIFICATION FAILED: valid_count should be 3 after 3 allocations"
    551
            );
    552
    553
            // Evict some
    554
            mgr.evict_lru();
    555
            assert_eq!(
    556
                mgr.valid_count(),
    557
                2,
    558
                "FALSIFICATION FAILED: valid_count should be 2 after 1 eviction"
    559
            );
    560
    561
            mgr.evict_lru();
    562
            mgr.evict_lru();
    563
            assert_eq!(
    564
                mgr.valid_count(),
    565
                0,
    566
                "FALSIFICATION FAILED: valid_count should be 0 after all evictions"
    567
            );
    568
    569
            // Can't go negative
    570
            mgr.evict_lru();
    571
            assert_eq!(
    572
                mgr.valid_count(),
    573
                0,
    574
                "FALSIFICATION FAILED: valid_count should not go negative"
    575
            );
    576
        }
    577
    578
        /// FALSIFICATION TEST: LRU eviction must evict oldest-accessed slot
    579
        ///
    580
        /// Given slots with different access times, evict_lru must always
    581
        /// return the slot with the oldest (smallest) last_access.
    582
        #[test]
    583
        fn test_falsify_lru_eviction_order() {
    584
            let mut mgr = KvCacheManager::new(4);
    585
    586
            // Allocate 4 slots with step advances
    587
            let idx0 = mgr.allocate(0, 0, 0, 0).unwrap();
    588
            mgr.step();
    589
            let idx1 = mgr.allocate(1, 0, 0, 0).unwrap();
    590
            mgr.step();
    591
            let idx2 = mgr.allocate(2, 0, 0, 0).unwrap();
    592
            mgr.step();
    593
            let idx3 = mgr.allocate(3, 0, 0, 0).unwrap();
    594
            mgr.step();
    595
    596
            // Access slots in specific order to set LRU
    597
            // Touch idx2, then idx0 - making idx1, idx3 the oldest
    598
            mgr.access(idx2);
    599
            mgr.step();
    600
            mgr.access(idx0);
    601
            mgr.step();
    602
    603
            // First eviction should be idx1 (oldest not accessed)
    604
            let evicted1 = mgr.evict_lru().unwrap();
    605
            assert_eq!(
    606
                evicted1, idx1,
    607
                "FALSIFICATION FAILED: First eviction should be idx1 (step 1)"
    608
            );
    609
    610
            // Second eviction should be idx3 (next oldest)
    611
            let evicted2 = mgr.evict_lru().unwrap();
    612
            assert_eq!(
    613
                evicted2, idx3,
    614
                "FALSIFICATION FAILED: Second eviction should be idx3 (step 3)"
    615
            );
    616
        }
    617
    618
        /// FALSIFICATION TEST: SequentialBatchOrderer must cover all indices
    619
        ///
    620
        /// After iteration completes, all indices 0..n must have been returned
    621
        /// exactly once.
    622
        #[test]
    623
        fn test_falsify_batch_orderer_covers_all_indices() {
    624
            for n_batches in [1, 2, 3, 5, 10, 100] {
    625
                for orderer in [
    626
                    SequentialBatchOrderer::new(n_batches),
    627
                    SequentialBatchOrderer::reversed(n_batches),
    628
                    SequentialBatchOrderer::interleaved(n_batches),
    629
                ] {
    630
                    let batches: Vec<_> = orderer.collect();
    631
    632
                    // Must have exactly n_batches elements
    633
                    assert_eq!(
    634
                        batches.len(),
    635
                        n_batches,
    636
                        "FALSIFICATION FAILED: orderer returned {} batches, expected {}",
    637
                        batches.len(),
    638
                        n_batches
    639
                    );
    640
    641
                    // Sort and verify all indices present
    642
                    let mut sorted = batches.clone();
    643
                    sorted.sort();
    644
                    let expected: Vec<_> = (0..n_batches).collect();
    645
                    assert_eq!(
    646
                        sorted, expected,
    647
                        "FALSIFICATION FAILED: orderer did not return all indices 0..{}",
    648
                        n_batches
    649
                    );
    650
                }
    651
            }
    652
        }
    653
    654
        /// FALSIFICATION TEST: Eviction priority must be monotonic with age
    655
        ///
    656
        /// For valid slots, older access time = higher eviction priority.
    657
        #[test]
    658
        fn test_falsify_eviction_priority_monotonic() {
    659
            let mut slot = KvCacheSlotInfo::new(0, 0, 0, 0);
    660
            slot.touch(10);
    661
    662
            let mut prev_priority = slot.eviction_priority(10);
    663
            for current_step in 11..=100 {
    664
                let priority = slot.eviction_priority(current_step);
    665
                assert!(
    666
                    priority >= prev_priority,
    667
                    "FALSIFICATION FAILED: eviction priority decreased from {} to {} at step {}",
    668
                    prev_priority,
    669
                    priority,
    670
                    current_step
    671
                );
    672
                prev_priority = priority;
    673
            }
    674
        }
    675
    }