b486678290
Library -Artifacts
514 lines
15 KiB
C#
514 lines
15 KiB
C#
using NUnit.Framework;
|
|
using System;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Collections.NotBurstCompatible;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Collections.Tests;
|
|
using Unity.Jobs;
|
|
using UnityEngine;
|
|
using UnityEngine.TestTools;
|
|
#if !UNITY_PORTABLE_TEST_RUNNER
|
|
using System.Text.RegularExpressions;
|
|
#endif
|
|
|
|
internal class NativeListTests : CollectionsTestFixture
|
|
{
|
|
static void ExpectedLength<T>(ref NativeList<T> container, int expected)
|
|
where T : unmanaged
|
|
{
|
|
Assert.AreEqual(expected == 0, container.IsEmpty);
|
|
Assert.AreEqual(expected, container.Length);
|
|
}
|
|
|
|
[Test]
|
|
public void NullListThrow()
|
|
{
|
|
var list = new NativeList<int>();
|
|
Assert.Throws<NullReferenceException>(() => list[0] = 5);
|
|
Assert.Throws<ObjectDisposedException>(
|
|
() => list.Add(1));
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_Allocate_Deallocate_Read_Write()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
|
|
list.Add(1);
|
|
list.Add(2);
|
|
|
|
ExpectedLength(ref list, 2);
|
|
Assert.AreEqual(1, list[0]);
|
|
Assert.AreEqual(2, list[1]);
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeArrayFromNativeList()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(42);
|
|
list.Add(2);
|
|
|
|
NativeArray<int> array = list;
|
|
|
|
Assert.AreEqual(2, array.Length);
|
|
Assert.AreEqual(42, array[0]);
|
|
Assert.AreEqual(2, array[1]);
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeArrayFromNativeListInvalidatesOnAdd()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
|
|
// This test checks that adding an element without reallocation invalidates the native array
|
|
// (length changes)
|
|
list.Capacity = 2;
|
|
list.Add(42);
|
|
|
|
NativeArray<int> array = list;
|
|
|
|
list.Add(1000);
|
|
|
|
ExpectedLength(ref list, 2);
|
|
Assert.Throws<ObjectDisposedException>(
|
|
() => { array[0] = 1; });
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeArrayFromNativeListInvalidatesOnCapacityChange()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(42);
|
|
|
|
NativeArray<int> array = list;
|
|
|
|
ExpectedLength(ref list, 1);
|
|
list.Capacity = 10;
|
|
|
|
//Assert.AreEqual(1, array.Length); - temporarily commenting out updated assert checks to ensure editor version promotion succeeds
|
|
Assert.Throws<ObjectDisposedException>(
|
|
() => { array[0] = 1; });
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeArrayFromNativeListInvalidatesOnDispose()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(42);
|
|
NativeArray<int> array = list;
|
|
list.Dispose();
|
|
|
|
Assert.Throws<ObjectDisposedException>(
|
|
() => { array[0] = 1; });
|
|
|
|
Assert.Throws<ObjectDisposedException>(
|
|
() => { list[0] = 1; });
|
|
}
|
|
|
|
[Test]
|
|
public void NativeArrayFromNativeListMayDeallocate()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(42);
|
|
|
|
NativeArray<int> array = list;
|
|
Assert.DoesNotThrow(() => { array.Dispose(); });
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void CopiedNativeListIsKeptInSync()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
var listCpy = list;
|
|
list.Add(42);
|
|
|
|
Assert.AreEqual(42, listCpy[0]);
|
|
Assert.AreEqual(42, list[0]);
|
|
Assert.AreEqual(1, listCpy.Length);
|
|
ExpectedLength(ref list, 1);
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_CopyFrom_Managed()
|
|
{
|
|
var list = new NativeList<float>(4, Allocator.Persistent);
|
|
var ar = new float[] { 0, 1, 2, 3, 4, 5, 6, 7 };
|
|
list.CopyFromNBC(ar);
|
|
ExpectedLength(ref list, 8);
|
|
for (int i = 0; i < list.Length; ++i)
|
|
{
|
|
Assert.AreEqual(i, list[i]);
|
|
}
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_CopyFrom_Unmanaged()
|
|
{
|
|
var list = new NativeList<float>(4, Allocator.Persistent);
|
|
var ar = new NativeArray<float>(new float[] { 0, 1, 2, 3, 4, 5, 6, 7 }, Allocator.Persistent);
|
|
|
|
list.CopyFrom(ar);
|
|
ExpectedLength(ref list, 8);
|
|
for (int i = 0; i < list.Length; ++i)
|
|
{
|
|
Assert.AreEqual(i, list[i]);
|
|
}
|
|
|
|
ar.Dispose();
|
|
list.Dispose();
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
struct TempListInJob : IJob
|
|
{
|
|
public NativeArray<int> Output;
|
|
public void Execute()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Temp);
|
|
|
|
list.Add(17);
|
|
|
|
Output[0] = list[0];
|
|
|
|
list.Dispose();
|
|
}
|
|
}
|
|
|
|
|
|
[Test]
|
|
[Ignore("Unstable on CI, DOTS-1965")]
|
|
public void TempListInBurstJob()
|
|
{
|
|
var job = new TempListInJob() { Output = CollectionHelper.CreateNativeArray<int>(1, CommonRwdAllocator.Handle) };
|
|
job.Schedule().Complete();
|
|
Assert.AreEqual(17, job.Output[0]);
|
|
|
|
job.Output.Dispose();
|
|
}
|
|
|
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
[Test]
|
|
public void SetCapacityLessThanLength()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Resize(10, NativeArrayOptions.UninitializedMemory);
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => { list.Capacity = 5; });
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void DisposingNativeListDerivedArrayDoesNotThrow()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(1);
|
|
|
|
NativeArray<int> array = list;
|
|
Assert.DoesNotThrow(() => { array.Dispose(); });
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
#endif
|
|
|
|
[Test]
|
|
public void NativeList_DisposeJob()
|
|
{
|
|
var container = new NativeList<int>(Allocator.Persistent);
|
|
Assert.True(container.IsCreated);
|
|
Assert.DoesNotThrow(() => { container.Add(0); });
|
|
Assert.DoesNotThrow(() => { container.Contains(0); });
|
|
|
|
var disposeJob = container.Dispose(default);
|
|
Assert.False(container.IsCreated);
|
|
Assert.Throws<ObjectDisposedException>(
|
|
() => { container.Contains(0); });
|
|
|
|
disposeJob.Complete();
|
|
}
|
|
|
|
[Test]
|
|
public void ForEachWorks()
|
|
{
|
|
var container = new NativeList<int>(Allocator.Persistent);
|
|
container.Add(10);
|
|
container.Add(20);
|
|
|
|
int sum = 0;
|
|
int count = 0;
|
|
GCAllocRecorder.ValidateNoGCAllocs(() =>
|
|
{
|
|
sum = 0;
|
|
count = 0;
|
|
foreach (var p in container)
|
|
{
|
|
sum += p;
|
|
count++;
|
|
}
|
|
});
|
|
|
|
Assert.AreEqual(30, sum);
|
|
Assert.AreEqual(2, count);
|
|
|
|
container.Dispose();
|
|
}
|
|
|
|
struct NativeQueueAddJob : IJob
|
|
{
|
|
NativeQueue<int> queue;
|
|
|
|
public NativeQueueAddJob(NativeQueue<int> queue) { this.queue = queue; }
|
|
|
|
public void Execute()
|
|
{
|
|
queue.Enqueue(1);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void NativeQueue_DisposeJobWithMissingDependencyThrows()
|
|
{
|
|
var queue = new NativeQueue<int>(Allocator.Persistent);
|
|
var deps = new NativeQueueAddJob(queue).Schedule();
|
|
Assert.Throws<InvalidOperationException>(() => { queue.Dispose(default); });
|
|
deps.Complete();
|
|
queue.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeQueue_DisposeJobCantBeScheduled()
|
|
{
|
|
var queue = new NativeQueue<int>(Allocator.Persistent);
|
|
var deps = queue.Dispose(default);
|
|
Assert.Throws<InvalidOperationException>(() => { new NativeQueueAddJob(queue).Schedule(deps); });
|
|
deps.Complete();
|
|
}
|
|
|
|
// These tests require:
|
|
// - JobsDebugger support for static safety IDs (added in 2020.1)
|
|
// - Asserting throws
|
|
#if !UNITY_DOTSRUNTIME
|
|
[Test,DotsRuntimeIgnore]
|
|
public void NativeList_UseAfterFree_UsesCustomOwnerTypeName()
|
|
{
|
|
var list = new NativeList<int>(10, CommonRwdAllocator.Handle);
|
|
list.Add(17);
|
|
list.Dispose();
|
|
Assert.That(() => list[0],
|
|
Throws.Exception.TypeOf<ObjectDisposedException>()
|
|
.With.Message.Contains($"The {list.GetType()} has been deallocated"));
|
|
}
|
|
|
|
[Test,DotsRuntimeIgnore]
|
|
public void AtomicSafetyHandle_AllocatorTemp_UniqueStaticSafetyIds()
|
|
{
|
|
// All collections that use Allocator.Temp share the same core AtomicSafetyHandle.
|
|
// This test verifies that containers can proceed to assign unique static safety IDs to each
|
|
// AtomicSafetyHandle value, which will not be shared by other containers using Allocator.Temp.
|
|
var listInt = new NativeList<int>(10, Allocator.Temp);
|
|
var listFloat = new NativeList<float>(10, Allocator.Temp);
|
|
listInt.Add(17);
|
|
listInt.Dispose();
|
|
Assert.That(() => listInt[0],
|
|
Throws.Exception.TypeOf<ObjectDisposedException>()
|
|
.With.Message.Contains($"The {listInt.GetType()} has been deallocated"));
|
|
listFloat.Add(1.0f);
|
|
listFloat.Dispose();
|
|
Assert.That(() => listFloat[0],
|
|
Throws.Exception.TypeOf<ObjectDisposedException>()
|
|
.With.Message.Contains($"The {listFloat.GetType()} has been deallocated"));
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
struct NativeListCreateAndUseAfterFreeBurst : IJob
|
|
{
|
|
public void Execute()
|
|
{
|
|
var list = new NativeList<int>(10, Allocator.Temp);
|
|
list.Add(17);
|
|
list.Dispose();
|
|
list.Add(42);
|
|
}
|
|
}
|
|
|
|
[Test,DotsRuntimeIgnore]
|
|
public void NativeList_CreateAndUseAfterFreeInBurstJob_UsesCustomOwnerTypeName()
|
|
{
|
|
// Make sure this isn't the first container of this type ever created, so that valid static safety data exists
|
|
var list = new NativeList<int>(10, CommonRwdAllocator.Handle);
|
|
list.Dispose();
|
|
|
|
var job = new NativeListCreateAndUseAfterFreeBurst
|
|
{
|
|
};
|
|
|
|
// Two things:
|
|
// 1. This exception is logged, not thrown; thus, we use LogAssert to detect it.
|
|
// 2. Calling write operation after container.Dispose() emits an unintuitive error message. For now, all this test cares about is whether it contains the
|
|
// expected type name.
|
|
job.Run();
|
|
LogAssert.Expect(LogType.Exception,
|
|
new Regex($"InvalidOperationException: The {Regex.Escape(list.GetType().ToString())} has been declared as \\[ReadOnly\\] in the job, but you are writing to it"));
|
|
}
|
|
#endif
|
|
|
|
[Test]
|
|
public unsafe void NativeList_IndexOf()
|
|
{
|
|
using (var list = new NativeList<int>(10, Allocator.Persistent) { 123, 789 })
|
|
{
|
|
bool r0 = false, r1 = false, r2 = false;
|
|
|
|
GCAllocRecorder.ValidateNoGCAllocs(() =>
|
|
{
|
|
r0 = -1 != list.IndexOf(456);
|
|
r1 = list.Contains(123);
|
|
r2 = list.Contains(789);
|
|
});
|
|
|
|
Assert.False(r0);
|
|
Assert.True(r1);
|
|
Assert.True(r2);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_InsertRangeWithBeginEnd()
|
|
{
|
|
var list = new NativeList<byte>(3, Allocator.Persistent);
|
|
list.Add(0);
|
|
list.Add(3);
|
|
list.Add(4);
|
|
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => list.InsertRangeWithBeginEnd(-1, 8));
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => list.InsertRangeWithBeginEnd(0, 8));
|
|
Assert.Throws<ArgumentException>(() => list.InsertRangeWithBeginEnd(3, 1));
|
|
|
|
Assert.DoesNotThrow(() => list.InsertRangeWithBeginEnd(1, 3));
|
|
|
|
list[1] = 1;
|
|
list[2] = 2;
|
|
|
|
for (var i = 0; i < 5; ++i)
|
|
{
|
|
Assert.AreEqual(i, list[i]);
|
|
}
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_CustomAllocatorTest()
|
|
{
|
|
AllocatorManager.Initialize();
|
|
var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
|
|
ref var allocator = ref allocatorHelper.Allocator;
|
|
allocator.Initialize();
|
|
|
|
using (var container = new NativeList<byte>(1, allocator.Handle))
|
|
{
|
|
}
|
|
|
|
Assert.IsTrue(allocator.WasUsed);
|
|
allocator.Dispose();
|
|
allocatorHelper.Dispose();
|
|
AllocatorManager.Shutdown();
|
|
}
|
|
|
|
[BurstCompile]
|
|
struct BurstedCustomAllocatorJob : IJob
|
|
{
|
|
[NativeDisableUnsafePtrRestriction]
|
|
public unsafe CustomAllocatorTests.CountingAllocator* Allocator;
|
|
|
|
public void Execute()
|
|
{
|
|
unsafe
|
|
{
|
|
using (var container = new NativeList<byte>(1, Allocator->Handle))
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public unsafe void NativeList_BurstedCustomAllocatorTest()
|
|
{
|
|
AllocatorManager.Initialize();
|
|
var allocatorHelper = new AllocatorHelper<CustomAllocatorTests.CountingAllocator>(AllocatorManager.Persistent);
|
|
ref var allocator = ref allocatorHelper.Allocator;
|
|
allocator.Initialize();
|
|
|
|
var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf<CustomAllocatorTests.CountingAllocator>(ref allocator);
|
|
unsafe
|
|
{
|
|
var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr}.Schedule();
|
|
handle.Complete();
|
|
}
|
|
|
|
Assert.IsTrue(allocator.WasUsed);
|
|
allocator.Dispose();
|
|
allocatorHelper.Dispose();
|
|
AllocatorManager.Shutdown();
|
|
}
|
|
|
|
[Test]
|
|
public unsafe void NativeList_SetCapacity()
|
|
{
|
|
using (var list = new NativeList<int>(1, Allocator.Persistent))
|
|
{
|
|
list.Add(1);
|
|
Assert.DoesNotThrow(() => list.SetCapacity(128));
|
|
|
|
list.Add(1);
|
|
Assert.AreEqual(2, list.Length);
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => list.SetCapacity(1));
|
|
|
|
list.RemoveAtSwapBack(0);
|
|
Assert.AreEqual(1, list.Length);
|
|
Assert.DoesNotThrow(() => list.SetCapacity(1));
|
|
|
|
list.TrimExcess();
|
|
Assert.AreEqual(1, list.Capacity);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public unsafe void NativeList_TrimExcess()
|
|
{
|
|
using (var list = new NativeList<int>(32, Allocator.Persistent))
|
|
{
|
|
list.Add(1);
|
|
list.TrimExcess();
|
|
Assert.AreEqual(1, list.Length);
|
|
Assert.AreEqual(1, list.Capacity);
|
|
|
|
list.RemoveAtSwapBack(0);
|
|
Assert.AreEqual(list.Length, 0);
|
|
list.TrimExcess();
|
|
Assert.AreEqual(list.Capacity, 0);
|
|
|
|
list.Add(1);
|
|
Assert.AreEqual(list.Length, 1);
|
|
Assert.AreNotEqual(list.Capacity, 0);
|
|
|
|
list.Clear();
|
|
}
|
|
}
|
|
}
|