225 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			225 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using NUnit.Framework; | ||
|  | using Unity.Collections; | ||
|  | using Unity.Collections.LowLevel.Unsafe; | ||
|  | using Unity.Collections.Tests; | ||
|  | using Unity.Jobs.LowLevel.Unsafe; | ||
|  | 
 | ||
|  | #if 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 | ||
|  | 
 | ||
|  |     [TestRequiresCollectionChecks] | ||
|  |     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] | ||
|  |     [TestRequiresCollectionChecks] | ||
|  |     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.AsArray()); | ||
|  |     } | ||
|  | 
 | ||
|  |     [Test] | ||
|  |     unsafe public void ExceedMaxBlockSize_BlockSizeLinearGrow() | ||
|  |     { | ||
|  |         AllocatorManager.AllocatorHandle allocatorHandle = RwdAllocator.Handle; | ||
|  | 
 | ||
|  |         var allocationSizes = new NativeList<int>(Allocator.Persistent); | ||
|  |         allocationSizes.Add(1); | ||
|  |         allocationSizes.Add((int)RwdAllocator.MaxMemoryBlockSize + 256); | ||
|  |         allocationSizes.Add(1); | ||
|  |         allocationSizes.Add(RwdAllocator.InitialSizeInBytes); | ||
|  | 
 | ||
|  |         int mask = JobsUtility.CacheLineSize - 1; | ||
|  | 
 | ||
|  |         var expectedBlockSizes = new NativeList<int>(Allocator.Persistent); | ||
|  |         expectedBlockSizes.Add(RwdAllocator.InitialSizeInBytes); | ||
|  |         expectedBlockSizes.Add(RwdAllocator.InitialSizeInBytes + (((int)RwdAllocator.MaxMemoryBlockSize + 256 + mask) & ~mask)); | ||
|  |         expectedBlockSizes.Add(RwdAllocator.InitialSizeInBytes + (((int)RwdAllocator.MaxMemoryBlockSize + 256 + mask) & ~mask)); | ||
|  |         var expected = RwdAllocator.InitialSizeInBytes + (((int)RwdAllocator.MaxMemoryBlockSize + 256 + mask) & ~mask) + | ||
|  |                             (((int)RwdAllocator.MaxMemoryBlockSize + 256 + mask) & ~mask) +(int)RwdAllocator.MaxMemoryBlockSize; | ||
|  |         expectedBlockSizes.Add(expected); | ||
|  | 
 | ||
|  |         for(int i = 0; i < allocationSizes.Length; i++) | ||
|  |         { | ||
|  |             AllocatorManager.Allocate(allocatorHandle, sizeof(byte), sizeof(byte), allocationSizes[i]); | ||
|  |             int bytesUsed = (int)RwdAllocator.BytesAllocated; | ||
|  |             Assert.AreEqual(bytesUsed, expectedBlockSizes[i]); | ||
|  |         } | ||
|  | 
 | ||
|  |         allocationSizes.Dispose(); | ||
|  |         expectedBlockSizes.Dispose(); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | #endif |