UnityGame/Library/PackageCache/com.unity.collections/Unity.Collections.Tests/NativeListTests_JobDebugger.cs

618 lines
18 KiB
C#
Raw Normal View History

2024-10-27 10:53:47 +03:00
#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