Skip to content

Commit 7608d98

Browse files
author
LoneWandererProductions
committed
add more tests
1 parent 9eba1a0 commit 7608d98

3 files changed

Lines changed: 149 additions & 1 deletion

File tree

MemoryManager.Core/MemoryCanary.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* PROGRAMMER: Peter Geinitz (Wayfarer)
77
*/
88

9-
using System;
109
using System.Runtime.CompilerServices;
1110

1211
namespace MemoryManager.Core

MemoryManager.Tests/FastLaneTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ public void FastLaneCompactRemovesGapsAfterFreedEntries()
149149
// Check that our FreeList is correctly reset to 1 giant block
150150
Assert.AreEqual(0, _fastLane.EstimateFragmentation(),
151151
"Fragmentation should be exactly 0 after compaction.");
152+
153+
_fastLane.DebugVisualMap();
152154
}
153155
}
154156
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* COPYRIGHT: See COPYING in the top level directory
3+
* PROJECT: MemoryManager.Tests
4+
* FILE: MemoryArenaChaosTests.cs
5+
* PURPOSE: Hardening and edge-case validation for the unmanaged memory arena.
6+
* PROGRAMMER: Peter Geinitz (Wayfarer)
7+
*/
8+
9+
using MemoryManager.Core;
10+
using MemoryManager.Lanes;
11+
12+
namespace MemoryManager.Tests
13+
{
14+
[TestClass]
15+
public class MemoryArenaChaosTests
16+
{
17+
/// <summary>
18+
/// Ensures that attempting to free an already freed handle throws an exception
19+
/// instead of corrupting the free-list or leaking structural slot array trackers.
20+
/// </summary>
21+
[TestMethod]
22+
[TestCategory("ChaosSafety")]
23+
public void Arena_DoubleFree_ThrowsInvalidOperationException()
24+
{
25+
// Arrange
26+
const int capacity = 1024 * 1024;
27+
using var slowLane = new SlowLane(capacity);
28+
using var fastLane = new FastLane(capacity, slowLane);
29+
30+
var handle = fastLane.Allocate(64);
31+
32+
// First free is the valid happy path
33+
fastLane.Free(handle);
34+
35+
// Act & Assert
36+
Assert.ThrowsException<InvalidOperationException>(() =>
37+
{
38+
// Second free is an exploitation vector or developer bug
39+
fastLane.Free(handle);
40+
}, "The lane must block a double-free attempt to protect the structural integrity of the free-list.");
41+
42+
fastLane.DebugVisualMap();
43+
}
44+
45+
/// <summary>
46+
/// Verifies that when an ID is recycled, its generational version increments.
47+
/// An old handle pointing to the recycled ID must trigger a safe access violation,
48+
/// while the new handle resolves perfectly to the newly assigned block.
49+
/// </summary>
50+
[TestMethod]
51+
[TestCategory("ChaosSafety")]
52+
public unsafe void Arena_HandleRecycling_IncrementsVersion_BlocksStaleHandles()
53+
{
54+
// Arrange
55+
const int capacity = 1024 * 1024;
56+
using var slowLane = new SlowLane(capacity);
57+
using var fastLane = new FastLane(capacity, slowLane);
58+
59+
// 1. Allocate block A, populate it, and record the original handle signature
60+
var oldHandle = fastLane.Allocate(32);
61+
int* oldPtr = (int*)fastLane.Resolve(oldHandle);
62+
*oldPtr = 42;
63+
64+
// 2. Free block A to return its sequential integer ID back to the pool
65+
fastLane.Free(oldHandle);
66+
67+
// 3. Force a new allocation. This must recycle the old ID, but increment the generation version
68+
var newHandle = fastLane.Allocate(32);
69+
70+
// Assert that the identifier was recycled but the version is explicitly distinct
71+
Assert.AreEqual(oldHandle.Id, newHandle.Id, "The allocator should recycle tracking IDs to prevent footprint bloat.");
72+
Assert.AreNotEqual(oldHandle.Version, newHandle.Version, "Recycled IDs must feature an incremented generation value.");
73+
74+
int* newPtr = (int*)fastLane.Resolve(newHandle);
75+
*newPtr = 99; // Write new distinct data to the recycled slot
76+
77+
// Act & Assert
78+
79+
// Verifying the new handle operates perfectly
80+
int* verifyPtr = (int*)fastLane.Resolve(newHandle);
81+
Assert.AreEqual(99, *verifyPtr);
82+
83+
// Verifying the zombie check blocks the legacy handle signature
84+
Assert.ThrowsException<AccessViolationException>(() =>
85+
{
86+
// Attempting to resolve via the stale handle version must break deterministically
87+
fastLane.Resolve(oldHandle);
88+
}, "Resolving a stale generational handle version must trigger a structural AccessViolationException.");
89+
90+
fastLane.DebugVisualMap();
91+
}
92+
93+
/// <summary>
94+
/// Pushes the allocator exactly to its saturation limit down to the last byte.
95+
/// Verifies that compaction behaves perfectly at saturation thresholds and that
96+
/// allocating even one single byte past capacity constraints emits a clean OutOfMemoryException.
97+
/// </summary>
98+
[TestMethod]
99+
[TestCategory("ChaosSafety")]
100+
public void Arena_AbsoluteSaturation_HandlesCompactionAndThrowsOOM()
101+
{
102+
// Arrange
103+
const int elementSize = 64;
104+
int physicalBlockSize = MemoryCanary.GetPhysicalSize(elementSize);
105+
106+
// Establish a strict budget that fits exactly 5 aligned physical blocks
107+
int customCapacity = physicalBlockSize * 5;
108+
109+
using var slowLane = new SlowLane(customCapacity);
110+
using var fastLane = new FastLane(customCapacity, slowLane);
111+
112+
var handles = new List<MemoryHandle>();
113+
114+
// 1. Fully saturate the lane down to the literal final byte line
115+
for (int i = 0; i < 5; i++)
116+
{
117+
handles.Add(fastLane.Allocate(elementSize));
118+
}
119+
120+
// Assert that the available free space tracking has hit zero
121+
Assert.AreEqual(0, fastLane.FreeSpace(), "Lane should have exactly 0 bytes remaining at absolute structural saturation.");
122+
123+
// 2. CRITICAL BOUNDARY CHECK: Try to force an extra allocation while completely full
124+
Assert.ThrowsException<OutOfMemoryException>(() =>
125+
{
126+
// Because the arena is packed at 100% saturation, this must cleanly fail
127+
fastLane.Allocate(1);
128+
}, "Allocating past strict capacity parameters when full must cleanly emit an OutOfMemoryException.");
129+
130+
// 3. Free alternating items to introduce calculated structural holes
131+
fastLane.Free(handles[1]);
132+
fastLane.Free(handles[3]);
133+
134+
// 4. Execute a complete compaction pass at high stress capacity limits
135+
fastLane.Compact();
136+
137+
// 5. Verify surviving blocks are still completely safe post-compaction slide
138+
Assert.IsTrue(fastLane.HasHandle(handles[0]), "Survivor ID 0 must maintain valid entry properties post-compaction.");
139+
Assert.IsTrue(fastLane.HasHandle(handles[2]), "Survivor ID 2 must maintain valid entry properties post-compaction.");
140+
Assert.IsTrue(fastLane.HasHandle(handles[4]), "Survivor ID 4 must maintain valid entry properties post-compaction.");
141+
142+
// 6. VERIFY RECLAIMED WORKSPACE: Request a new item now that compaction freed up consolidated space
143+
var hNew = fastLane.Allocate(elementSize);
144+
Assert.IsTrue(fastLane.HasHandle(hNew), "The allocator should be able to accept new allocations again after compaction consolidates holes.");
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)