618 lines
18 KiB
C#
618 lines
18 KiB
C#
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
using UnityEngine;
|
|
using NUnit.Framework;
|
|
using System;
|
|
using Unity.Jobs;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Collections.NotBurstCompatible;
|
|
using Unity.Collections.Tests;
|
|
|
|
#pragma warning disable 0219
|
|
#pragma warning disable 0414
|
|
|
|
internal class NativeListJobDebuggerTests : CollectionsTestFixture
|
|
{
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
struct NativeListAddJob : IJob
|
|
{
|
|
NativeList<int> list;
|
|
|
|
public NativeListAddJob(NativeList<int> list) { this.list = list; }
|
|
|
|
public void Execute()
|
|
{
|
|
list.Add(1);
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
struct NativeArrayTest : IJob
|
|
{
|
|
NativeArray<int> array;
|
|
|
|
public NativeArrayTest(NativeArray<int> array) { this.array = array; }
|
|
|
|
public void Execute()
|
|
{
|
|
}
|
|
}
|
|
|
|
struct NestedContainerJob : IJob
|
|
{
|
|
public NativeList<NativeList<int>> nestedContainer;
|
|
|
|
public void Execute()
|
|
{
|
|
nestedContainer.Clear();
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void NativeList_NestedJob_Error()
|
|
{
|
|
var container = new NativeList<NativeList<int>>(CommonRwdAllocator.Handle);
|
|
|
|
var nestedJob = new NestedContainerJob
|
|
{
|
|
nestedContainer = container
|
|
};
|
|
|
|
JobHandle job = default;
|
|
Assert.Throws<InvalidOperationException>(() => { job = nestedJob.Schedule(); });
|
|
job.Complete();
|
|
|
|
container.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void AddElementToListFromJobInvalidatesArray()
|
|
{
|
|
var list = new NativeList<int>(CommonRwdAllocator.Handle);
|
|
list.Add(0);
|
|
|
|
NativeArray<int> arrayBeforeSchedule = list.AsArray();
|
|
Assert.AreEqual(list.Length, 1);
|
|
|
|
var jobData = new NativeListAddJob(list);
|
|
var job = jobData.Schedule();
|
|
|
|
Assert.Throws<ObjectDisposedException>(
|
|
() => {
|
|
int readVal = arrayBeforeSchedule[0];
|
|
});
|
|
Assert.Throws<InvalidOperationException>(() => { NativeArray<int> array = list.AsArray(); Debug.Log(array.Length); });
|
|
Assert.Throws<InvalidOperationException>(() => { int readVal = list.Capacity; });
|
|
Assert.Throws<InvalidOperationException>(() => { list.Dispose(); });
|
|
Assert.Throws<InvalidOperationException>(() => { int readVal = list[0]; });
|
|
|
|
job.Complete();
|
|
|
|
// Assert.AreEqual(1, arrayBeforeSchedule.Length); - temporarily commenting out updated assert checks to ensure editor version promotion succeeds
|
|
Assert.Throws<ObjectDisposedException>(
|
|
() => {
|
|
int readVal = arrayBeforeSchedule[0];
|
|
});
|
|
|
|
Assert.AreEqual(2, list.Length);
|
|
Assert.AreEqual(0, list[0]);
|
|
Assert.AreEqual(1, list[1]);
|
|
|
|
NativeArray<int> arrayAfter = list.AsArray();
|
|
Assert.AreEqual(2, arrayAfter.Length);
|
|
Assert.AreEqual(0, arrayAfter[0]);
|
|
Assert.AreEqual(1, arrayAfter[1]);
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void AccessBefore()
|
|
{
|
|
var list = new NativeList<int>(CommonRwdAllocator.Handle);
|
|
|
|
var jobHandle = new NativeListAddJob(list).Schedule();
|
|
Assert.Throws<InvalidOperationException>(() =>
|
|
{
|
|
list.AsArray();
|
|
});
|
|
|
|
jobHandle.Complete();
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void AccessAfter()
|
|
{
|
|
var list = new NativeList<int>(CommonRwdAllocator.Handle);
|
|
var array = list.AsArray();
|
|
var jobHandle = new NativeListAddJob(list).Schedule();
|
|
Assert.Throws<InvalidOperationException>(() =>
|
|
{
|
|
new NativeArrayTest(array).Schedule(jobHandle);
|
|
});
|
|
jobHandle.Complete();
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void ScheduleDerivedArrayAllowDerivingArrayAgain()
|
|
{
|
|
var list = new NativeList<int>(1, Allocator.Persistent);
|
|
|
|
// The scheduled job only receives a NativeArray thus it can't be resized
|
|
var writeJobHandle = new NativeArrayTest(list.AsArray()).Schedule();
|
|
|
|
// For that reason casting here is legal, as opposed to AddElementToListFromJobInvalidatesArray case where it is not legal
|
|
// Since we NativeList is passed to the job
|
|
#pragma warning disable 0219 // assigned but its value is never used
|
|
NativeArray<int> array = list.AsArray();
|
|
#pragma warning restore 0219
|
|
|
|
list.Dispose(writeJobHandle);
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void ScheduleDerivedArrayExceptions()
|
|
{
|
|
var list = new NativeList<int>(1, Allocator.Persistent);
|
|
|
|
var addListJobHandle = new NativeListAddJob(list).Schedule();
|
|
#pragma warning disable 0219 // assigned but its value is never used
|
|
Assert.Throws<InvalidOperationException>(() => { NativeArray<int> array = list.AsArray(); });
|
|
#pragma warning restore 0219
|
|
|
|
addListJobHandle.Complete();
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void ScheduleDerivedArrayExceptions2()
|
|
{
|
|
var list = new NativeList<int>(1, Allocator.Persistent);
|
|
NativeArray<int> array = list.AsArray();
|
|
|
|
var addListJobHandle = new NativeListAddJob(list).Schedule();
|
|
// The array previously cast should become invalid
|
|
// as soon as the job is scheduled, since we can't predict if an element will be added or not
|
|
Assert.Throws<InvalidOperationException>(() => { new NativeArrayTest(array).Schedule(); });
|
|
|
|
addListJobHandle.Complete();
|
|
list.Dispose();
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
struct ReadOnlyListAccess : IJob
|
|
{
|
|
[ReadOnly]
|
|
NativeList<int> list;
|
|
|
|
public ReadOnlyListAccess(NativeList<int> list) { this.list = list; }
|
|
|
|
public void Execute()
|
|
{
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ReadOnlyListInJobKeepsAsArrayValid()
|
|
{
|
|
var list = new NativeList<int>(CommonRwdAllocator.Handle);
|
|
list.Add(0);
|
|
var arrayBeforeSchedule = list.AsArray();
|
|
|
|
var jobData = new ReadOnlyListAccess(list);
|
|
var job = jobData.Schedule();
|
|
job.Complete();
|
|
|
|
Assert.AreEqual(0, arrayBeforeSchedule[0]);
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void AsArrayJobKeepsAsArrayValid()
|
|
{
|
|
var list = new NativeList<int>(CommonRwdAllocator.Handle);
|
|
list.Add(0);
|
|
var arrayBeforeSchedule = list.AsArray();
|
|
|
|
var jobData = new NativeArrayTest(list.AsArray());
|
|
var job = jobData.Schedule();
|
|
job.Complete();
|
|
|
|
Assert.AreEqual(0, arrayBeforeSchedule[0]);
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
struct NativeListToArrayConversionFromJob : IJob
|
|
{
|
|
public NativeList<int> list;
|
|
|
|
public void Execute()
|
|
{
|
|
list.Add(0);
|
|
list.Add(0);
|
|
|
|
NativeArray<int> arr = list.AsArray();
|
|
arr[0] = 1;
|
|
arr[1] = 2;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void CastListToArrayInsideJob()
|
|
{
|
|
var jobData = new NativeListToArrayConversionFromJob();
|
|
jobData.list = new NativeList<int>(1, Allocator.Persistent);
|
|
jobData.Schedule().Complete();
|
|
|
|
Assert.AreEqual(new int[] { 1, 2 }, jobData.list.ToArrayNBC());
|
|
jobData.list.Dispose();
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
struct WriteJob : IJobParallelFor
|
|
{
|
|
public NativeArray<float> output;
|
|
|
|
public void Execute(int i)
|
|
{
|
|
output[i] = i;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void WriteToArrayFromJobThenReadListFromMainThread()
|
|
{
|
|
var list = new NativeList<float>(1, Allocator.Persistent);
|
|
list.Add(0);
|
|
list.Add(1);
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
var writeJob = new WriteJob();
|
|
writeJob.output = list.AsArray();
|
|
var writeJobHandle = writeJob.Schedule(list.Length, 1);
|
|
|
|
Assert.Throws<InvalidOperationException>(() => { float val = writeJob.output[0]; });
|
|
|
|
writeJobHandle.Complete();
|
|
}
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_DisposeJob()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
var deps = new NativeListAddJob(list).Schedule();
|
|
deps = list.Dispose(deps);
|
|
Assert.IsFalse(list.IsCreated);
|
|
deps.Complete();
|
|
}
|
|
|
|
struct InvalidArrayAccessFromListJob : IJob
|
|
{
|
|
public NativeList<int> list;
|
|
|
|
public void Execute()
|
|
{
|
|
list.Add(1);
|
|
NativeArray<int> array = list.AsArray();
|
|
list.Add(2);
|
|
|
|
// Assert.Throws<InvalidOperationException>(() => { array[0] = 5; }); - temporarily commenting out updated assert checks to ensure editor version promotion succeeds
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void InvalidatedArrayAccessFromListThrowsInsideJob()
|
|
{
|
|
var job = new InvalidArrayAccessFromListJob { list = new NativeList<int>(CommonRwdAllocator.Handle) };
|
|
job.Schedule().Complete();
|
|
job.list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void DisposeAliasedArrayDoesNotThrow()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
var array = list.AsArray();
|
|
Assert.DoesNotThrow(() => { array.Dispose(); });
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
// Burst error BC1071: Unsupported assert type
|
|
// [BurstCompile(CompileSynchronously = true)]
|
|
struct NativeArrayTestReadOnly : IJob
|
|
{
|
|
[ReadOnly]
|
|
NativeArray<int> array;
|
|
|
|
public NativeArrayTestReadOnly(NativeArray<int> array) { this.array = array; }
|
|
|
|
public void Execute()
|
|
{
|
|
var arr = array;
|
|
Assert.Throws<InvalidOperationException>(() => { arr[0] = 5; });
|
|
Assert.AreEqual(7, array[0]);
|
|
}
|
|
}
|
|
|
|
// Burst error BC1071: Unsupported assert type
|
|
// [BurstCompile(CompileSynchronously = true)]
|
|
struct NativeArrayTestAsReadOnly : IJob
|
|
{
|
|
[ReadOnly]
|
|
NativeArray<int>.ReadOnly array;
|
|
|
|
public NativeArrayTestAsReadOnly(NativeArray<int>.ReadOnly array) { this.array = array; }
|
|
|
|
public void Execute()
|
|
{
|
|
var arr = array;
|
|
Assert.AreEqual(7, array[0]);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void ReadOnlyAliasedArrayThrows()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(7);
|
|
new NativeArrayTestReadOnly(list.AsArray()).Schedule().Complete();
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
// Burst error BC1071: Unsupported assert type
|
|
// [BurstCompile(CompileSynchronously = true)]
|
|
struct NativeArrayTestWriteOnly : IJob
|
|
{
|
|
[WriteOnly]
|
|
NativeArray<int> array;
|
|
|
|
public NativeArrayTestWriteOnly(NativeArray<int> array) { this.array = array; }
|
|
|
|
public void Execute()
|
|
{
|
|
var arr = array;
|
|
Assert.Throws<InvalidOperationException>(() => { int read = arr[0]; });
|
|
arr[0] = 7;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void NativeList_AsArray_Jobs()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(0);
|
|
|
|
var writer = list.AsArray();
|
|
var writerJob = new NativeArrayTestWriteOnly(writer).Schedule();
|
|
|
|
var reader = list.AsArray();
|
|
var readerJob = new NativeArrayTestReadOnly(reader).Schedule(writerJob);
|
|
|
|
// Tests that read only container safety check trows...
|
|
var writerJob2 = new NativeArrayTestWriteOnly(reader).Schedule(readerJob);
|
|
|
|
// Tests that write only container safety check trows...
|
|
var readerJob2 = new NativeArrayTestReadOnly(writer).Schedule(writerJob2);
|
|
|
|
readerJob2.Complete();
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void NativeList_AsReadOnly_Jobs()
|
|
{
|
|
var list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(0);
|
|
|
|
var writer = list.AsArray();
|
|
var writerJob = new NativeArrayTestWriteOnly(writer).Schedule();
|
|
|
|
var reader = list.AsReadOnly();
|
|
var readerJob = new NativeArrayTestAsReadOnly(reader).Schedule(writerJob);
|
|
|
|
readerJob.Complete();
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
// Burst error BC1071: Unsupported assert type
|
|
// [BurstCompile(CompileSynchronously = true)]
|
|
struct NativeListTestReadOnly : IJob
|
|
{
|
|
[ReadOnly]
|
|
public NativeArray<int>.ReadOnly reader;
|
|
|
|
public void Execute()
|
|
{
|
|
Assert.True(reader.Contains(7));
|
|
Assert.AreEqual(7, reader[0]);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_AsReadOnly()
|
|
{
|
|
NativeList<int> list;
|
|
JobHandle readerJob;
|
|
|
|
{
|
|
list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(7);
|
|
|
|
var reader = list.AsReadOnly();
|
|
list.Dispose(); // <- cause invalid use
|
|
Assert.Throws<InvalidOperationException>(() => { readerJob = new NativeListTestReadOnly { reader = reader }.Schedule(); });
|
|
}
|
|
|
|
{
|
|
list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(7);
|
|
|
|
var reader = list.AsReadOnly();
|
|
readerJob = new NativeListTestReadOnly { reader = reader }.Schedule();
|
|
}
|
|
|
|
list.Dispose(readerJob);
|
|
readerJob.Complete();
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
struct NativeListTestParallelWriter : IJob
|
|
{
|
|
[WriteOnly]
|
|
public NativeList<int>.ParallelWriter writer;
|
|
|
|
public unsafe void Execute()
|
|
{
|
|
var range = stackalloc int[2] { 7, 3 };
|
|
|
|
writer.AddNoResize(range[0]);
|
|
writer.AddRangeNoResize(range, 1);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_ParallelWriter()
|
|
{
|
|
NativeList<int> list;
|
|
|
|
{
|
|
list = new NativeList<int>(2, Allocator.Persistent);
|
|
var writer = list.AsParallelWriter();
|
|
list.Dispose(); // <- cause invalid use
|
|
Assert.Throws<InvalidOperationException>(() =>
|
|
{
|
|
var writerJob = new NativeListTestParallelWriter { writer = writer }.Schedule();
|
|
writerJob.Complete();
|
|
});
|
|
}
|
|
|
|
{
|
|
list = new NativeList<int>(2, Allocator.Persistent);
|
|
var writer = list.AsParallelWriter();
|
|
var writerJob = new NativeListTestParallelWriter { writer = writer }.Schedule();
|
|
writerJob.Complete();
|
|
}
|
|
|
|
Assert.AreEqual(2, list.Length);
|
|
Assert.AreEqual(7, list[0]);
|
|
Assert.AreEqual(7, list[1]);
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void NativeList_ParallelWriter_NoPtrCaching()
|
|
{
|
|
NativeList<int> list;
|
|
|
|
{
|
|
list = new NativeList<int>(2, Allocator.Persistent);
|
|
var writer = list.AsParallelWriter();
|
|
list.Capacity = 100;
|
|
var writerJob = new NativeListTestParallelWriter { writer = writer }.Schedule();
|
|
writerJob.Complete();
|
|
}
|
|
|
|
Assert.AreEqual(2, list.Length);
|
|
Assert.AreEqual(7, list[0]);
|
|
Assert.AreEqual(7, list[1]);
|
|
|
|
list.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
[TestRequiresCollectionChecks]
|
|
public void NativeList_AsReadOnlyAndParallelWriter()
|
|
{
|
|
NativeList<int> list;
|
|
JobHandle jobHandle;
|
|
|
|
list = new NativeList<int>(Allocator.Persistent);
|
|
list.Add(7);
|
|
|
|
jobHandle = new NativeListTestReadOnly { reader = list.AsReadOnly() }.Schedule();
|
|
jobHandle = new NativeListTestParallelWriter { writer = list.AsParallelWriter() }.Schedule(jobHandle);
|
|
jobHandle = new NativeListTestReadOnly { reader = list.AsReadOnly() }.Schedule(jobHandle);
|
|
jobHandle = new NativeListTestParallelWriter { writer = list.AsParallelWriter() }.Schedule(jobHandle);
|
|
|
|
list.Dispose(jobHandle);
|
|
jobHandle.Complete();
|
|
}
|
|
|
|
unsafe void Expected(ref NativeList<int> list, int expectedLength, int[] expected)
|
|
{
|
|
Assert.AreEqual(0 == expectedLength, list.IsEmpty);
|
|
Assert.AreEqual(list.Length, expectedLength);
|
|
for (var i = 0; i < list.Length; ++i)
|
|
{
|
|
var value = list[i];
|
|
Assert.AreEqual(expected[i], value);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public unsafe void NativeList_RemoveRange()
|
|
{
|
|
var list = new NativeList<int>(10, Allocator.Persistent);
|
|
|
|
int[] range = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
|
|
// test removing from the end
|
|
fixed (int* r = range) list.AddRange(r, 10);
|
|
list.RemoveRange(6, 3);
|
|
Expected(ref list, 7, new int[] { 0, 1, 2, 3, 4, 5, 9 });
|
|
list.Clear();
|
|
|
|
// test removing all but one
|
|
fixed (int* r = range) list.AddRange(r, 10);
|
|
list.RemoveRange(0, 9);
|
|
Expected(ref list, 1, new int[] { 9 });
|
|
list.Clear();
|
|
|
|
// test removing from the front
|
|
fixed (int* r = range) list.AddRange(r, 10);
|
|
list.RemoveRange(0, 3);
|
|
Expected(ref list, 7, new int[] { 3, 4, 5, 6, 7, 8, 9 });
|
|
list.Clear();
|
|
|
|
// test removing from the middle
|
|
fixed (int* r = range) list.AddRange(r, 10);
|
|
list.RemoveRange(0, 3);
|
|
Expected(ref list, 7, new int[] { 3, 4, 5, 6, 7, 8, 9 });
|
|
list.Clear();
|
|
|
|
// test removing whole range
|
|
fixed (int* r = range) list.AddRange(r, 10);
|
|
list.RemoveRange(0, 10);
|
|
Expected(ref list, 0, new int[] { 0 });
|
|
list.Clear();
|
|
|
|
list.Dispose();
|
|
}
|
|
}
|
|
#endif
|