using System.Numerics;
using FluentAssertions;
using Neo.Sol.Runtime.Memory;
using NUnit.Framework;
namespace Neo.Sol.Runtime.Tests.Memory;
[TestFixture]
public class EvmMemoryManagerTests
{
private EvmMemoryManager _memoryManager = null!;
[SetUp]
public void SetUp()
{
_memoryManager = new EvmMemoryManager();
}
[TearDown]
public void TearDown()
{
_memoryManager.Dispose();
}
[Test]
public void Constructor_ShouldInitializeEmptyMemory()
{
var memory = new EvmMemoryManager();
memory.Size.Should().Be(0);
}
[Test]
public void Store_ShouldStoreWordAtAddress()
{
var address = 0u;
var value = new byte[32];
value[31] = 0x42;
_memoryManager.Store(address, value);
_memoryManager.Size.Should().Be(32);
var loaded = _memoryManager.Load(address);
loaded.Should().BeEquivalentTo(value);
}
[Test]
public void Store_BigInteger_ShouldStoreBigEndian()
{
var address = 0u;
var value = new BigInteger(0x123456789ABCDEF0);
_memoryManager.Store(address, value);
var loaded = _memoryManager.LoadBigInteger(address);
loaded.Should().Be(value);
}
[Test]
public void Load_UnallocatedMemory_ShouldReturnZero()
{
var address = 64u;
var result = _memoryManager.Load(address);
result.Should().HaveCount(32);
result.Should().OnlyContain(b => b == 0);
}
[Test]
public void StoreBytes_ShouldStoreArbitraryData()
{
var address = 0u;
var data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
_memoryManager.StoreBytes(address, data);
var loaded = _memoryManager.LoadBytes(address, (uint)data.Length);
loaded.Should().BeEquivalentTo(data);
}
[Test]
public void ExpandMemory_ShouldGrowMemorySize()
{
var newSize = 1024u;
_memoryManager.ExpandMemory(newSize);
_memoryManager.Size.Should().Be(newSize);
}
[Test]
public void CalculateExpansionCost_ShouldUseQuadraticFormula()
{
_memoryManager.ExpandMemory(32); var newSize = 64u;
var cost = _memoryManager.CalculateExpansionCost(newSize);
cost.Should().Be(3);
}
[Test]
public void Store_WordSizeValidation_ShouldThrowForWrongSize()
{
var address = 0u;
var invalidValue = new byte[16];
Assert.Throws<ArgumentException>(() => _memoryManager.Store(address, invalidValue));
}
[Test]
public void ExpandMemory_ExceedsLimit_ShouldThrow()
{
var excessiveSize = 64 * 1024 * 1024u + 1;
Assert.Throws<InvalidOperationException>(() => _memoryManager.ExpandMemory(excessiveSize));
}
[Test]
public void GetStats_ShouldReturnAccurateStatistics()
{
_memoryManager.Store(0, new byte[32]);
_memoryManager.Store(32, new byte[32]);
var stats = _memoryManager.GetStats();
stats.TotalSize.Should().Be(64);
stats.AllocatedPages.Should().BeGreaterThan(0);
stats.UtilizationRatio.Should().BeGreaterThan(0);
}
[Test]
public void Clear_ShouldResetMemory()
{
_memoryManager.Store(0, new byte[32]);
_memoryManager.Size.Should().Be(32);
_memoryManager.Clear();
_memoryManager.Size.Should().Be(0);
var stats = _memoryManager.GetStats();
stats.AllocatedPages.Should().Be(0);
}
[Test]
public void ConcurrentAccess_ShouldHandleMultipleOperations()
{
var tasks = new List<Task>();
var addresses = Enumerable.Range(0, 10).Select(i => (uint)(i * 32)).ToArray();
foreach (var addr in addresses)
{
tasks.Add(Task.Run(() =>
{
var value = new byte[32];
value[31] = (byte)(addr / 32); _memoryManager.Store(addr, value);
}));
}
Task.WaitAll(tasks.ToArray());
foreach (var addr in addresses)
{
var loaded = _memoryManager.Load(addr);
loaded[31].Should().Be((byte)(addr / 32));
}
}
}