memory/
test.rs

1//! Test template for `ManagerTrait` implementations.
2//!
3//! This module provides a comprehensive test suite that can be used to validate
4//! any implementation of the `ManagerTrait`. The tests cover all the core functionality
5//! including allocation, deallocation, reallocation, and cache management.
6//!
7//! # Usage
8//!
9//! ## Using the Macro (Recommended)
10//!
11//! The easiest way to test your memory manager implementation is to use the
12//! `implement_memory_manager_tests!` macro, which generates individual test
13//! functions for each test category:
14//!
15//! ```rust,ignore
16//! use memory::implement_memory_manager_tests;
17//!
18//! #[cfg(test)]
19//! mod tests {
20//!     use super::*;
21//!     use memory::{Manager, ManagerTrait};
22//!     
23//!     struct MyAllocator;
24//!     impl ManagerTrait for MyAllocator {
25//!         // ... implementation ...
26//!     }
27//!     
28//!     static ALLOCATOR: MyAllocator = MyAllocator;
29//!     
30//!     implement_memory_manager_tests! {
31//!         Manager::new(&ALLOCATOR)
32//!     }
33//! }
34//! ```
35//!
36//! This will generate 14 individual `#[test]` functions, each testing a specific
37//! aspect of your memory manager implementation.
38//!
39//! ## Using Individual Test Functions
40//!
41//! You can also call individual test functions directly:
42//!
43//! ```rust,ignore
44//! use memory::{Manager, test_template};
45//!
46//! #[cfg(test)]
47//! mod tests {
48//!     use super::*;
49//!
50//!     fn get_test_manager() -> Manager<'static> {
51//!         static ALLOCATOR: YourAllocatorType = YourAllocatorType::new();
52//!         Manager::new(&ALLOCATOR)
53//!     }
54//!
55//!     #[test]
56//!     fn test_basic_allocation() {
57//!         let manager = get_test_manager();
58//!         test_template::test_basic_allocation($manager);
59//!     }
60//!
61//!     #[test]
62//!     fn test_all() {
63//!         let manager = get_test_manager();
64//!         test_template::run_all_tests($manager);
65//!     }
66//! }
67//! ```
68//!
69//! # Test Categories
70//!
71//! ## Basic Allocation Tests
72//! - `test_basic_allocation` - Basic allocation and deallocation
73//! - `test_zero_sized_allocation` - Zero-size allocation handling
74//! - `test_aligned_allocation` - Various alignment requirements
75//! - `test_allocation_with_capabilities` - Capability-based allocation
76//!
77//! ## Deallocation & Reallocation Tests
78//! - `test_deallocation` - Memory deallocation and usage tracking
79//! - `test_reallocation_grow` - Growing allocations
80//! - `test_reallocation_shrink` - Shrinking allocations
81//! - `test_reallocation_same_size` - Same-size reallocation
82//!
83//! ## Complex Operations Tests
84//! - `test_multiple_allocations` - Multiple simultaneous allocations
85//! - `test_allocation_pattern` - Complex allocation/deallocation patterns
86//! - `test_large_allocation` - Large memory allocations
87//!
88//! ## Manager Features Tests
89//! - `test_memory_statistics` - Memory usage statistics
90//! - `test_page_size` - Page size reporting
91//! - `test_cache_flush_operations` - Cache management operations
92
93use crate::{CapabilityFlags, Layout, Manager, ManagerTrait};
94use alloc::vec::Vec;
95
96/// Tests basic memory allocation.
97///
98/// Verifies that the manager can allocate memory with a simple layout
99/// and default capabilities, and that the returned pointer is valid.
100///
101/// # Parameters
102/// * `manager` - The memory manager to test
103///
104/// # Panics
105/// Panics if allocation fails or returns a null pointer.
106pub fn test_basic_allocation(allocator: impl ManagerTrait) {
107    let manager = Manager::new(&allocator);
108
109    let layout = Layout::from_size_align(64, 8).unwrap();
110    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
111
112    assert!(
113        !ptr.is_null(),
114        "Basic allocation should not return null pointer"
115    );
116
117    // Verify we can write to the allocated memory
118    unsafe {
119        core::ptr::write_bytes(ptr, 0xAA, layout.size());
120
121        // Verify the write worked
122        for i in 0..layout.size() {
123            assert_eq!(*ptr.add(i), 0xAA, "Memory should be writable");
124        }
125
126        manager.deallocate(ptr, layout);
127    }
128}
129
130/// Tests zero-sized allocation behavior.
131///
132/// Verifies that allocating zero bytes returns a null pointer as expected.
133///
134/// # Parameters
135/// * `manager` - The memory manager to test
136pub fn test_zero_sized_allocation(allocator: impl ManagerTrait) {
137    let manager = Manager::new(&allocator);
138
139    let layout = Layout::from_size_align(0, 1).unwrap();
140    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
141
142    assert!(
143        ptr.is_null(),
144        "Zero-sized allocation should return null pointer"
145    );
146}
147
148/// Tests allocation with specific alignment requirements.
149///
150/// Verifies that the manager respects alignment requirements and returns
151/// properly aligned memory addresses.
152///
153/// # Parameters
154/// * `manager` - The memory manager to test
155///
156/// # Panics
157/// Panics if allocation fails or the returned pointer is not properly aligned.
158pub fn test_aligned_allocation(allocator: impl ManagerTrait) {
159    let manager = Manager::new(&allocator);
160
161    let alignments = [8, 16, 32, 64, 128];
162
163    for &align in &alignments {
164        let layout = Layout::from_size_align(256, align).unwrap();
165        let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
166
167        assert!(
168            !ptr.is_null(),
169            "Aligned allocation should not return null pointer"
170        );
171        assert_eq!(
172            ptr as usize % align,
173            0,
174            "Pointer should be aligned to {} bytes",
175            align
176        );
177
178        unsafe { manager.deallocate(ptr, layout) };
179    }
180}
181
182/// Tests allocation with different capability flags.
183///
184/// Verifies that the manager can handle allocations with different capability
185/// requirements (executable, direct memory access, etc.).
186///
187/// # Parameters
188/// * `manager` - The memory manager to test
189///
190/// # Panics
191/// Panics if allocation with capabilities fails.
192pub fn test_allocation_with_capabilities(allocator: impl ManagerTrait) {
193    let manager = Manager::new(&allocator);
194
195    let layout = Layout::from_size_align(128, 8).unwrap();
196
197    // Test executable capability
198    let caps_exec = CapabilityFlags::Executable;
199    let ptr = unsafe { manager.allocate(caps_exec, layout) };
200    assert!(
201        !ptr.is_null(),
202        "Allocation with executable capability should succeed"
203    );
204    unsafe { manager.deallocate(ptr, layout) };
205
206    // Test DMA capability
207    // let caps_dma = Capabilities::new(false, true);
208    // let ptr = unsafe { manager.allocate(caps_dma, layout) };
209    // assert!(
210    //     !ptr.is_null(),
211    //     "Allocation with DMA capability should succeed"
212    // );
213    // unsafe { manager.deallocate(ptr, layout) };
214
215    // Test both capabilities
216    // let caps_both = Capabilities::new(true, true);
217    // let ptr = unsafe { manager.allocate(caps_both, layout) };
218    // assert!(
219    //     !ptr.is_null(),
220    //     "Allocation with both capabilities should succeed"
221    // );
222    // unsafe { manager.deallocate(ptr, layout) };
223}
224
225/// Tests memory deallocation.
226///
227/// Verifies that memory can be allocated and then safely deallocated.
228///
229/// # Parameters
230/// * `manager` - The memory manager to test
231pub fn test_deallocation(allocator: impl ManagerTrait) {
232    let manager = Manager::new(&allocator);
233
234    let layout = Layout::from_size_align(128, 8).unwrap();
235
236    let initial_used = manager.get_used();
237
238    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
239    assert!(!ptr.is_null(), "Allocation should succeed");
240
241    let used_after_alloc = manager.get_used();
242    assert!(
243        used_after_alloc >= initial_used,
244        "Used memory should increase after allocation"
245    );
246
247    unsafe { manager.deallocate(ptr, layout) };
248
249    // Note: The used memory might not return exactly to initial_used due to
250    // allocator overhead, but it should not increase further
251}
252/// Tests memory reallocation to a larger size.
253///
254/// Verifies that the manager can grow an existing allocation and preserve
255/// the original data.
256///
257/// # Parameters
258/// * `manager` - The memory manager to test
259///
260/// # Panics
261/// Panics if reallocation fails or data is not preserved.
262pub fn test_reallocation_grow(allocator: impl ManagerTrait) {
263    let manager = Manager::new(&allocator);
264    let initial_size = 64;
265    let new_size = 256;
266    let layout = Layout::from_size_align(initial_size, 8).unwrap();
267
268    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
269    assert!(!ptr.is_null(), "Initial allocation should succeed");
270
271    // Write pattern to memory
272    unsafe {
273        for i in 0..initial_size {
274            *ptr.add(i) = (i % 256) as u8;
275        }
276    }
277
278    // Reallocate to larger size
279    let new_ptr = unsafe { manager.reallocate(ptr, layout, new_size) };
280    assert!(!new_ptr.is_null(), "Reallocation should succeed");
281
282    // Verify original data is preserved
283    unsafe {
284        for i in 0..initial_size {
285            assert_eq!(
286                *new_ptr.add(i),
287                (i % 256) as u8,
288                "Original data should be preserved after reallocation"
289            );
290        }
291    }
292
293    let new_layout = Layout::from_size_align(new_size, 8).unwrap();
294    unsafe { manager.deallocate(new_ptr, new_layout) };
295}
296
297/// Tests memory reallocation to a smaller size.
298///
299/// Verifies that the manager can shrink an existing allocation and preserve
300/// data up to the new size.
301///
302/// # Parameters
303/// * `manager` - The memory manager to test
304///
305/// # Panics
306/// Panics if reallocation fails or data is not preserved.
307pub fn test_reallocation_shrink(allocator: impl ManagerTrait) {
308    let manager = Manager::new(&allocator);
309    let initial_size = 256;
310    let new_size = 64;
311    let layout = Layout::from_size_align(initial_size, 8).unwrap();
312
313    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
314    assert!(!ptr.is_null(), "Initial allocation should succeed");
315
316    // Write pattern to memory
317    unsafe {
318        for i in 0..initial_size {
319            *ptr.add(i) = (i % 256) as u8;
320        }
321    }
322
323    // Reallocate to smaller size
324    let new_ptr = unsafe { manager.reallocate(ptr, layout, new_size) };
325    assert!(!new_ptr.is_null(), "Reallocation should succeed");
326
327    // Verify data up to new size is preserved
328    unsafe {
329        for i in 0..new_size {
330            assert_eq!(
331                *new_ptr.add(i),
332                (i % 256) as u8,
333                "Data should be preserved up to new size after reallocation"
334            );
335        }
336    }
337
338    let new_layout = Layout::from_size_align(new_size, 8).unwrap();
339    unsafe { manager.deallocate(new_ptr, new_layout) };
340}
341
342/// Tests reallocation with the same size.
343///
344/// Verifies that reallocating with the same size either returns the same pointer
345/// or successfully creates a new allocation with preserved data.
346///
347/// # Parameters
348/// * `manager` - The memory manager to test
349pub fn test_reallocation_same_size(allocator: impl ManagerTrait) {
350    let manager = Manager::new(&allocator);
351    let size = 128;
352    let layout = Layout::from_size_align(size, 8).unwrap();
353
354    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
355    assert!(!ptr.is_null(), "Initial allocation should succeed");
356
357    // Write pattern to memory
358    unsafe {
359        for i in 0..size {
360            *ptr.add(i) = (i % 256) as u8;
361        }
362    }
363
364    // Reallocate with same size
365    let new_ptr = unsafe { manager.reallocate(ptr, layout, size) };
366    assert!(!new_ptr.is_null(), "Reallocation should succeed");
367
368    // Verify data is preserved
369    unsafe {
370        for i in 0..size {
371            assert_eq!(
372                *new_ptr.add(i),
373                (i % 256) as u8,
374                "Data should be preserved after same-size reallocation"
375            );
376        }
377    }
378
379    unsafe { manager.deallocate(new_ptr, layout) };
380}
381
382/// Tests multiple simultaneous allocations.
383///
384/// Verifies that the manager can handle multiple allocations at once
385/// without interference.
386///
387/// # Parameters
388/// * `manager` - The memory manager to test
389///
390/// # Panics
391/// Panics if any allocation fails or data is corrupted.
392pub fn test_multiple_allocations(allocator: impl ManagerTrait) {
393    let manager = Manager::new(&allocator);
394    const NUM_ALLOCATIONS: usize = 10;
395    let mut allocations = [(
396        core::ptr::null_mut::<u8>(),
397        Layout::from_size_align(1, 1).unwrap(),
398    ); NUM_ALLOCATIONS];
399
400    // Allocate multiple blocks
401    for (i, allocation) in allocations.iter_mut().enumerate().take(NUM_ALLOCATIONS) {
402        let size = 64 + i * 32;
403        let layout = Layout::from_size_align(size, 8).unwrap();
404        let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
405
406        assert!(!ptr.is_null(), "Allocation {} should succeed", i);
407
408        // Write unique pattern to each allocation
409        unsafe {
410            for j in 0..size {
411                *ptr.add(j) = ((i + j) % 256) as u8;
412            }
413        }
414
415        *allocation = (ptr, layout);
416    }
417
418    // Verify all allocations still have correct data
419    for (i, &(ptr, layout)) in allocations.iter().enumerate().take(NUM_ALLOCATIONS) {
420        let size = layout.size();
421
422        unsafe {
423            for j in 0..size {
424                assert_eq!(
425                    *ptr.add(j),
426                    ((i + j) % 256) as u8,
427                    "Data should be preserved in allocation {}",
428                    i
429                );
430            }
431        }
432    }
433
434    // Deallocate all blocks
435    for &(ptr, layout) in allocations.iter().take(NUM_ALLOCATIONS) {
436        unsafe { manager.deallocate(ptr, layout) };
437    }
438}
439
440/// Tests memory usage statistics.
441///
442/// Verifies that the manager correctly tracks used and free memory.
443///
444/// # Parameters
445/// * `manager` - The memory manager to test
446pub fn test_memory_statistics(allocator: impl ManagerTrait) {
447    let manager = Manager::new(&allocator);
448    let initial_used = manager.get_used();
449    //let initial_free = manager.get_free();
450
451    let layout = Layout::from_size_align(1024, 8).unwrap();
452    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
453
454    assert!(!ptr.is_null(), "Allocation should succeed");
455
456    let used_after_alloc = manager.get_used();
457    //let free_after_alloc = manager.get_free(); // Used memory should increase
458    assert!(
459        used_after_alloc >= initial_used,
460        "Used memory should increase after allocation"
461    );
462
463    // Free memory should decrease or stay the same
464    // assert!(
465    //     free_after_alloc <= initial_free,
466    //     "Free memory should decrease or stay the same after allocation"
467    // );
468
469    unsafe { manager.deallocate(ptr, layout) };
470}
471
472/// Tests the page size reporting.
473///
474/// Verifies that the manager reports a valid page size.
475///
476/// # Parameters
477/// * `manager` - The memory manager to test
478pub fn test_page_size(allocator: impl ManagerTrait) {
479    let manager = Manager::new(&allocator);
480    let page_size = manager.get_page_size();
481
482    assert!(page_size > 0, "Page size should be greater than zero");
483    assert!(
484        page_size.is_power_of_two(),
485        "Page size should be a power of two"
486    );
487}
488
489/// Tests cache flush operations.
490///
491/// Verifies that cache flush operations can be called without panicking.
492/// Note: This doesn't verify actual cache behavior as that's hardware-specific.
493///
494/// # Parameters
495/// * `manager` - The memory manager to test
496pub fn test_cache_flush_operations(allocator: impl ManagerTrait) {
497    let manager = Manager::new(&allocator);
498    // Test data cache flush
499    manager.flush_data_cache();
500
501    // Test instruction cache flush with allocated memory
502    let layout = Layout::from_size_align(256, 8).unwrap();
503    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
504
505    if !ptr.is_null() {
506        unsafe {
507            manager.flush_instruction_cache(ptr, layout.size());
508            manager.deallocate(ptr, layout);
509        }
510    }
511}
512
513/// Tests large memory allocation.
514///
515/// Verifies that the manager can handle larger allocations.
516///
517/// # Parameters
518/// * `manager` - The memory manager to test
519pub fn test_large_allocation(allocator: impl ManagerTrait) {
520    let manager = Manager::new(&allocator);
521    let large_size = 1024 * 1024; // 1 MB
522    let layout = Layout::from_size_align(large_size, 8).unwrap();
523
524    let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
525
526    if !ptr.is_null() {
527        // If allocation succeeded, verify we can use the memory
528        unsafe {
529            *ptr = 0xFF;
530            *ptr.add(large_size - 1) = 0xFF;
531
532            assert_eq!(
533                *ptr, 0xFF,
534                "Should be able to write to start of large allocation"
535            );
536            assert_eq!(
537                *ptr.add(large_size - 1),
538                0xFF,
539                "Should be able to write to end of large allocation"
540            );
541
542            manager.deallocate(ptr, layout);
543        }
544    }
545    // Note: It's acceptable for very large allocations to fail on some systems
546}
547
548/// Tests allocation and deallocation pattern.
549///
550/// Verifies that the manager can handle a complex pattern of allocations
551/// and deallocations without memory leaks or corruption.
552///
553/// # Parameters
554/// * `manager` - The memory manager to test
555pub fn test_allocation_pattern(allocator: impl ManagerTrait) {
556    let manager = Manager::new(&allocator);
557    let initial_used = manager.get_used();
558
559    // Pattern: allocate 5, deallocate 2, allocate 3, deallocate all
560    let mut ptrs = Vec::new(); // Allocate 5
561    for i in 0..5 {
562        let size = 128 + i * 64;
563        let layout = Layout::from_size_align(size, 8).unwrap();
564        let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
565        assert!(!ptr.is_null(), "Allocation should succeed");
566        ptrs.push((ptr, layout));
567    }
568
569    // Deallocate 2
570    for _ in 0..2 {
571        if let Some((ptr, layout)) = ptrs.pop() {
572            unsafe { manager.deallocate(ptr, layout) };
573        }
574    }
575
576    // Allocate 3 more
577    for i in 0..3 {
578        let size = 96 + i * 32;
579        let layout = Layout::from_size_align(size, 8).unwrap();
580        let ptr = unsafe { manager.allocate(CapabilityFlags::None, layout) };
581        assert!(!ptr.is_null(), "Allocation should succeed");
582        ptrs.push((ptr, layout));
583    }
584
585    // Deallocate all
586    for (ptr, layout) in ptrs {
587        unsafe { manager.deallocate(ptr, layout) };
588    }
589
590    let final_used = manager.get_used();
591
592    // Memory usage should be close to initial (allowing for some allocator overhead)
593    assert!(
594        final_used <= initial_used + 1024,
595        "Memory should be mostly freed after pattern completion"
596    );
597}
598
599/// Macro to implement all memory manager tests for a given `ManagerTrait` implementation.
600///
601/// This macro generates individual test functions for each test category, making it easy
602/// to integrate comprehensive memory manager testing into your test suite.
603///
604/// # Usage
605///
606/// ```rust,ignore
607/// use memory::implement_memory_manager_tests;
608///
609/// mod tests {
610///     use super::*;
611///     use memory::{Manager, ManagerTrait};
612///     
613///     struct MyAllocator;
614///     impl ManagerTrait for MyAllocator {
615///         // ... implementation ...
616///     }
617///     
618///     static ALLOCATOR: MyAllocator = MyAllocator;
619///     
620///     implement_memory_manager_tests! {
621///         Manager::new(&ALLOCATOR)
622///     }
623/// }
624/// ```
625///
626/// This will generate individual `#[test]` functions for each test category:
627/// - test_basic_allocation
628/// - test_zero_sized_allocation
629/// - test_aligned_allocation
630/// - test_allocation_with_capabilities
631/// - test_deallocation
632/// - test_reallocation_grow
633/// - test_reallocation_shrink
634/// - test_reallocation_same_size
635/// - test_multiple_allocations
636/// - test_memory_statistics
637/// - test_page_size
638/// - test_cache_flush_operations
639/// - test_large_allocation
640/// - test_allocation_pattern
641#[macro_export]
642macro_rules! implement_memory_manager_tests {
643    ($manager:expr) => {
644        #[test]
645        fn test_basic_allocation() {
646            $crate::test::test_basic_allocation($manager);
647        }
648
649        #[test]
650        fn test_zero_sized_allocation() {
651            $crate::test::test_zero_sized_allocation($manager);
652        }
653
654        #[test]
655        fn test_aligned_allocation() {
656            $crate::test::test_aligned_allocation($manager);
657        }
658
659        #[test]
660        fn test_allocation_with_capabilities() {
661            $crate::test::test_allocation_with_capabilities($manager);
662        }
663
664        #[test]
665        fn test_deallocation() {
666            $crate::test::test_deallocation($manager);
667        }
668
669        #[test]
670        fn test_reallocation_grow() {
671            $crate::test::test_reallocation_grow($manager);
672        }
673
674        #[test]
675        fn test_reallocation_shrink() {
676            $crate::test::test_reallocation_shrink($manager);
677        }
678
679        #[test]
680        fn test_reallocation_same_size() {
681            $crate::test::test_reallocation_same_size($manager);
682        }
683
684        #[test]
685        fn test_multiple_allocations() {
686            $crate::test::test_multiple_allocations($manager);
687        }
688
689        #[test]
690        fn test_memory_statistics() {
691            $crate::test::test_memory_statistics($manager);
692        }
693
694        #[test]
695        fn test_page_size() {
696            $crate::test::test_page_size($manager);
697        }
698
699        #[test]
700        fn test_cache_flush_operations() {
701            $crate::test::test_cache_flush_operations($manager);
702        }
703
704        #[test]
705        fn test_large_allocation() {
706            $crate::test::test_large_allocation($manager);
707        }
708
709        #[test]
710        fn test_allocation_pattern() {
711            $crate::test::test_allocation_pattern($manager);
712        }
713    };
714}