protected TryExpectedExceptionResult TryExpectedException(ExecutionContext context, Action action, string contextName, Func <Type, bool> expectedException, string expectedExceptionName, bool isTargetException, bool requireException = true) { Type caughtType = null; Exception caughtException = null; try { action(); } catch (Exception ex) { if (isTargetException && ex is TargetInvocationException) { ex = ((TargetInvocationException)ex).InnerException; } if (ex is NUnitException) { ex = ex.InnerException; } caughtException = ex; if (caughtException != null) { caughtType = caughtException.GetType(); } } if (caughtException == null && !requireException) { return(TryExpectedExceptionResult.DidNotThrowException); } if (caughtType != null && expectedException(caughtType)) { var loggedDiagnosticIds = GetLoggedDiagnosticIds().OrderBy(x => x); var expectedDiagnosticIds = _expectedDiagnosticIds.OrderBy(x => x); string GetDiagnosticIds(IEnumerable <DiagnosticId> diagnosticIds) => string.Join(",", diagnosticIds); if (!loggedDiagnosticIds.SequenceEqual(expectedDiagnosticIds)) { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expecting diagnostic(s) to be logged with IDs {GetDiagnosticIds(_expectedDiagnosticIds)} but instead the following diagnostic(s) were logged: {GetDiagnosticIds(loggedDiagnosticIds)}"); return(TryExpectedExceptionResult.ThrewUnexpectedException); } else { context.CurrentResult.SetResult(ResultState.Success); return(TryExpectedExceptionResult.ThrewExpectedException); } } else if (caughtType != null) { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expected {expectedExceptionName} but got {caughtType.Name}. Exception: {caughtException}"); return(TryExpectedExceptionResult.ThrewUnexpectedException); } else { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expected {expectedExceptionName} but no exception was thrown"); return(TryExpectedExceptionResult.ThrewUnexpectedException); } }
public override TestResult Execute(ExecutionContext context) { TestResult lastResult = null; for (int i = 0; i < GetRunCount(); i++) { lastResult = ExecuteMethod(context); } return lastResult; }
protected TryExpectedExceptionResult TryExpectedException(ExecutionContext context, Action action, string contextName, Func <Type, bool> expectedException, string expectedExceptionName, bool isTargetException, bool requireException = true) { Type caughtType = null; Exception caughtException = null; try { action(); } catch (Exception ex) { if (isTargetException && ex is TargetInvocationException) { ex = ((TargetInvocationException)ex).InnerException; } if (ex is NUnitException) { ex = ex.InnerException; } caughtException = ex; if (caughtException != null) { caughtType = caughtException.GetType(); } } if (caughtException == null && !requireException) { return(TryExpectedExceptionResult.DidNotThrowException); } if (caughtType != null && expectedException(caughtType)) { if (!CheckExpectedDiagnostics(context, contextName)) { return(TryExpectedExceptionResult.ThrewUnexpectedException); } else { context.CurrentResult.SetResult(ResultState.Success); return(TryExpectedExceptionResult.ThrewExpectedException); } } else if (caughtType != null) { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expected {expectedExceptionName} but got {caughtType.Name}. Exception: {caughtException}"); return(TryExpectedExceptionResult.ThrewUnexpectedException); } else { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expected {expectedExceptionName} but no exception was thrown"); return(TryExpectedExceptionResult.ThrewUnexpectedException); } }
protected bool CheckExpectedDiagnostics(ExecutionContext context, string contextName) { var loggedDiagnosticIds = GetLoggedDiagnosticIds().OrderBy(x => x); var expectedDiagnosticIds = _expectedDiagnosticIds.OrderBy(x => x); if (!loggedDiagnosticIds.SequenceEqual(expectedDiagnosticIds)) { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expecting diagnostic(s) to be logged with IDs {GetDiagnosticIds(_expectedDiagnosticIds)} but instead the following diagnostic(s) were logged: {GetDiagnosticIds(loggedDiagnosticIds)}"); return(false); } return(true); }
private bool CheckExpectedDiagnostics(ExecutionContext context, string contextName) { var loggedDiagnosticIds = GetLoggedDiagnosticIds().OrderBy(x => x); var expectedDiagnosticIds = _expectedDiagnosticIds.OrderBy(x => x); string GetDiagnosticIds(IEnumerable<DiagnosticId> diagnosticIds) => string.Join(",", diagnosticIds); if (!loggedDiagnosticIds.SequenceEqual(expectedDiagnosticIds)) { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expecting diagnostic(s) to be logged with IDs {GetDiagnosticIds(_expectedDiagnosticIds)} but instead the following diagnostic(s) were logged: {GetDiagnosticIds(loggedDiagnosticIds)}"); return false; } return true; }
public override TestResult Execute(ExecutionContext context) { // Make sure that we are not running in // the tests lock (typeof(TestCompilerCommandBase)) { TestResult lastResult = null; for (int i = 0; i < GetRunCount(); i++) { lastResult = ExecuteMethod(context); } return(lastResult); } }
protected void TryExpectedException(ExecutionContext context, Action action, string contextName, Func <Type, bool> expectedException, string expectedExceptionName, bool isTargetException) { Type caughtType = null; Exception caughtException = null; try { action(); } catch (Exception ex) { if (isTargetException && ex is TargetInvocationException) { ex = ((TargetInvocationException)ex).InnerException; } if (ex is NUnitException) { ex = ex.InnerException; } caughtException = ex; if (caughtException != null) { caughtType = caughtException.GetType(); } } if (caughtType != null && expectedException(caughtType)) { context.CurrentResult.SetResult(ResultState.Success); } else if (caughtType != null) { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expected {expectedExceptionName} but got {caughtType.Name}. Exception: {caughtException}"); } else { context.CurrentResult.SetResult(ResultState.Failure, $"In {contextName} code, expected {expectedExceptionName} but no exception was thrown"); } }
protected abstract TestResult HandleCompilerException(ExecutionContext context, MethodInfo methodInfo);
protected abstract Delegate CompileDelegate(ExecutionContext context, MethodInfo methodInfo, Type delegateType);
protected abstract void CompleteTest(ExecutionContext context);
private TestResult ExecuteMethod(ExecutionContext context) { Setup(); var methodInfo = _originalMethod.Method.MethodInfo; var arguments = GetArgumentsArray(_originalMethod); // Special case for Compiler Exceptions when IL can't be translated if (_expectCompilerException) { // We still need to transform arguments here in case there's a function pointer that we expect to fail compilation. var expectedExceptionResult = TryExpectedException( context, () => TransformArguments(_originalMethod.Method.MethodInfo, arguments, out _, out _), "Transforming arguments", type => true, "Any exception", false, false); if (expectedExceptionResult != TryExpectedExceptionResult.DidNotThrowException) { return(context.CurrentResult); } return(HandleCompilerException(context, methodInfo)); } object[] nativeArgs; Type[] nativeArgTypes; TransformArguments(_originalMethod.Method.MethodInfo, arguments, out nativeArgs, out nativeArgTypes); bool isInRegistry = false; Func <object, object[], object> nativeDelegateCaller; var delegateType = CreateNativeDelegateType(_originalMethod.Method.MethodInfo.ReturnType, nativeArgTypes, out isInRegistry, out nativeDelegateCaller); if (!isInRegistry) { Console.WriteLine($"Warning, the delegate for the method `{_originalMethod.Method}` has not been generated"); } var compiledFunction = CompileDelegate(context, methodInfo, delegateType); if (compiledFunction == null) { return(context.CurrentResult); } // Special case if we have an expected exception if (_expectedException != null) { if (TryExpectedException(context, () => _originalMethod.Method.Invoke(context.TestObject, arguments), ".NET", type => type == _expectedException, _expectedException.FullName, false) != TryExpectedExceptionResult.ThrewExpectedException) { return(context.CurrentResult); } if (TryExpectedException(context, () => nativeDelegateCaller(compiledFunction, nativeArgs), "Native", type => type == _expectedException, _expectedException.FullName, true) != TryExpectedExceptionResult.ThrewExpectedException) { return(context.CurrentResult); } } else { // We are forced to run native before managed, because on IL2CPP, if a parameter // is a ref, it will keep the same memory location for both managed and burst // while in .NET CLR we have a different behavior // The result is that on functions expecting the same input value through the ref // it won't be anymore true because the managed could have modified the value before // burst var resultNative = nativeDelegateCaller(compiledFunction, nativeArgs); var resultClr = _originalMethod.Method.Invoke(context.TestObject, arguments); var overrideResultOnMono = _originalMethod.Properties.Get("OverrideResultOnMono"); if (overrideResultOnMono != null) { Console.WriteLine($"Using OverrideResultOnMono: `{overrideResultOnMono}` instead of `{resultClr}` compare to burst `{resultNative}`"); resultClr = overrideResultOnMono; } // Use our own version (for handling correctly float precision) AssertHelper.AreEqual(resultClr, resultNative, GetULP()); // Validate deterministic outputs - Disabled for now //RunDeterminismValidation(_originalMethod, resultNative); // Allow to process native result ProcessNativeResult(_originalMethod, resultNative); context.CurrentResult.SetResult(ResultState.Success); } // Check that the method is actually in the registry Assert.True(isInRegistry, "The test method is not in the registry, recompile the project with the updated StaticDelegateRegistry.generated.cs"); // Make an attempt to clean up arguments (to reduce wasted native heap memory) DisposeObjects(arguments); DisposeObjects(nativeArgs); CompleteTest(context); return(context.CurrentResult); }
private TestResult ExecuteMethod(ExecutionContext context) { Setup(); var methodInfo = _originalMethod.Method.MethodInfo; // Special case for Compiler Expceptions when IL can't be translated if (_expectCompilerException) { return(HandleCompilerException(context, methodInfo)); } var arguments = GetArgumentsArray(_originalMethod); object[] nativeArgs; Type[] nativeArgTypes; TransformArguments(_originalMethod.Method.MethodInfo, arguments, out nativeArgs, out nativeArgTypes); bool isInRegistry = false; Func <object, object[], object> nativeDelegateCaller; var delegateType = CreateNativeDelegateType(_originalMethod.Method.MethodInfo.ReturnType, nativeArgTypes, out isInRegistry, out nativeDelegateCaller); if (!isInRegistry) { Console.WriteLine($"Warning, the delegate for the method `{_originalMethod.Method}` has not been generated"); } var compiledFunction = CompileDelegate(context, methodInfo, delegateType); if (compiledFunction == null) { return(context.CurrentResult); } // Special case if we have an expected exception if (_expectedException != null) { TryExpectedException(context, () => _originalMethod.Method.Invoke(context.TestObject, arguments), ".NET", type => type == _expectedException, _expectedException.FullName, false); if (context.CurrentResult.ResultState.Equals(ResultState.Success)) { TryExpectedException(context, () => nativeDelegateCaller(compiledFunction, nativeArgs), "Native", type => type == _expectedException, _expectedException.FullName, true); } } else { var resultNative = nativeDelegateCaller(compiledFunction, nativeArgs); var resultClr = _originalMethod.Method.Invoke(context.TestObject, arguments); var overrideResultOnMono = _originalMethod.Properties.Get("OverrideResultOnMono"); if (overrideResultOnMono != null) { Console.WriteLine($"Using OverrideResultOnMono: `{overrideResultOnMono}` instead of `{resultClr}` compare to burst `{resultNative}`"); resultClr = overrideResultOnMono; } // Use our own version (for handling correctly float precision) AssertHelper.AreEqual(resultClr, resultNative, GetULP()); // Allow to process native result ProcessNativeResult(_originalMethod, resultNative); context.CurrentResult.SetResult(ResultState.Success); } // Check that the method is actually in the registry Assert.True(isInRegistry, "The test method is not in the registry, recompile the project with the updated StaticDelegateRegistry.generated.cs"); // Make an attempt to clean up arguments (to reduce wasted native heap memory) DisposeObjects(arguments); DisposeObjects(nativeArgs); CompleteTest(context); return(context.CurrentResult); }
protected abstract unsafe Delegate CompileDelegate(ExecutionContext context, MethodInfo methodInfo, Type delegateType, byte *returnBox, out Type returnBoxType);
protected virtual void PostAssert(ExecutionContext context) { }
private unsafe TestResult ExecuteMethod(ExecutionContext context) { byte *returnBox = stackalloc byte[MaxReturnBoxSize]; Setup(); var methodInfo = _originalMethod.Method.MethodInfo; var arguments = GetArgumentsArray(_originalMethod); // Special case for Compiler Exceptions when IL can't be translated if (_expectCompilerException) { // We still need to transform arguments here in case there's a function pointer that we expect to fail compilation. var expectedExceptionResult = TryExpectedException( context, () => TransformArguments(_originalMethod.Method.MethodInfo, arguments, out _, out _, returnBox, out _), "Transforming arguments", type => true, "Any exception", false, false); if (expectedExceptionResult != TryExpectedExceptionResult.DidNotThrowException) { return(context.CurrentResult); } return(HandleCompilerException(context, methodInfo)); } object[] nativeArgs; Type[] nativeArgTypes; TransformArguments(_originalMethod.Method.MethodInfo, arguments, out nativeArgs, out nativeArgTypes, returnBox, out Type returnBoxType); bool isInRegistry = false; Func <object, object[], object> nativeDelegateCaller; var delegateType = CreateNativeDelegateType(_originalMethod.Method.MethodInfo.ReturnType, nativeArgTypes, out isInRegistry, out nativeDelegateCaller); if (!isInRegistry) { Console.WriteLine($"Warning, the delegate for the method `{_originalMethod.Method}` has not been generated"); } Delegate compiledFunction; try { compiledFunction = CompileDelegate(context, methodInfo, delegateType, returnBox, out _); } catch (Exception ex) when(_expectedException != null && ex.GetType() == _expectedException) { context.CurrentResult.SetResult(ResultState.Success); return(context.CurrentResult); } Assert.IsTrue(returnBoxType == null || Marshal.SizeOf(returnBoxType) <= MaxReturnBoxSize); if (compiledFunction == null) { return(context.CurrentResult); } // Check for expected compiler warnings (we'll check for compiler errors below, // but this is for tests that we expect to pass but still log a warning). if (!CheckExpectedDiagnostics(context, "Compiling code")) { return(context.CurrentResult); } if (_compileOnly) // If the test only wants to compile the code, bail now. { return(context.CurrentResult); } else if (_expectedException != null) // Special case if we have an expected exception { if (TryExpectedException(context, () => _originalMethod.Method.Invoke(context.TestObject, arguments), ".NET", type => type == _expectedException, _expectedException.FullName, false) != TryExpectedExceptionResult.ThrewExpectedException) { return(context.CurrentResult); } if (TryExpectedException(context, () => nativeDelegateCaller(compiledFunction, nativeArgs), "Native", type => type == _expectedException, _expectedException.FullName, true) != TryExpectedExceptionResult.ThrewExpectedException) { return(context.CurrentResult); } } else { object resultNative = null; // We are forced to run native before managed, because on IL2CPP, if a parameter // is a ref, it will keep the same memory location for both managed and burst // while in .NET CLR we have a different behavior // The result is that on functions expecting the same input value through the ref // it won't be anymore true because the managed could have modified the value before // burst // ------------------------------------------------------------------ // Run Native (Before) // ------------------------------------------------------------------ if (!RunManagedBeforeNative) { resultNative = nativeDelegateCaller(compiledFunction, nativeArgs); if (returnBoxType != null) { resultNative = Marshal.PtrToStructure((IntPtr)returnBox, returnBoxType); } } // ------------------------------------------------------------------ // Run Managed // ------------------------------------------------------------------ object resultClr; // This option skips running the managed version completely var overrideManagedResult = _originalMethod.Properties.Get("OverrideManagedResult"); if (overrideManagedResult != null) { Console.WriteLine($"Using OverrideManagedResult: `{overrideManagedResult}` to compare to burst `{resultNative}`, managed version not run"); resultClr = overrideManagedResult; } else { resultClr = _originalMethod.Method.Invoke(context.TestObject, arguments); if (returnBoxType != null) { resultClr = Marshal.PtrToStructure((IntPtr)returnBox, returnBoxType); } } var overrideResultOnMono = _originalMethod.Properties.Get("OverrideResultOnMono"); if (overrideResultOnMono != null) { Console.WriteLine($"Using OverrideResultOnMono: `{overrideResultOnMono}` instead of `{resultClr}` compare to burst `{resultNative}`"); resultClr = overrideResultOnMono; } // ------------------------------------------------------------------ // Run Native (After) // ------------------------------------------------------------------ if (RunManagedBeforeNative) { resultNative = nativeDelegateCaller(compiledFunction, nativeArgs); if (returnBoxType != null) { resultNative = Marshal.PtrToStructure((IntPtr)returnBox, returnBoxType); } } // Use our own version (for handling correctly float precision) AssertHelper.AreEqual(resultClr, resultNative, GetULP()); // Validate deterministic outputs - Disabled for now //RunDeterminismValidation(_originalMethod, resultNative); // Allow to process native result ProcessNativeResult(_originalMethod, resultNative); context.CurrentResult.SetResult(ResultState.Success); PostAssert(context); } // Check that the method is actually in the registry Assert.True(isInRegistry, "The test method is not in the registry, recompile the project with the updated StaticDelegateRegistry.generated.cs"); // Compile the method once again, this time for Arm CPU to check against gold asm images CompileDelegateForArm(methodInfo); // Make an attempt to clean up arguments (to reduce wasted native heap memory) DisposeObjects(arguments); DisposeObjects(nativeArgs); CompleteTest(context); return(context.CurrentResult); }