internal UnitTestResult RunUnitTest (UnitTest test, string suiteName, string pathName, string testName, TestContext testContext) { var runnerExe = GetCustomConsoleRunnerCommand (); if (runnerExe != null) return RunWithConsoleRunner (runnerExe, test, suiteName, pathName, testName, testContext); var console = testContext.ExecutionContext.ConsoleFactory.CreateConsole (); ExternalTestRunner runner = new ExternalTestRunner (); runner.Connect (NUnitVersion, testContext.ExecutionContext.ExecutionHandler, console).Wait (); LocalTestMonitor localMonitor = new LocalTestMonitor (testContext, test, suiteName, testName != null); string[] filter = null; if (test != null) { if (test is UnitTestGroup && NUnitVersion == NUnitVersion.NUnit2) { filter = CollectTests ((UnitTestGroup)test); } else if (test.TestId != null) { filter = new string [] { test.TestId }; } } RunData rd = new RunData (); rd.Runner = runner; rd.Test = this; rd.LocalMonitor = localMonitor; var cancelReg = testContext.Monitor.CancellationToken.Register (rd.Cancel); UnitTestResult result; var crashLogFile = Path.GetTempFileName (); try { if (string.IsNullOrEmpty (AssemblyPath)) { string msg = GettextCatalog.GetString ("Could not get a valid path to the assembly. There may be a conflict in the project configurations."); throw new Exception (msg); } string testRunnerAssembly, testRunnerType; GetCustomTestRunner (out testRunnerAssembly, out testRunnerType); testContext.Monitor.CancellationToken.ThrowIfCancellationRequested (); result = runner.Run (localMonitor, filter, AssemblyPath, "", new List<string> (SupportAssemblies), testRunnerType, testRunnerAssembly, crashLogFile).Result; if (testName != null) result = localMonitor.SingleTestResult; ReportCrash (testContext, crashLogFile); } catch (Exception ex) { if (ReportCrash (testContext, crashLogFile)) { result = UnitTestResult.CreateFailure (GettextCatalog.GetString ("Unhandled exception"), null); } else if (!localMonitor.Canceled) { LoggingService.LogError (ex.ToString ()); if (localMonitor.RunningTest != null) { RuntimeErrorCleanup (testContext, localMonitor.RunningTest, ex); } else { testContext.Monitor.ReportRuntimeError (null, ex); throw; } result = UnitTestResult.CreateFailure (ex); } else { result = UnitTestResult.CreateFailure (GettextCatalog.GetString ("Canceled"), null); } } finally { if (console != null) console.Dispose (); cancelReg.Dispose (); runner.Dispose (); File.Delete (crashLogFile); } return result; }
UnitTestResult RunWithConsoleRunner (ProcessExecutionCommand cmd, UnitTest test, string suiteName, string pathName, string testName, TestContext testContext) { var outFile = Path.GetTempFileName (); var xmlOutputConsole = new LocalConsole (); var appDebugOutputConsole = testContext.ExecutionContext.ConsoleFactory.CreateConsole (); OperationConsole cons; if (appDebugOutputConsole != null) { cons = new MultipleOperationConsoles (appDebugOutputConsole, xmlOutputConsole); } else { cons = xmlOutputConsole; } try { MonoDevelop.UnitTesting.NUnit.External.TcpTestListener tcpListener = null; LocalTestMonitor localMonitor = new LocalTestMonitor (testContext, test, suiteName, testName != null); if (!string.IsNullOrEmpty (cmd.Arguments)) cmd.Arguments += " "; cmd.Arguments += "\"-xml=" + outFile + "\" " + AssemblyPath; bool automaticUpdates = cmd.Command != null && (cmd.Command.Contains ("GuiUnit") || (cmd.Command.Contains ("mdtool.exe") && cmd.Arguments.Contains ("run-md-tests"))); if (!string.IsNullOrEmpty(pathName)) cmd.Arguments += " -run=\"" + test.TestId.Replace("\"", "\\\"") + "\""; if (automaticUpdates) { tcpListener = new MonoDevelop.UnitTesting.NUnit.External.TcpTestListener (localMonitor, suiteName); cmd.Arguments += " -port=" + tcpListener.Port; } // Note that we always dispose the tcp listener as we don't want it listening // forever if the test runner does not try to connect to it using (tcpListener) { var handler = testContext.ExecutionContext.ExecutionHandler; if (handler == null) handler = Runtime.ProcessService.DefaultExecutionHandler; var p = handler.Execute (cmd, cons); using (testContext.Monitor.CancellationToken.Register (p.Cancel)) p.Task.Wait (); if (new FileInfo (outFile).Length == 0) throw new Exception ("Command failed"); } // mdtool.exe does not necessarily guarantee we get automatic updates. It just guarantees // that if guiunit is being used then it will give us updates. If you have a regular test // assembly compiled against nunit.framework.dll if (automaticUpdates && tcpListener.HasReceivedConnection) { if (testName != null) return localMonitor.SingleTestResult; return test.GetLastResult (); } XDocument doc = XDocument.Load (outFile); if (doc.Root != null) { var root = doc.Root.Elements ("test-suite").FirstOrDefault (); if (root != null) { xmlOutputConsole.SetDone (); var ot = xmlOutputConsole.OutReader.ReadToEnd (); var et = xmlOutputConsole.ErrorReader.ReadToEnd (); testContext.Monitor.WriteGlobalLog (ot); if (!string.IsNullOrEmpty (et)) { testContext.Monitor.WriteGlobalLog ("ERROR:\n"); testContext.Monitor.WriteGlobalLog (et); } bool macunitStyle = doc.Root.Element ("environment") != null && doc.Root.Element ("environment").Attribute ("macunit-version") != null; var result = ReportXmlResult (localMonitor, root, "", macunitStyle); if (testName != null) result = localMonitor.SingleTestResult; return result; } } throw new Exception ("Test results could not be parsed."); } catch (Exception ex) { xmlOutputConsole.SetDone (); var ot = xmlOutputConsole.OutReader.ReadToEnd (); var et = xmlOutputConsole.ErrorReader.ReadToEnd (); testContext.Monitor.WriteGlobalLog (ot); if (!string.IsNullOrEmpty (et)) { testContext.Monitor.WriteGlobalLog ("ERROR:\n"); testContext.Monitor.WriteGlobalLog (et); } testContext.Monitor.ReportRuntimeError ("Test execution failed.\n" + ot + "\n" + et, ex); return UnitTestResult.CreateIgnored ("Test execution failed"); } finally { File.Delete (outFile); cons.Dispose (); } }