using NUnit.Framework; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Collections.Tests; using Unity.Jobs; using System; using Unity.Jobs.Tests.ManagedJobs; [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] internal struct GenericContainerJob : IJob { public T data; public void Execute() { // We just care about creating job dependencies } } internal struct GenericContainerReadonlyJob : IJob { [ReadOnly] public T data; public void Execute() { // We just care about creating job dependencies } } internal class GenericContainerTests : CollectionsTestFixture { UnsafeAppendBuffer CreateEmpty_UnsafeAppendBuffer() { var container = new UnsafeAppendBuffer(0, 8, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeBitArray CreateEmpty_NativeBitArray() { var container = new NativeBitArray(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void NativeBitArray_IsCreated_Uninitialized() { NativeBitArray container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); NativeBitArray.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } UnsafeBitArray CreateEmpty_UnsafeBitArray() { var container = new UnsafeBitArray(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafeBitArray_IsCreated_Uninitialized() { UnsafeBitArray container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); UnsafeBitArray.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } NativeHashMap CreateEmpty_NativeHashMap() { var container = new NativeHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void NativeHashMap_IsCreated_Uninitialized() { NativeHashMap container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); NativeHashMap.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } UnsafeHashMap CreateEmpty_UnsafeHashMap() { var container = new UnsafeHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafeHashMap_IsCreated_Uninitialized() { UnsafeHashMap container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); UnsafeHashMap.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } NativeHashSet CreateEmpty_NativeHashSet() { var container = new NativeHashSet(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void NativeHashSet_IsCreated_Uninitialized() { NativeHashSet container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); NativeHashSet.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } UnsafeHashSet CreateEmpty_UnsafeHashSet() { var container = new UnsafeHashSet(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafeHashSet_IsCreated_Uninitialized() { UnsafeHashSet container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); UnsafeHashSet.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } NativeList CreateEmpty_NativeList() { var container = new NativeList(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void NativeList_IsCreated_Uninitialized() { NativeList container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); NativeArray.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); // NativeArray doesn't have IsEmpty property -> Assert.True(containerRo.IsEmpty); } UnsafeList CreateEmpty_UnsafeList() { var container = new UnsafeList(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafeList_IsCreated_Uninitialized() { UnsafeList container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); UnsafeList.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } UnsafePtrList CreateEmpty_UnsafePtrList() { var container = new UnsafePtrList(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafePtrList_IsCreated_Uninitialized() { UnsafePtrList container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); UnsafePtrList.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } NativeParallelHashMap CreateEmpty_NativeParallelHashMap() { var container = new NativeParallelHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void NativeParallelHashMap_IsCreated_Uninitialized() { NativeParallelHashMap container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); NativeParallelHashMap.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } UnsafeParallelHashMap CreateEmpty_UnsafeParallelHashMap() { var container = new UnsafeParallelHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafeParallelHashMap_IsCreated_Uninitialized() { UnsafeParallelHashMap container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); UnsafeParallelHashMap.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } NativeParallelHashSet CreateEmpty_NativeParallelHashSet() { var container = new NativeParallelHashSet(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void NativeParallelHashSet_IsCreated_Uninitialized() { NativeParallelHashSet container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); NativeParallelHashSet.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } UnsafeParallelHashSet CreateEmpty_UnsafeParallelHashSet() { var container = new UnsafeParallelHashSet(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafeParallelHashSet_IsCreated_Uninitialized() { UnsafeParallelHashSet container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); UnsafeParallelHashSet.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } NativeParallelMultiHashMap CreateEmpty_NativeParallelMultiHashMap() { var container = new NativeParallelMultiHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void NativeParallelMultiHashMap_IsCreated_Uninitialized() { NativeParallelMultiHashMap container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); NativeParallelMultiHashMap.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } UnsafeParallelMultiHashMap CreateEmpty_UnsafeParallelMultiHashMap() { var container = new UnsafeParallelMultiHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafeParallelMultiHashMap_IsCreated_Uninitialized() { UnsafeParallelMultiHashMap container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); UnsafeParallelMultiHashMap.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); Assert.True(containerRo.IsEmpty); } NativeQueue CreateEmpty_NativeQueue() { var container = new NativeQueue(Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty()); return container; } void NativeQueue_IsCreated_Uninitialized() { NativeQueue container = default; Assert.False(container.IsCreated); NativeQueue.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); } UnsafeQueue CreateEmpty_UnsafeQueue() { var container = new UnsafeQueue(Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty()); return container; } void UnsafeQueue_IsCreated_Uninitialized() { UnsafeQueue container = default; Assert.False(container.IsCreated); UnsafeQueue.ReadOnly containerRo = default; Assert.False(containerRo.IsCreated); } NativeReference CreateEmpty_NativeReference() { var container = new NativeReference(Allocator.Persistent); Assert.True(container.IsCreated); return container; } NativeRingQueue CreateEmpty_NativeRingQueue() { var container = new NativeRingQueue(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void NativeRingQueue_IsCreated_Uninitialized() { NativeRingQueue container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); } UnsafeRingQueue CreateEmpty_UnsafeRingQueue() { var container = new UnsafeRingQueue(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } void UnsafeRingQueue_IsCreated_Uninitialized() { UnsafeRingQueue container = default; Assert.False(container.IsCreated); Assert.True(container.IsEmpty); } NativeStream CreateEmpty_NativeStream() { var container = new NativeStream(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty()); return container; } UnsafeStream CreateEmpty_UnsafeStream() { var container = new UnsafeStream(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty()); return container; } NativeText CreateEmpty_NativeText() { var container = new NativeText(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeText CreateEmpty_UnsafeText() { var container = new UnsafeText(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } //------------------------------------------------------------------------------------------------------- [Test] public void Test_IsCreated_Uninitialized() { NativeBitArray_IsCreated_Uninitialized(); UnsafeBitArray_IsCreated_Uninitialized(); NativeHashMap_IsCreated_Uninitialized(); UnsafeHashMap_IsCreated_Uninitialized(); NativeHashSet_IsCreated_Uninitialized(); UnsafeHashSet_IsCreated_Uninitialized(); NativeList_IsCreated_Uninitialized(); UnsafeList_IsCreated_Uninitialized(); UnsafePtrList_IsCreated_Uninitialized(); NativeParallelHashMap_IsCreated_Uninitialized(); UnsafeParallelHashMap_IsCreated_Uninitialized(); NativeParallelHashSet_IsCreated_Uninitialized(); UnsafeParallelHashSet_IsCreated_Uninitialized(); NativeParallelMultiHashMap_IsCreated_Uninitialized(); UnsafeParallelMultiHashMap_IsCreated_Uninitialized(); NativeQueue_IsCreated_Uninitialized(); UnsafeQueue_IsCreated_Uninitialized(); NativeRingQueue_IsCreated_Uninitialized(); UnsafeRingQueue_IsCreated_Uninitialized(); } //------------------------------------------------------------------------------------------------------- void Test_Dispose_Uninitialized() where T : INativeDisposable { T uninitialized = default; Assert.DoesNotThrow(() => uninitialized.Dispose()); Assert.DoesNotThrow(() => uninitialized.Dispose(default)); } [Test] public void INativeDisposable_Dispose_Uninitialized() { Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized(); } //------------------------------------------------------------------------------------------------------- void Test_Unsafe_Double_Dispose(T container) where T : INativeDisposable { Assert.DoesNotThrow(() => container.Dispose()); Assert.DoesNotThrow(() => container.Dispose()); } void Test_Native_Double_Dispose(T container) where T : INativeDisposable { Assert.DoesNotThrow(() => container.Dispose()); #if ENABLE_UNITY_COLLECTIONS_CHECKS Assert.Throws(() => container.Dispose()); #else Assert.DoesNotThrow(() => container.Dispose()); #endif } [Test] public void INativeDisposable_Init_Double_Dispose() { Test_Native_Double_Dispose(CreateEmpty_NativeBitArray()); Test_Native_Double_Dispose(CreateEmpty_NativeHashMap()); Test_Native_Double_Dispose(CreateEmpty_NativeHashSet()); Test_Native_Double_Dispose(CreateEmpty_NativeList()); Test_Native_Double_Dispose(CreateEmpty_NativeParallelHashMap()); Test_Native_Double_Dispose(CreateEmpty_NativeParallelHashSet()); Test_Native_Double_Dispose(CreateEmpty_NativeParallelMultiHashMap()); Test_Native_Double_Dispose(CreateEmpty_NativeQueue()); Test_Native_Double_Dispose(CreateEmpty_NativeReference()); Test_Native_Double_Dispose(CreateEmpty_NativeRingQueue()); Test_Native_Double_Dispose(CreateEmpty_NativeStream()); Test_Native_Double_Dispose(CreateEmpty_NativeText()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeAppendBuffer()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeBitArray()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeHashMap()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeHashSet()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeList()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafePtrList()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeParallelHashMap()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeParallelHashSet()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeParallelMultiHashMap()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeQueue()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeRingQueue()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeStream()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeText()); } //------------------------------------------------------------------------------------------------------- void Test_Unsafe_Double_Dispose_Job(T container) where T : INativeDisposable { Assert.DoesNotThrow(() => container.Dispose(default)); Assert.DoesNotThrow(() => container.Dispose(default)); } void Test_Native_Double_Dispose_Job(T container) where T : INativeDisposable { Assert.DoesNotThrow(() => container.Dispose(default)); #if ENABLE_UNITY_COLLECTIONS_CHECKS Assert.Throws(() => container.Dispose(default)); #else Assert.DoesNotThrow(() => container.Dispose(default)); #endif } [Test] public void INativeDisposable_Init_Double_Dispose_Job() { Test_Native_Double_Dispose_Job(CreateEmpty_NativeBitArray()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeHashMap()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeHashSet()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeList()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeParallelHashMap()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeParallelHashSet()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeParallelMultiHashMap()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeQueue()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeReference()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeRingQueue()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeStream()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeText()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeAppendBuffer()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeBitArray()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeHashMap()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeHashSet()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeList()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafePtrList()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeParallelHashMap()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeParallelHashSet()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeParallelMultiHashMap()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeQueue()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeRingQueue()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeStream()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeText()); } //------------------------------------------------------------------------------------------------------- void Test_Dispose_Job_Missing_Dependency(T container) where T : INativeDisposable { GenericContainerJob job = new GenericContainerJob() { data = container }; JobHandle jobHandle = job.Schedule(); Assert.Throws(() => container.Dispose(default)); Assert.DoesNotThrow(() => jobHandle = container.Dispose(jobHandle)); jobHandle.Complete(); } [Test] [TestRequiresCollectionChecks("Tests dispose job while another job is scheduled - crashes without safety system")] public void INativeDisposable_Dispose_Job_Missing_Dependency() { Test_Dispose_Job_Missing_Dependency(new NativeBitArray(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeHashSet(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeList(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeParallelHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeParallelHashSet(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeParallelMultiHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeQueue(Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeReference(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeRingQueue(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeStream(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeText(16, Allocator.Persistent)); } //------------------------------------------------------------------------------------------------------- void Test_Dispose_Job_Then_Schedule_Work(T container) where T : INativeDisposable { GenericContainerJob job = new GenericContainerJob() { data = container }; JobHandle jobHandle = container.Dispose(default); Assert.Throws(() => job.Schedule(jobHandle)); jobHandle.Complete(); } [Test] [TestRequiresCollectionChecks("Tests job depending on a dispose job with same data - crashes without safety system")] public void INativeDisposable_Dispose_Job_Then_Schedule_Work() { Test_Dispose_Job_Then_Schedule_Work(new NativeBitArray(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeHashSet(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeList(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeParallelHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeParallelHashSet(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeParallelMultiHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeQueue(Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeReference(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeRingQueue(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeStream(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeText(16, Allocator.Persistent)); } //------------------------------------------------------------------------------------------------------- // Avoid running this test when on older unity releases since those editor versions // used a global safety handle for temp allocations which could lead to invalid safety errors // if we were to perform the safety checks when writing the Length that this test validates // (The test uses Persistent allocations, but the code in NativeList.Length is conditional on // this define so we make the tests conditional as well) #if UNITY_2022_2_16F1_OR_NEWER void Test_Change_Length_Missing_Dependency(T container) where T : unmanaged, IIndexable where U : unmanaged { int localLength = 0; // Readonly Job { var job = new GenericContainerReadonlyJob() { data = container }; var jobHandle = job.Schedule(); Assert.DoesNotThrow(() => localLength = container.Length); // Reading is safe Assert.Throws(() => container.Length = 0); // Writing while a job is in flight it not safe jobHandle.Complete(); Assert.DoesNotThrow(() => container.Length = 0); } // ReadWrite job { var job = new GenericContainerJob() { data = container }; var jobHandle = job.Schedule(); Assert.Throws(() => localLength = container.Length); // Reading is not safe Assert.Throws(() => container.Length = 0); // Writing while a job is in flight it not safe jobHandle.Complete(); Assert.DoesNotThrow(() => localLength = container.Length); Assert.DoesNotThrow(() => container.Length = 0); } } [Test] [TestRequiresCollectionChecks()] public void IIndexable_Change_Length_Missing_Dependency() { var container = new NativeList(16, Allocator.Persistent); Test_Change_Length_Missing_Dependency, int>(container); container.Dispose(); } #endif //------------------------------------------------------------------------------------------------------- struct NativeHashMapJobForEach : IJob { public NativeHashMap input; public void Execute() { foreach (var _ in input) { } } } struct NativeHashMapJobForEachReadOnly : IJob { public NativeHashMap.ReadOnly input; public void Execute() { foreach (var _ in input) { } } } struct NativeHashMapJobForEachEnumerator : IJob { [ReadOnly] public NativeHashMap.Enumerator input; public void Execute() { while (input.MoveNext()) { } } } [Test] [TestRequiresCollectionChecks("Tests depend on safety system to catch incorrect use.")] public void ForEach() { // CreateEmpty_NativeBitArray(); { var container = CreateEmpty_NativeHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } new NativeHashMapJobForEach { input = container }.Run(); new NativeHashMapJobForEachReadOnly { input = ro }.Run(); new NativeHashMapJobForEachEnumerator { input = container.GetEnumerator() }.Run(); new NativeHashMapJobForEachEnumerator { input = ro.GetEnumerator() }.Run(); }); { var job = new NativeHashMapJobForEach { input = container }.Schedule(); Assert.Throws(() => { container.Add(123, 456); }); job.Complete(); Assert.DoesNotThrow(() => container.Add(123, 456)); container.Clear(); } { var job = new NativeHashMapJobForEachReadOnly { input = ro }.Schedule(); Assert.Throws(() => { container.Add(123, 456); }); job.Complete(); Assert.DoesNotThrow(() => container.Add(123, 456)); container.Clear(); } { var job = new NativeHashMapJobForEachEnumerator { input = container.GetEnumerator() }.Schedule(); Assert.Throws(() => { container.Add(123, 456); }); job.Complete(); Assert.DoesNotThrow(() => container.Add(123, 456)); container.Clear(); } { var job = new NativeHashMapJobForEachEnumerator { input = ro.GetEnumerator() }.Schedule(); Assert.Throws(() => { container.Add(123, 456); }); job.Complete(); Assert.DoesNotThrow(() => container.Add(123, 456)); container.Clear(); } { var iter = container.GetEnumerator(); container.Add(123, 456); Assert.Throws(() => { while (iter.MoveNext()) { } }); Assert.DoesNotThrow(() => container.Remove(123)); Assert.AreEqual(0, container.Count); } { var iter = container.AsReadOnly().GetEnumerator(); container.Add(123, 456); Assert.Throws(() => { while (iter.MoveNext()) { } }); Assert.DoesNotThrow(() => container.Remove(123)); Assert.AreEqual(0, container.Count); } container.Dispose(); } { var container = CreateEmpty_NativeHashSet(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeList(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeParallelHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeParallelHashSet(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeParallelMultiHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeQueue(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { // foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } // CreateEmpty_NativeReference(); // CreateEmpty_NativeRingQueue(); // CreateEmpty_NativeStream(); { var container = CreateEmpty_NativeText(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } // CreateEmpty_UnsafeAppendBuffer(); // CreateEmpty_UnsafeBitArray { var container = CreateEmpty_UnsafeHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeHashSet(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeList(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } // CreateEmpty_UnsafePtrList(); - not possible to implement intefrace because container returns T*, and interface wants T as return value. { var container = CreateEmpty_UnsafeParallelHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeParallelHashSet(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeParallelMultiHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeQueue(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { // foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } // CreateEmpty_UnsafeRingQueue // CreateEmpty_UnsafeStream // CreateEmpty_UnsafeText } }