1+ /*
2+ * COPYRIGHT: See COPYING in the top level directory
3+ * PROJECT: MemoryManager.Tests
4+ * FILE: MemoryArenaTests.cs
5+ * PURPOSE: Exhaustive integration and stress verification for the MemoryArena wrapper.
6+ * PROGRAMMER: Peter Geinitz (Wayfarer)
7+ */
8+
9+ using MemoryManager . Core ;
10+
11+ namespace MemoryManager . Tests
12+ {
13+ [ TestClass ]
14+ public class MemoryArenaCoreIntegrationTests
15+ {
16+ private MemoryManagerConfig _config ;
17+
18+ [ TestInitialize ]
19+ public void Setup ( )
20+ {
21+ _config = new MemoryManagerConfig
22+ {
23+ FastLaneSize = 256 * 1024 , // 256 KB
24+ SlowLaneSize = 1024 * 1024 , // 1 MB
25+ Threshold = 4 * 1024 , // 4 KB Lane Boundary
26+ FastLaneUsageThreshold = 0.85f ,
27+ SlowLaneUsageThreshold = 0.80f ,
28+ CompactionThreshold = 0.85f ,
29+ SlowLaneSafetyMargin = 0.10f ,
30+ EnableAutoCompaction = false , // Controlled manually within integration steps
31+ PolicyCheckInterval = TimeSpan . Zero
32+ } ;
33+ }
34+
35+ #region --- SECTION 1: LATEST ROUTING & WORKSPACE BOUNDARY RULES ---
36+
37+ [ TestMethod ]
38+ public void Allocate_SizeBelowThreshold_RoutesToFastLane ( )
39+ {
40+ var arena = new MemoryArena ( _config ) ;
41+ int size = 2 * 1024 ; // 2 KB (< 4 KB Threshold)
42+
43+ var handle = arena . Allocate ( size ) ;
44+
45+ Assert . IsTrue ( handle . Id > 0 , "Allocations under the size threshold must receive a positive FastLane ID." ) ;
46+ Assert . IsFalse ( handle . IsInvalid , "Returned handle must declare proof of life." ) ;
47+ }
48+
49+ [ TestMethod ]
50+ public void Allocate_SizeAboveThreshold_RoutesToSlowLane ( )
51+ {
52+ var arena = new MemoryArena ( _config ) ;
53+ int size = 8 * 1024 ; // 8 KB (> 4 KB Threshold)
54+
55+ var handle = arena . Allocate ( size ) ;
56+
57+ Assert . IsTrue ( handle . Id < 0 , "Allocations over the size threshold must receive a negative SlowLane ID." ) ;
58+ }
59+
60+ [ TestMethod ]
61+ public void Allocate_SizeSpansEntireFastLane_FallbackRoutesToSlowLane ( )
62+ {
63+ var arena = new MemoryArena ( _config ) ;
64+ // Request size safely underneath the threshold boundary routing rule,
65+ // but configure it to request more space than the entire FastLane block contains.
66+ var tightConfig = new MemoryManagerConfig
67+ {
68+ FastLaneSize = 1024 ,
69+ SlowLaneSize = 64 * 1024 ,
70+ Threshold = 2048 // Rules technically point to FastLane
71+ } ;
72+ var smallArena = new MemoryArena ( tightConfig ) ;
73+
74+ var handle = smallArena . Allocate ( 1500 ) ;
75+
76+ Assert . IsTrue ( handle . Id < 0 , "If the FastLane cannot fit the requested block size, the orchestrator must fallback to the SlowLane." ) ;
77+ }
78+
79+ [ TestMethod ]
80+ public void Allocate_BothLanesExhausted_ThrowsOutOfMemoryException ( )
81+ {
82+ var arena = new MemoryArena ( _config ) ;
83+ int impossibleSize = 2 * 1024 * 1024 ; // 2 MB (Exceeds total system capacity allocation bounds)
84+
85+ Assert . ThrowsException < OutOfMemoryException > ( ( ) =>
86+ {
87+ arena . Allocate ( impossibleSize ) ;
88+ } , "Requesting more memory than total workspace partitions allow must throw an explicit OutOfMemoryException." ) ;
89+ }
90+
91+ #endregion
92+
93+ #region --- SECTION 2: SYNTACTIC SUGAR & EXTENSION STRUCT VALIDATION ---
94+
95+ [ TestMethod ]
96+ public void Extensions_StoreAndGetPrimitive_MaintainsValueFlawlessly ( )
97+ {
98+ var arena = new MemoryArena ( _config ) ;
99+ double diagnosticValue = 12345.67890 ;
100+
101+ var handle = arena . Store ( diagnosticValue ) ;
102+ double retrievedValue = arena . Get < double > ( handle ) ;
103+
104+ Assert . AreEqual ( diagnosticValue , retrievedValue , "Primitive type serialization extensions must guarantee bitwise alignment reading back data values." ) ;
105+ }
106+
107+ [ TestMethod ]
108+ public void Extensions_AllocateArrayAndBulkSetSpan_ValidatesDataBoundaries ( )
109+ {
110+ var arena = new MemoryArena ( _config ) ;
111+ int [ ] sourceData = { 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 } ;
112+
113+ var handle = arena . AllocateArray < int > ( sourceData . Length ) ;
114+ arena . BulkSet ( handle , sourceData . AsSpan ( ) ) ;
115+
116+ var outputSpan = arena . GetSpan < int > ( handle , sourceData . Length ) ;
117+
118+ CollectionAssert . AreEqual ( sourceData , outputSpan . ToArray ( ) , "Bulk span transfers onto flat allocated structural block memory coordinates failed data parity checks." ) ;
119+ }
120+
121+ [ TestMethod ]
122+ public void Extensions_BulkSetOutOfBoundsPayload_ThrowsArgumentException ( )
123+ {
124+ var arena = new MemoryArena ( _config ) ;
125+ var handle = arena . AllocateArray < short > ( 5 ) ; // Space reserved for 10 bytes
126+ short [ ] overflowingPayload = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } ; // Payload size requirements = 16 bytes
127+
128+ Assert . ThrowsException < ArgumentException > ( ( ) =>
129+ {
130+ arena . BulkSet < short > ( handle , overflowingPayload ) ;
131+ } , "Writing an array payload that exceeds the structural entry allocation size boundaries must throw an explicit ArgumentException." ) ;
132+ }
133+
134+ [ TestMethod ]
135+ public void Extensions_StoreStringUTF8_DecodesAccurately ( )
136+ {
137+ var arena = new MemoryArena ( _config ) ;
138+ string initialText = "Wayfarer Memory Manager Test String Token #2026!" ;
139+
140+ var handle = arena . StoreString ( initialText ) ;
141+ var entry = arena . GetEntry ( handle ) ;
142+
143+ var payloadSpan = arena . GetSpan < byte > ( handle , entry . Size ) ;
144+ string reconstitutedText = System . Text . Encoding . UTF8 . GetString ( payloadSpan ) ;
145+
146+ Assert . AreEqual ( initialText , reconstitutedText , "String serialization extensions must accurately marshal strings to UTF8 workspace tracks." ) ;
147+ }
148+
149+ #endregion
150+
151+ #region --- SECTION 3: ADVANCED MIGRATION & REVERSAL STRESS ---
152+
153+ [ TestMethod ]
154+ public void MoveSlowToFast_ValidMigration_TransfersDataAndFreesOldSlot ( )
155+ {
156+ var arena = new MemoryArena ( _config ) ;
157+ var baselineStruct = new SampleMetric { IdField = 99 , Velocity = 451.2f } ;
158+
159+ var slowHandle = arena . Allocate ( 8 * 1024 , AllocationPriority . Critical , AllocationHints . None ) ;
160+ arena . Get < SampleMetric > ( slowHandle ) = baselineStruct ;
161+
162+ Assert . IsTrue ( slowHandle . Id < 0 , "Pre-condition failure: test value must start inside SlowLane territory." ) ;
163+
164+ // 2. Migrate the layout structural record upward into the FastLane
165+ var fastHandle = arena . MoveSlowToFast ( slowHandle ) ;
166+
167+ // 3. Assert structural position flipping properties
168+ Assert . IsTrue ( fastHandle . Id > 0 , "The newly assigned structural reference handle must point to the FastLane namespace." ) ;
169+
170+ var migratedStruct = arena . Get < SampleMetric > ( fastHandle ) ;
171+ Assert . AreEqual ( baselineStruct . IdField , migratedStruct . IdField , "Data properties dropped or tracking corruption noted during lane promotion." ) ;
172+ Assert . AreEqual ( baselineStruct . Velocity , migratedStruct . Velocity , "Floating point alignment errors inside structure allocation layout shifts." ) ;
173+
174+ // 4. Assert the old SlowLane handles are cleanly destroyed
175+ Assert . ThrowsException < InvalidOperationException > ( ( ) =>
176+ {
177+ arena . Resolve ( slowHandle ) ;
178+ } , "Resolving the old slow lane index handle after execution transfers must trigger invalid token failures." ) ;
179+ }
180+
181+ [ TestMethod ]
182+ public void MoveSlowToFast_PassingPositiveIdHandle_ThrowsArgumentException ( )
183+ {
184+ var arena = new MemoryArena ( _config ) ;
185+ var fastHandle = arena . Store ( 42 ) ;
186+
187+ Assert . ThrowsException < ArgumentException > ( ( ) =>
188+ {
189+ arena . MoveSlowToFast ( fastHandle ) ;
190+ } , "Invoking a slow-to-fast migration using a fast handle identifier must crash validation steps immediately." ) ;
191+ }
192+
193+ #endregion
194+
195+ #region --- SECTION 4: CONCURRENCY LOCKOUT & MULTITHREAD STRESS ---
196+
197+ [ TestMethod ]
198+ public void Concurrency_ParallelAllocationsAndBackgroundCompaction_MaintainsStateSanity ( )
199+ {
200+ var threadedConfig = new MemoryManagerConfig
201+ {
202+ FastLaneSize = 512 * 1024 ,
203+ SlowLaneSize = 2 * 1024 * 1024 ,
204+ Threshold = 512 ,
205+ EnableAutoCompaction = false
206+ } ;
207+ var arena = new MemoryArena ( threadedConfig ) ;
208+
209+ int taskCount = 8 ;
210+ int allocationsPerTask = 100 ;
211+ var barrier = new Barrier ( taskCount + 1 ) ; // Blocks synched tracks + 1 compaction thread
212+
213+ var allocatedHandles = new System . Collections . Concurrent . ConcurrentBag < MemoryHandle > ( ) ;
214+
215+ // Thread Loop A: Concurrent allocations executing against shared arena tracks
216+ var allocationTasks = Enumerable . Range ( 0 , taskCount ) . Select ( t => Task . Run ( ( ) =>
217+ {
218+ barrier . SignalAndWait ( ) ; // Synchronize all workers for high contention
219+ for ( int i = 0 ; i < allocationsPerTask ; i ++ )
220+ {
221+ var handle = arena . AllocateAndStore ( i + ( t * 1000 ) ) ;
222+ allocatedHandles . Add ( handle ) ;
223+
224+ // Simulate processing cycles by resolving and updating values immediately
225+ ref int currentVal = ref arena . Get < int > ( handle ) ;
226+ currentVal += 5 ;
227+ }
228+ } ) ) . ToList ( ) ;
229+
230+ // Thread Loop B: Trigger active maintenance cycles concurrently during operations
231+ var compactionTask = Task . Run ( ( ) =>
232+ {
233+ barrier . SignalAndWait ( ) ;
234+ for ( int i = 0 ; i < 5 ; i ++ )
235+ {
236+ arena . RunMaintenanceCycle ( ) ;
237+ arena . CompactAll ( ) ;
238+ Thread . Sleep ( 5 ) ; // Force brief scheduling gaps to hit race conditions
239+ }
240+ } ) ;
241+
242+ // Wait for full work execution completion
243+ Task . WaitAll ( allocationTasks . Concat ( new [ ] { compactionTask } ) . ToArray ( ) ) ;
244+
245+ // Post-execution data verification block
246+ Assert . AreEqual ( taskCount * allocationsPerTask , allocatedHandles . Count , "Total completed allocation counts do not match configured expectations." ) ;
247+
248+ foreach ( var handle in allocatedHandles )
249+ {
250+ // Every resolved int value should remain accessible and valid
251+ int storedVal = arena . Get < int > ( handle ) ;
252+ Assert . IsTrue ( storedVal >= 5 , "Unsynchronized background data sweeps corrupted active memory data footprints." ) ;
253+ }
254+ }
255+
256+ #endregion
257+
258+ #region --- SECTION 5: SAFETY BOUNDARIES & EXPIRED TOKENS ---
259+
260+ [ TestMethod ]
261+ public void Security_InvalidHandleIdentifiers_ThrowException ( )
262+ {
263+ var arena = new MemoryArena ( _config ) ;
264+ var invalidHandle = new MemoryHandle ( 0 , 1 , arena . FastLane ) ; // 0 token boundary rule
265+
266+ Assert . ThrowsException < InvalidOperationException > ( ( ) => arena . Resolve ( invalidHandle ) ) ;
267+ Assert . ThrowsException < InvalidOperationException > ( ( ) => arena . Free ( invalidHandle ) ) ;
268+ Assert . ThrowsException < InvalidOperationException > ( ( ) => arena . GetEntry ( invalidHandle ) ) ;
269+ }
270+
271+ [ TestMethod ]
272+ public void Security_TamperedVersionHandle_ThrowsAccessViolationException ( )
273+ {
274+ var arena = new MemoryArena ( _config ) ;
275+ var trueHandle = arena . Store ( 999 ) ;
276+
277+ // Construct a fake handle targeting the exact same ID slot tracking address,
278+ // but inject a spoofed generational token sequence signature.
279+ var forgedHandle = new MemoryHandle ( trueHandle . Id , ( uint ) ( trueHandle . Version + 5 ) , arena . FastLane ) ;
280+
281+ Assert . ThrowsException < AccessViolationException > ( ( ) =>
282+ {
283+ arena . Get < int > ( forgedHandle ) ;
284+ } , "Submitting expired, stale, or tampered handles must trigger access validation crashes." ) ;
285+ }
286+
287+ #endregion
288+
289+ #region --- HELPER STRUCTURES ---
290+
291+ private struct SampleMetric
292+ {
293+ public long IdField ;
294+ public float Velocity ;
295+ }
296+
297+ #endregion
298+ }
299+ }
0 commit comments