diff options
author | Lluis Sanchez <lluis@xamarin.com> | 2015-04-30 18:35:02 +0300 |
---|---|---|
committer | Lluis Sanchez <lluis@xamarin.com> | 2015-04-30 18:35:17 +0300 |
commit | a50cfd4e543d449a0d8fd4aeeae2e0c9acba2505 (patch) | |
tree | 844af0d90215f444cb3be863d58aa93944a3e959 /main/src/addins/NUnit | |
parent | 296ea76e6c7327f9f89a5f66f8d93555f657725f (diff) |
[NUnit] Properly report unhandled exceptions
Fixes bug 29457 - Uncaught exceptions from test can bring down the test runner.
Unhandled exceptions still bring down the test runner, but at least they
are now properly reported.
Diffstat (limited to 'main/src/addins/NUnit')
-rw-r--r-- | main/src/addins/NUnit/Gui/TestResultsPad.cs | 15 | ||||
-rw-r--r-- | main/src/addins/NUnit/Services/ExternalTestRunner.cs | 59 | ||||
-rw-r--r-- | main/src/addins/NUnit/Services/NUnitAssemblyTestSuite.cs | 25 |
3 files changed, 88 insertions, 11 deletions
diff --git a/main/src/addins/NUnit/Gui/TestResultsPad.cs b/main/src/addins/NUnit/Gui/TestResultsPad.cs index e7ebaf40f4..638ed50d5f 100644 --- a/main/src/addins/NUnit/Gui/TestResultsPad.cs +++ b/main/src/addins/NUnit/Gui/TestResultsPad.cs @@ -44,6 +44,7 @@ using System.Text.RegularExpressions; using MonoDevelop.Components; using MonoDevelop.Ide.Commands; using MonoDevelop.Ide.Fonts; +using MonoDevelop.NUnit.External; namespace MonoDevelop.NUnit { @@ -369,13 +370,19 @@ namespace MonoDevelop.NUnit public void AddErrorMessage () { - string msg = GettextCatalog.GetString ("Internal error"); - if (errorMessage != null) - msg += ": " + Escape (errorMessage); + string msg; + if (error is RemoteUnhandledException) + msg = Escape (errorMessage); + else { + msg = GettextCatalog.GetString ("Internal error"); + if (errorMessage != null) + msg += ": " + Escape (errorMessage); + } var stock = ImageService.GetIcon (Ide.Gui.Stock.Error, Gtk.IconSize.Menu); TreeIter testRow = failuresStore.AppendValues (stock, msg, null, null, 0); - failuresStore.AppendValues (testRow, null, Escape (error.GetType ().Name + ": " + error.Message), null); + string name = error is RemoteUnhandledException ? ((RemoteUnhandledException)error).RemoteExceptionName : error.GetType ().Name; + failuresStore.AppendValues (testRow, null, Escape (name + ": " + error.Message), null); TreeIter row = failuresStore.AppendValues (testRow, null, GettextCatalog.GetString ("Stack Trace"), null, null, 0); AddStackTrace (row, error.StackTrace, null); } diff --git a/main/src/addins/NUnit/Services/ExternalTestRunner.cs b/main/src/addins/NUnit/Services/ExternalTestRunner.cs index 480c845aee..ea9f51d972 100644 --- a/main/src/addins/NUnit/Services/ExternalTestRunner.cs +++ b/main/src/addins/NUnit/Services/ExternalTestRunner.cs @@ -75,13 +75,24 @@ namespace MonoDevelop.NUnit.External Assembly.LoadFrom (asm); } - public UnitTestResult Run (IRemoteEventListener listener, ITestFilter filter, string path, string suiteName, List<string> supportAssemblies, string testRunnerType, string testRunnerAssembly) + public UnitTestResult Run (IRemoteEventListener listener, ITestFilter filter, string path, string suiteName, List<string> supportAssemblies, string testRunnerType, string testRunnerAssembly, string crashLogFile) { NUnitTestRunner runner = GetRunner (path); EventListenerWrapper listenerWrapper = listener != null ? new EventListenerWrapper (listener) : null; - TestResult res = runner.Run (listenerWrapper, filter, path, suiteName, supportAssemblies, testRunnerType, testRunnerAssembly); - return listenerWrapper.GetLocalTestResult (res); + UnhandledExceptionEventHandler exceptionHandler = (object sender, UnhandledExceptionEventArgs e) => { + + var ex = new RemoteUnhandledException ((Exception) e.ExceptionObject); + File.WriteAllText (crashLogFile, ex.Serialize ()); + }; + + AppDomain.CurrentDomain.UnhandledException += exceptionHandler; + try { + TestResult res = runner.Run (listenerWrapper, filter, path, suiteName, supportAssemblies, testRunnerType, testRunnerAssembly); + return listenerWrapper.GetLocalTestResult (res); + } finally { + AppDomain.CurrentDomain.UnhandledException -= exceptionHandler; + } } public NunitTestInfo GetTestInfo (string path, List<string> supportAssemblies) @@ -366,7 +377,47 @@ namespace MonoDevelop.NUnit.External { return null; } - } + + /// <summary> + /// Exception class that can be serialized + /// </summary> + class RemoteUnhandledException: Exception + { + string stack; + + public RemoteUnhandledException (string exceptionName, string message, string stack): base (message) + { + RemoteExceptionName = exceptionName; + this.stack = stack; + } + + public RemoteUnhandledException (Exception ex): base (ex.Message) + { + RemoteExceptionName = ex.GetType().Name; + this.stack = ex.StackTrace; + } + + public string Serialize () + { + return RemoteExceptionName + "\n" + Message.Replace ('\r',' ').Replace ('\n',' ') + "\n" + StackTrace; + } + + public static RemoteUnhandledException Parse (string s) + { + int i = s.IndexOf ('\n'); + string name = s.Substring (0, i++); + int i2 = s.IndexOf ('\n', i); + return new RemoteUnhandledException (name, s.Substring (i, i2 - i), s.Substring (i2 + 1)); + } + + public string RemoteExceptionName { get; set; } + + public override string StackTrace { + get { + return stack; + } + } + } } diff --git a/main/src/addins/NUnit/Services/NUnitAssemblyTestSuite.cs b/main/src/addins/NUnit/Services/NUnitAssemblyTestSuite.cs index 3e55a824be..3a50455d73 100644 --- a/main/src/addins/NUnit/Services/NUnitAssemblyTestSuite.cs +++ b/main/src/addins/NUnit/Services/NUnitAssemblyTestSuite.cs @@ -414,6 +414,7 @@ namespace MonoDevelop.NUnit testContext.Monitor.CancelRequested += new TestHandler (rd.Cancel); UnitTestResult result; + var crashLogFile = Path.GetTempFileName (); try { if (string.IsNullOrEmpty (AssemblyPath)) { @@ -425,11 +426,17 @@ namespace MonoDevelop.NUnit string testRunnerAssembly, testRunnerType; GetCustomTestRunner (out testRunnerAssembly, out testRunnerType); - result = runner.Run (localMonitor, filter, AssemblyPath, "", new List<string> (SupportAssemblies), testRunnerType, testRunnerAssembly); + result = runner.Run (localMonitor, filter, AssemblyPath, "", new List<string> (SupportAssemblies), testRunnerType, testRunnerAssembly, crashLogFile); if (testName != null) result = localMonitor.SingleTestResult; + + ReportCrash (testContext, crashLogFile); + } catch (Exception ex) { - if (!localMonitor.Canceled) { + if (ReportCrash (testContext, crashLogFile)) { + result = UnitTestResult.CreateFailure (GettextCatalog.GetString ("Undhandled exception"), null); + } + else if (!localMonitor.Canceled) { LoggingService.LogError (ex.ToString ()); if (localMonitor.RunningTest != null) { RuntimeErrorCleanup (testContext, localMonitor.RunningTest, ex); @@ -442,6 +449,7 @@ namespace MonoDevelop.NUnit result = UnitTestResult.CreateFailure (GettextCatalog.GetString ("Canceled"), null); } } finally { + File.Delete (crashLogFile); testContext.Monitor.CancelRequested -= new TestHandler (rd.Cancel); runner.Dispose (); System.Runtime.Remoting.RemotingServices.Disconnect (localMonitor); @@ -449,7 +457,18 @@ namespace MonoDevelop.NUnit return result; } - + + bool ReportCrash (TestContext testContext, string crashLogFile) + { + var crash = File.ReadAllText (crashLogFile); + if (crash.Length == 0) + return false; + + var ex = RemoteUnhandledException.Parse (crash); + testContext.Monitor.ReportRuntimeError (GettextCatalog.GetString ("Unhandled exception"), ex); + return true; + } + void RuntimeErrorCleanup (TestContext testContext, UnitTest t, Exception ex) { UnitTestResult result = UnitTestResult.CreateFailure (ex); |