public string GetRunResultsAsXml(int maxConcurrentRunners) { if (!_configured) { throw new InvalidOperationException("You must call ConfigureRun first"); } //Keep a reference to standard out var stdOut = new TextWriterWrapper(Console.Out); var totalRuntime = new Stopwatch(); totalRuntime.Start(); var outputPath = _runnerSettings.OutputBasePath; if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } var shouldRunOther = _categoriesToRun.Count == 0 || _categoriesToRun.Contains("all"); //var categoryMessage = "Categories run will be: " + string.Join(", ", runnableCategories); //Debug.WriteLine(categoryMessage); //Console.WriteLine(categoryMessage); stdOut.WriteLine("Starting tests..."); if (maxConcurrentRunners > 0) { stdOut.WriteLine(" Running upto " + maxConcurrentRunners + " concurrently"); } stdOut.WriteLine("(ctrl-c and a few seconds to cancel, '{0}' means running, '{1}' means finished)", _progressDisplayBuilder.ArrayValueToRunningStatusChar(ProgressState.Running), _progressDisplayBuilder.ArrayValueToRunningStatusChar(ProgressState.Finished)); var testFixturesToRun = new List <string>(); if (shouldRunOther) { testFixturesToRun = new List <string>(_otherTestFixtures); } var runnableCategories = _categoriesToRun.Count > 0 ? _categories.Intersect(_categoriesToRun).ToList() : _categories; int totalToRun = runnableCategories.Count(); if (_runnerSettings.RunUncategorizedTestFixturesParallel) { totalToRun += testFixturesToRun.Count; } else if (shouldRunOther && _otherTestFixtures.Any()) { totalToRun += 1; } stdOut.WriteLine(); stdOut.WriteLine("Found {0} categories/fixtures to run", totalToRun); var testResults = new ConcurrentBag <RunStats>(); bool cancelled = false; try { var runningTests = new ProgressStats(totalToRun); int indicatorPos = 0; var buildingDisplay = new object(); var timer = new Timer(x => { if (Console.IsOutputRedirected) { return; } if (!Monitor.TryEnter(buildingDisplay)) { return; } try { int windowWidth = Console.WindowWidth; stdOut.Write("\r"); stdOut.Write(_progressDisplayBuilder.BuildProgressDisplay(windowWidth, runningTests, ref indicatorPos, _runnerSettings.DisplayFailureSymbolsInProgressDisplay)); } catch (Exception exception) { stdOut.Write("display error..."); throw new ApplicationException("Unable to properly build progress display.", exception); } finally { Monitor.Exit(buildingDisplay); } }, null, 0, 250); if (Console.IsOutputRedirected) { timer.Change(Timeout.Infinite, Timeout.Infinite); } //Setup ability to catch ctrl-c Console.CancelKeyPress += (sender, args) => { timer.Change(Timeout.Infinite, Timeout.Infinite); args.Cancel = true; _cancelTokenSource.Cancel(); //stdOut.WriteLine(); //stdOut.WriteLine("CANCEL KEY PUSHED"); //Stop any running ones... maybe don't do this until pressed twice? Process.GetProcesses() .Where(p => p.ProcessName == "nunit-console" || p.ProcessName == "nunit-agent") .Each(x => x.Kill()); }; var startOrderInt = 0; var token = _cancelTokenSource.Token; var buildSortedAllActions = BuildSortedAllActions(testFixturesToRun, runnableCategories) .ToArray(); RunActionsOnThreads(maxConcurrentRunners, buildSortedAllActions, token, stdOut, runningTests, startOrderInt, totalRuntime, testResults); timer.Change(0, Timeout.Infinite); //Tacky way to fix line printing problem Thread.Sleep(100); stdOut.WriteLine(); } catch (OperationCanceledException) { cancelled = true; stdOut.WriteLine(); stdOut.WriteLine("== Cancelled =="); } totalRuntime.Stop(); stdOut.WriteLine("= Total runtime: " + TimeSpanFormat(totalRuntime.Elapsed)); stdOut.WriteLine("Finished with tests, merging results"); var SkippedTests = _categories.Except(testResults.Select(a => a.Name)).ToList(); _resultsStatsWriter.OutputRunStats(totalRuntime.Elapsed, testResults, SkippedTests); var outputResultsXmlPath = _runnerSettings.ResultsXmlFilepath; var outputResultsReportPath = _runnerSettings.ResultsHtmlReportFilepath; var xmlOutput = _resultsWriter.MergeResultsProcess(outputPath, outputResultsXmlPath, outputResultsReportPath); if (cancelled) { Environment.ExitCode = -9; } if (testResults.Any(x => x.ExitCode != 0)) { stdOut.WriteLine("ERROR: Test process exited with error!"); Environment.ExitCode = -1; } //Do we really need to return this? if so we should have the writing happen elsewhere... return(xmlOutput); }