From 179c864e4de5129bdd7cdde894827921a7a3099b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Fri, 16 Apr 2021 10:44:44 +0200 Subject: [PATCH] Handle possible null ref in error handler (#819) I have a dump that throws access violation exception in native code and the test run fails with: `An exception occurred while invoking executor 'executor://mstestadapter/v2': Object reference not set to an instance of an object.` I looked through the code and the only way I can get this result is when ex would be null in the handler. Otherwise it fails with different error, either from the asserts in the `StackTraceHelper` methods or in upstream error handlers. --- .../Execution/TestMethodInfo.cs | 18 +++++++++++++++++- .../Execution/TestMethodRunner.cs | 6 +++--- .../Resources/Resource.Designer.cs | 13 +++++++++++-- .../MSTest.CoreAdapter/Resources/Resource.resx | 5 ++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodInfo.cs b/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodInfo.cs index c508f35efe..949fe5acd7 100644 --- a/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodInfo.cs +++ b/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodInfo.cs @@ -717,11 +717,27 @@ private object CreateTestClassInstance(TestResult result) } catch (Exception ex) { + if (ex == null) + { + // It seems that ex can be null in some rare cases when initialization fails in native code. + // Get our own exception with a stack trace to satisfy GetStackTraceInformation. + try + { + throw new InvalidOperationException(Resource.UTA_UserCodeThrewNullValueException); + } + catch (Exception exception) + { + ex = exception; + } + } + // In most cases, exception will be TargetInvocationException with real exception wrapped - // in the InnerException; or user code throws an exception + // in the InnerException; or user code throws an exception. + // It also seems that in rare cases the ex can be null. var actualException = ex.InnerException ?? ex; var exceptionMessage = StackTraceHelper.GetExceptionMessage(actualException); var stackTraceInfo = StackTraceHelper.GetStackTraceInformation(actualException); + var errorMessage = string.Format( CultureInfo.CurrentCulture, Resource.UTA_InstanceCreationError, diff --git a/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodRunner.cs b/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodRunner.cs index 63bc78323c..b05ce4abef 100644 --- a/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodRunner.cs +++ b/src/Adapter/MSTest.CoreAdapter/Execution/TestMethodRunner.cs @@ -270,7 +270,7 @@ internal UnitTestResult[] RunTestMethod() { testResults = new[] { - new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex.Message), ex) } + new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex?.Message, ex?.StackTrace), ex) } }; } @@ -324,7 +324,7 @@ internal UnitTestResult[] RunTestMethod() { testResults = new[] { - new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex.Message), ex) } + new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex?.Message, ex?.StackTrace), ex) } }; } @@ -346,7 +346,7 @@ internal UnitTestResult[] RunTestMethod() } catch (Exception ex) { - results.Add(new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex.Message), ex) }); + results.Add(new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex?.Message, ex?.StackTrace), ex) }); } } } diff --git a/src/Adapter/MSTest.CoreAdapter/Resources/Resource.Designer.cs b/src/Adapter/MSTest.CoreAdapter/Resources/Resource.Designer.cs index 4724a2c692..6ee7b4f840 100644 --- a/src/Adapter/MSTest.CoreAdapter/Resources/Resource.Designer.cs +++ b/src/Adapter/MSTest.CoreAdapter/Resources/Resource.Designer.cs @@ -20,7 +20,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resource { @@ -495,7 +495,7 @@ internal static string UTA_ErrorTestPropertyNullOrEmpty { } /// - /// Looks up a localized string similar to Exception thrown while executing test. If using extension of TestMethodAttribute then please contact vendor. Error message: {0}. + /// Looks up a localized string similar to Exception thrown while executing test. If using extension of TestMethodAttribute then please contact vendor. Error message: {0}, Stack trace: {1}. /// internal static string UTA_ExecuteThrewException { get { @@ -640,6 +640,15 @@ internal static string UTA_TypeLoadError { } } + /// + /// Looks up a localized string similar to The called code threw an exception that was caught, but the exception value was null. + /// + internal static string UTA_UserCodeThrewNullValueException { + get { + return ResourceManager.GetString("UTA_UserCodeThrewNullValueException", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} For UWP projects, if you are using UI objects in test consider using [UITestMethod] attribute instead of [TestMethod] to execute test in UI thread.. /// diff --git a/src/Adapter/MSTest.CoreAdapter/Resources/Resource.resx b/src/Adapter/MSTest.CoreAdapter/Resources/Resource.resx index 474f4ed5d0..cf72e6072d 100644 --- a/src/Adapter/MSTest.CoreAdapter/Resources/Resource.resx +++ b/src/Adapter/MSTest.CoreAdapter/Resources/Resource.resx @@ -209,7 +209,7 @@ Error: {1} Class Initialization method {0}.{1} threw exception. {2}: {3}. - Exception thrown while executing test. If using extension of TestMethodAttribute then please contact vendor. Error message: {0} + Exception thrown while executing test. If using extension of TestMethodAttribute then please contact vendor. Error message: {0}, Stack trace: {1} Error in executing test. No result returned by extension. If using extension of TestMethodAttribute then please contact vendor. @@ -320,4 +320,7 @@ Error: {1} Test '{0}' execution has been aborted. + + The called code threw an exception that was caught, but the exception value was null + \ No newline at end of file