using NUnit.Framework; using System; using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Collections.Tests; using Unity.Jobs; class NativeReferenceTests : CollectionsTestCommonBase { [Test] public void NativeReference_AllocateDeallocate_ReadWrite() { var reference = new NativeReference(Allocator.Persistent); reference.Value = 1; Assert.That(reference.Value, Is.EqualTo(1)); reference.Dispose(); } [Test] public void NativeReference_CopyFrom() { var referenceA = new NativeReference(Allocator.Persistent); var referenceB = new NativeReference(Allocator.Persistent); referenceA.Value = new TestData { Integer = 42, Float = 3.1416f }; referenceB.CopyFrom(referenceA); Assert.That(referenceB.Value, Is.EqualTo(referenceA.Value)); referenceA.Dispose(); referenceB.Dispose(); } [Test] public void NativeReference_CopyTo() { var referenceA = new NativeReference(Allocator.Persistent); var referenceB = new NativeReference(Allocator.Persistent); referenceA.Value = new TestData { Integer = 42, Float = 3.1416f }; referenceA.CopyTo(referenceB); Assert.That(referenceB.Value, Is.EqualTo(referenceA.Value)); referenceA.Dispose(); referenceB.Dispose(); } [Test] [TestRequiresCollectionChecks] public void NativeReference_NullThrows() { var reference = new NativeReference(); Assert.Throws(() => reference.Value = 5); } [Test] public void NativeReference_CopiedIsKeptInSync() { var reference = new NativeReference(Allocator.Persistent); var referenceCopy = reference; reference.Value = 42; Assert.That(reference.Value, Is.EqualTo(referenceCopy.Value)); reference.Dispose(); } struct TestData { public int Integer; public float Float; } [BurstCompile(CompileSynchronously = true)] struct TempNativeReferenceInJob : IJob { public NativeReference Output; public void Execute() { var reference = new NativeReference(Allocator.Temp); reference.Value = 42; Output.Value = reference.Value; reference.Dispose(); } } [Test] public void NativeReference_TempInBurstJob() { var job = new TempNativeReferenceInJob() { Output = new NativeReference(CommonRwdAllocator.Handle) }; job.Schedule().Complete(); Assert.That(job.Output.Value, Is.EqualTo(42)); job.Output.Dispose(); } [Test] public unsafe void NativeReference_UnsafePtr() { var reference = new NativeReference(CommonRwdAllocator.Handle); var job = new TempNativeReferenceInJob() { Output = reference }; var jobHandle = job.Schedule(); #if ENABLE_UNITY_COLLECTIONS_CHECKS Assert.Throws(() => reference.GetUnsafePtr()); Assert.Throws(() => reference.GetUnsafeReadOnlyPtr()); #endif Assert.DoesNotThrow(() => reference.GetUnsafePtrWithoutChecks()); jobHandle.Complete(); Assert.AreEqual(*reference.GetUnsafePtr(), 42); Assert.AreEqual(*reference.GetUnsafeReadOnlyPtr(), 42); Assert.AreEqual(*reference.GetUnsafePtrWithoutChecks(), 42); Assert.That(job.Output.Value, Is.EqualTo(42)); job.Output.Dispose(); } [Test] public void NativeReference_DisposeJob() { var reference = new NativeReference(Allocator.Persistent); Assert.That(reference.IsCreated, Is.True); Assert.DoesNotThrow(() => reference.Value = 99); var disposeJob = reference.Dispose(default); Assert.That(reference.IsCreated, Is.False); #if ENABLE_UNITY_COLLECTIONS_CHECKS Assert.Throws(() => reference.Value = 3); #endif disposeJob.Complete(); } [Test] public void NativeReference_NoGCAllocations() { var reference = new NativeReference(Allocator.Persistent); GCAllocRecorder.ValidateNoGCAllocs(() => { reference.Value = 1; reference.Value++; }); Assert.That(reference.Value, Is.EqualTo(2)); reference.Dispose(); } [Test] public void NativeReference_Equals() { var referenceA = new NativeReference(12345, Allocator.Persistent); var referenceB = new NativeReference(Allocator.Persistent) { Value = 12345 }; Assert.That(referenceA, Is.EqualTo(referenceB)); referenceB.Value = 54321; Assert.AreNotEqual(referenceA, referenceB); referenceA.Dispose(); referenceB.Dispose(); } [Test] public void NativeReference_ReadOnly() { var referenceA = new NativeReference(12345, Allocator.Persistent); var referenceB = new NativeReference(Allocator.Persistent) { Value = 12345 }; var referenceARO = referenceA.AsReadOnly(); Assert.AreEqual(referenceARO.Value, referenceB.Value); referenceA.Dispose(); referenceB.Dispose(); } [Test] public void NativeReference_GetHashCode() { var integer = 42; var reference = new NativeReference(integer, Allocator.Persistent); Assert.That(reference.GetHashCode(), Is.EqualTo(integer.GetHashCode())); reference.Dispose(); } [Test] public void NativeReference_CustomAllocatorTest() { AllocatorManager.Initialize(); var allocatorHelper = new AllocatorHelper(AllocatorManager.Persistent); ref var allocator = ref allocatorHelper.Allocator; allocator.Initialize(); using (var container = new NativeReference(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 NativeReference(Allocator->Handle)) { } } } } [Test] public unsafe void NativeReference_BurstedCustomAllocatorTest() { AllocatorManager.Initialize(); var allocatorHelper = new AllocatorHelper(AllocatorManager.Persistent); ref var allocator = ref allocatorHelper.Allocator; allocator.Initialize(); var allocatorPtr = (CustomAllocatorTests.CountingAllocator*)UnsafeUtility.AddressOf(ref allocator); unsafe { var handle = new BurstedCustomAllocatorJob {Allocator = allocatorPtr}.Schedule(); handle.Complete(); } Assert.IsTrue(allocator.WasUsed); allocator.Dispose(); allocatorHelper.Dispose(); AllocatorManager.Shutdown(); } public struct NestedContainer { public NativeReference data; } [Test] public void NativeReference_Nested() { var inner = new NativeReference(CommonRwdAllocator.Handle); NestedContainer nestedStruct = new NestedContainer { data = inner }; var containerNestedStruct = new NativeReference(CommonRwdAllocator.Handle); var containerNested = new NativeReference>(CommonRwdAllocator.Handle); containerNested.Value = inner; containerNestedStruct.Value = nestedStruct; containerNested.Dispose(); containerNestedStruct.Dispose(); inner.Dispose(); } struct NestedContainerJob : IJob { public NativeReference> nestedContainer; public void Execute() { nestedContainer.Value = default; } } [Test] [TestRequiresCollectionChecks] public void NativeReference_NestedJob_Error() { var container = new NativeReference>(CommonRwdAllocator.Handle); var nestedJob = new NestedContainerJob { nestedContainer = container }; JobHandle job = default; Assert.Throws(() => { job = nestedJob.Schedule(); }); job.Complete(); container.Dispose(); } }