401 lines
14 KiB
C#
401 lines
14 KiB
C#
|
using System;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using NUnit.Framework;
|
||
|
using System.Text.RegularExpressions;
|
||
|
using Unity.Burst;
|
||
|
using Unity.Collections.LowLevel.Unsafe;
|
||
|
using Unity.Jobs;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.TestTools;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
|
||
|
#if UNITY_2019_4_OR_NEWER
|
||
|
namespace ExceptionsFromBurstJobs
|
||
|
{
|
||
|
[BurstCompile]
|
||
|
class ManagedExceptionsBurstJobs
|
||
|
{
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
private static void ThrowNewArgumentException()
|
||
|
{
|
||
|
throw new ArgumentException("A");
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
struct ThrowArgumentExceptionJob : IJob
|
||
|
{
|
||
|
public void Execute()
|
||
|
{
|
||
|
ThrowNewArgumentException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowArgumentException()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("ArgumentException: A"));
|
||
|
|
||
|
var jobData = new ThrowArgumentExceptionJob();
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
private static void ThrowNewArgumentNullException()
|
||
|
{
|
||
|
throw new ArgumentNullException("N");
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
struct ThrowArgumentNullExceptionJob : IJob
|
||
|
{
|
||
|
public void Execute()
|
||
|
{
|
||
|
ThrowNewArgumentNullException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowArgumentNullException()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("System.ArgumentNullException: N"));
|
||
|
|
||
|
var jobData = new ThrowArgumentNullExceptionJob();
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
private static void ThrowNewNullReferenceException()
|
||
|
{
|
||
|
throw new NullReferenceException("N");
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
struct ThrowNullReferenceExceptionJob : IJob
|
||
|
{
|
||
|
public void Execute()
|
||
|
{
|
||
|
ThrowNewNullReferenceException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowNullReferenceException()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: N"));
|
||
|
|
||
|
var jobData = new ThrowNullReferenceExceptionJob();
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
private static void ThrowNewInvalidOperationException()
|
||
|
{
|
||
|
throw new InvalidOperationException("IO");
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
struct ThrowInvalidOperationExceptionJob : IJob
|
||
|
{
|
||
|
public void Execute()
|
||
|
{
|
||
|
ThrowNewInvalidOperationException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowInvalidOperationException()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("InvalidOperationException: IO"));
|
||
|
|
||
|
var jobData = new ThrowInvalidOperationExceptionJob();
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
private static void ThrowNewNotSupportedException()
|
||
|
{
|
||
|
throw new NotSupportedException("NS");
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
struct ThrowNotSupportedExceptionJob : IJob
|
||
|
{
|
||
|
public void Execute()
|
||
|
{
|
||
|
ThrowNewNotSupportedException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowNotSupportedException()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("NotSupportedException: NS"));
|
||
|
|
||
|
var jobData = new ThrowNotSupportedExceptionJob();
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
private static void ThrowNewUnityException()
|
||
|
{
|
||
|
throw new UnityException("UE");
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
struct ThrowUnityExceptionJob : IJob
|
||
|
{
|
||
|
public void Execute()
|
||
|
{
|
||
|
ThrowNewUnityException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowUnityException()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("UnityException: UE"));
|
||
|
|
||
|
var jobData = new ThrowUnityExceptionJob();
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
private static void ThrowNewIndexOutOfRangeException()
|
||
|
{
|
||
|
throw new IndexOutOfRangeException("IOOR");
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
struct ThrowIndexOutOfRangeExceptionJob : IJob
|
||
|
{
|
||
|
public void Execute()
|
||
|
{
|
||
|
ThrowNewIndexOutOfRangeException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowIndexOutOfRange()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("IndexOutOfRangeException: IOOR"));
|
||
|
|
||
|
var jobData = new ThrowIndexOutOfRangeExceptionJob();
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
private unsafe struct ThrowFromDereferenceNullJob : IJob
|
||
|
{
|
||
|
[NativeDisableUnsafePtrRestriction]
|
||
|
public int* Ptr;
|
||
|
|
||
|
public void Execute()
|
||
|
{
|
||
|
*Ptr = 42;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowFromDereferenceNull()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: Object reference not set to an instance of an object"));
|
||
|
|
||
|
var jobData = new ThrowFromDereferenceNullJob() { Ptr = null };
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
private unsafe struct ThrowFromDivideByZeroJob : IJob
|
||
|
{
|
||
|
public int Int;
|
||
|
|
||
|
public void Execute()
|
||
|
{
|
||
|
Int = 42 / Int;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowFromDivideByZero()
|
||
|
{
|
||
|
if (RuntimeInformation.OSArchitecture == Architecture.Arm64)
|
||
|
{
|
||
|
// Arm64 does not throw a divide-by-zero exception, instead it flushes the result to zero.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("DivideByZeroException: Attempted to divide by zero"));
|
||
|
|
||
|
var jobData = new ThrowFromDivideByZeroJob() { Int = 0 };
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
private unsafe delegate void ExceptionDelegate(int* a);
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
private static unsafe void DereferenceNull(int* a)
|
||
|
{
|
||
|
*a = 42;
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
unsafe struct ThrowFromFunctionPointerJob : IJob
|
||
|
{
|
||
|
#pragma warning disable 649
|
||
|
[NativeDisableUnsafePtrRestriction] public IntPtr FuncPtr;
|
||
|
[NativeDisableUnsafePtrRestriction] public int* Ptr;
|
||
|
#pragma warning restore 649
|
||
|
|
||
|
public void Execute()
|
||
|
{
|
||
|
new FunctionPointer<ExceptionDelegate>(FuncPtr).Invoke(Ptr);
|
||
|
|
||
|
// Set Ptr to non null which should never be hit because the above will throw.
|
||
|
Ptr = (int*)0x42;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public unsafe void ThrowFromFunctionPointer()
|
||
|
{
|
||
|
var funcPtr = BurstCompiler.CompileFunctionPointer<ExceptionDelegate>(DereferenceNull);
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: Object reference not set to an instance of an object"));
|
||
|
var job = new ThrowFromFunctionPointerJob { FuncPtr = funcPtr.Value, Ptr = null };
|
||
|
job.Run();
|
||
|
Assert.AreEqual((IntPtr)job.Ptr, (IntPtr)0);
|
||
|
}
|
||
|
|
||
|
[BurstCompile(CompileSynchronously = true)]
|
||
|
private unsafe struct ThrowFromDereferenceNullParallelJob : IJobParallelFor
|
||
|
{
|
||
|
[NativeDisableUnsafePtrRestriction]
|
||
|
public int* Ptr;
|
||
|
|
||
|
public void Execute(int index)
|
||
|
{
|
||
|
*Ptr = index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
// No RuntimePlatform.OSXEditor in this list because of a subtle Mojave only bug.
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowFromDereferenceNullParallel()
|
||
|
{
|
||
|
var messageCount = 0;
|
||
|
|
||
|
void OnMessage(string message, string stackTrace, LogType type)
|
||
|
{
|
||
|
Assert.AreEqual(LogType.Exception, type);
|
||
|
StringAssert.Contains("NullReferenceException: Object reference not set to an instance of an object", message);
|
||
|
messageCount++;
|
||
|
}
|
||
|
|
||
|
LogAssert.ignoreFailingMessages = true;
|
||
|
Application.logMessageReceivedThreaded += OnMessage;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
var jobData = new ThrowFromDereferenceNullParallelJob() { Ptr = null };
|
||
|
jobData.Schedule(128, 1).Complete();
|
||
|
|
||
|
Assert.GreaterOrEqual(messageCount, 1);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
Application.logMessageReceivedThreaded -= OnMessage;
|
||
|
LogAssert.ignoreFailingMessages = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private unsafe struct ThrowFromDereferenceNullManagedJob : IJob
|
||
|
{
|
||
|
[NativeDisableUnsafePtrRestriction]
|
||
|
public int* Ptr;
|
||
|
|
||
|
public void Execute()
|
||
|
{
|
||
|
*Ptr = 42;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowFromDereferenceNullManaged()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: Object reference not set to an instance of an object"));
|
||
|
|
||
|
var jobData = new ThrowFromDereferenceNullManagedJob() { Ptr = null };
|
||
|
jobData.Run();
|
||
|
}
|
||
|
|
||
|
[Test]
|
||
|
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
|
||
|
public void ThrowFromDereferenceNullBurstDisabled()
|
||
|
{
|
||
|
var previous = BurstCompiler.Options.EnableBurstCompilation;
|
||
|
BurstCompiler.Options.EnableBurstCompilation = false;
|
||
|
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: Object reference not set to an instance of an object"));
|
||
|
|
||
|
var jobData = new ThrowFromDereferenceNullJob() { Ptr = null };
|
||
|
jobData.Run();
|
||
|
|
||
|
BurstCompiler.Options.EnableBurstCompilation = previous;
|
||
|
}
|
||
|
|
||
|
private unsafe struct ThrowFromManagedStackOverflowJob : IJob
|
||
|
{
|
||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||
|
private static int DoStackOverflow(ref int x)
|
||
|
{
|
||
|
// Copy just to make the stack grow.
|
||
|
var copy = x;
|
||
|
return copy + DoStackOverflow(ref x);
|
||
|
}
|
||
|
|
||
|
public int Int;
|
||
|
|
||
|
public void Execute()
|
||
|
{
|
||
|
Int = DoStackOverflow(ref Int);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//[Test]
|
||
|
//[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
|
||
|
public void ThrowFromManagedStackOverflow()
|
||
|
{
|
||
|
LogAssert.Expect(LogType.Exception, new Regex("StackOverflowException: The requested operation caused a stack overflow"));
|
||
|
|
||
|
var jobData = new ThrowFromManagedStackOverflowJob() { Int = 1 };
|
||
|
jobData.Run();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // #if UNITY_2019_3_OR_NEWER
|