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}