189 lines
6.1 KiB
C#
189 lines
6.1 KiB
C#
|
using System;
|
||
|
using NUnit.Framework;
|
||
|
using Unity.Collections;
|
||
|
using Unity.Collections.LowLevel.Unsafe;
|
||
|
using Unity.Collections.Tests;
|
||
|
|
||
|
#if !UNITY_DOTSRUNTIME && ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
internal class RewindableAllocatorTests
|
||
|
{
|
||
|
AllocatorHelper<RewindableAllocator> m_AllocatorHelper;
|
||
|
protected ref RewindableAllocator RwdAllocator => ref m_AllocatorHelper.Allocator;
|
||
|
|
||
|
[SetUp]
|
||
|
public void Setup()
|
||
|
{
|
||
|
m_AllocatorHelper = new AllocatorHelper<RewindableAllocator>(Allocator.Persistent);
|
||
|
m_AllocatorHelper.Allocator.Initialize(128 * 1024, true);
|
||
|
}
|
||
|
|
||
|
[TearDown]
|
||
|
public void TearDown()
|
||
|
{
|
||
|
m_AllocatorHelper.Allocator.Dispose();
|
||
|
m_AllocatorHelper.Dispose();
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public unsafe void RewindTestVersionOverflow()
|
||
|
{
|
||
|
// Check allocator version overflow
|
||
|
for (int i = 0; i < 65536 + 100; i++)
|
||
|
{
|
||
|
var container = RwdAllocator.AllocateNativeList<byte>(RwdAllocator.InitialSizeInBytes / 1000);
|
||
|
container.Resize(1, NativeArrayOptions.ClearMemory);
|
||
|
container[0] = 0xFE;
|
||
|
RwdAllocator.Rewind();
|
||
|
CollectionHelper.CheckAllocator(RwdAllocator.ToAllocator);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if UNITY_2022_3_OR_NEWER
|
||
|
[Test]
|
||
|
public unsafe void NativeArrayCustomAllocatorExceptionWorks()
|
||
|
{
|
||
|
NativeArray<int> array = default;
|
||
|
Assert.Throws<ArgumentException>(() =>
|
||
|
{
|
||
|
array = new NativeArray<int>(2, RwdAllocator.ToAllocator);
|
||
|
});
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
public unsafe void RewindInvalidatesNativeList()
|
||
|
{
|
||
|
var container = RwdAllocator.AllocateNativeList<byte>(RwdAllocator.InitialSizeInBytes / 1000);
|
||
|
container.Resize(1, NativeArrayOptions.ClearMemory);
|
||
|
container[0] = 0xFE;
|
||
|
RwdAllocator.Rewind();
|
||
|
Assert.Throws<ObjectDisposedException>(() =>
|
||
|
{
|
||
|
container[0] = 0xEF;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public unsafe void RewindInvalidatesNativeArray()
|
||
|
{
|
||
|
var container = RwdAllocator.AllocateNativeArray<byte>(RwdAllocator.InitialSizeInBytes / 1000);
|
||
|
container[0] = 0xFE;
|
||
|
RwdAllocator.Rewind();
|
||
|
Assert.Throws<ObjectDisposedException>(() =>
|
||
|
{
|
||
|
container[0] = 0xEF;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public unsafe void NativeListCanBeCreatedViaMemberFunction()
|
||
|
{
|
||
|
var container = RwdAllocator.AllocateNativeList<byte>(RwdAllocator.InitialSizeInBytes / 1000);
|
||
|
container.Resize(1, NativeArrayOptions.ClearMemory);
|
||
|
container[0] = 0xFE;
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public unsafe void NativeListCanBeDisposed()
|
||
|
{
|
||
|
var container = RwdAllocator.AllocateNativeList<byte>(RwdAllocator.InitialSizeInBytes / 1000);
|
||
|
container.Resize(1, NativeArrayOptions.ClearMemory);
|
||
|
container[0] = 0xFE;
|
||
|
container.Dispose();
|
||
|
RwdAllocator.Rewind();
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void NativeArrayCanBeDisposed()
|
||
|
{
|
||
|
var container = RwdAllocator.AllocateNativeArray<byte>(RwdAllocator.InitialSizeInBytes / 1000);
|
||
|
container[0] = 0xFE;
|
||
|
container.Dispose();
|
||
|
RwdAllocator.Rewind();
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void NumberOfBlocksIsTemporarilyStable()
|
||
|
{
|
||
|
RwdAllocator.AllocateNativeList<byte>(RwdAllocator.InitialSizeInBytes * 10);
|
||
|
var blocksBefore = RwdAllocator.BlocksAllocated;
|
||
|
RwdAllocator.Rewind();
|
||
|
var blocksAfter = RwdAllocator.BlocksAllocated;
|
||
|
Assert.AreEqual(blocksAfter, blocksBefore);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void NumberOfBlocksEventuallyDrops()
|
||
|
{
|
||
|
RwdAllocator.AllocateNativeList<byte>(RwdAllocator.InitialSizeInBytes * 10);
|
||
|
var blocksBefore = RwdAllocator.BlocksAllocated;
|
||
|
RwdAllocator.Rewind();
|
||
|
RwdAllocator.Rewind();
|
||
|
var blocksAfter = RwdAllocator.BlocksAllocated;
|
||
|
Assert.IsTrue(blocksAfter < blocksBefore);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void PossibleToAllocateGigabytes()
|
||
|
{
|
||
|
const int giga = 1024 * 1024 * 1024;
|
||
|
var container0 = RwdAllocator.AllocateNativeList<byte>(giga);
|
||
|
var container1 = RwdAllocator.AllocateNativeList<byte>(giga);
|
||
|
var container2 = RwdAllocator.AllocateNativeList<byte>(giga);
|
||
|
container0.Resize(1, NativeArrayOptions.ClearMemory);
|
||
|
container1.Resize(1, NativeArrayOptions.ClearMemory);
|
||
|
container2.Resize(1, NativeArrayOptions.ClearMemory);
|
||
|
container0[0] = 0;
|
||
|
container1[0] = 1;
|
||
|
container2[0] = 2;
|
||
|
Assert.AreEqual((byte)0, container0[0]);
|
||
|
Assert.AreEqual((byte)1, container1[0]);
|
||
|
Assert.AreEqual((byte)2, container2[0]);
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void ExhaustsFirstBlockBeforeAllocatingMore()
|
||
|
{
|
||
|
for (var i = 0; i < 50; ++i)
|
||
|
{
|
||
|
RwdAllocator.AllocateNativeList<byte>(RwdAllocator.InitialSizeInBytes / 100);
|
||
|
Assert.AreEqual(1, RwdAllocator.BlocksAllocated);
|
||
|
}
|
||
|
RwdAllocator.AllocateNativeList<byte>(RwdAllocator.InitialSizeInBytes);
|
||
|
Assert.AreEqual(2, RwdAllocator.BlocksAllocated);
|
||
|
}
|
||
|
|
||
|
unsafe struct ListProvider
|
||
|
{
|
||
|
NativeList<byte> m_Bytes;
|
||
|
|
||
|
public ListProvider(AllocatorManager.AllocatorHandle allocatorHandle) => m_Bytes = new NativeList<byte>(allocatorHandle);
|
||
|
|
||
|
public void Append<T>(ref T data) where T : unmanaged =>
|
||
|
m_Bytes.AddRange(UnsafeUtility.AddressOf(ref data), UnsafeUtility.SizeOf<T>());
|
||
|
}
|
||
|
|
||
|
static void TriggerBug(AllocatorManager.AllocatorHandle allocatorHandle, NativeArray<byte> data)
|
||
|
{
|
||
|
var listProvider = new ListProvider(allocatorHandle);
|
||
|
|
||
|
var datum = 0u;
|
||
|
listProvider.Append(ref datum); // 'data' is now invalid after call to AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
|
||
|
|
||
|
Assert.That(data[0], Is.EqualTo(0));
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
public void AddRange_WhenCalledOnStructMember_DoesNotInvalidateUnrelatedListHigherOnCallStack()
|
||
|
{
|
||
|
AllocatorManager.AllocatorHandle allocatorHandle = RwdAllocator.Handle;
|
||
|
|
||
|
var unrelatedList = new NativeList<byte>(allocatorHandle) { 0, 0 };
|
||
|
Assert.That(unrelatedList.Length, Is.EqualTo(2));
|
||
|
Assert.That(unrelatedList[0], Is.EqualTo(0));
|
||
|
|
||
|
TriggerBug(allocatorHandle, unrelatedList);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|