Beispiel #1
0
 internal void WriteRoundBox(ColorTextBuilder builder, string str, int leftPadding = 0, Color?color = null)
 {
     builder.AppendLine($"{UTF8Constants.RoundBoxTopLeft}{DisplayUtil.Pad(str.Length + 4, UTF8Constants.RoundBoxHorizontal)}{UTF8Constants.RoundBoxTopRight}{DisplayUtil.Pad(leftPadding)}", color ?? _colorScheme.Highlight);
     builder.AppendLine($"{UTF8Constants.RoundBoxVertical}  {str}  {UTF8Constants.RoundBoxVertical}{DisplayUtil.Pad(leftPadding)}", color ?? _colorScheme.Highlight);
     builder.AppendLine($"{UTF8Constants.RoundBoxBottomLeft}{DisplayUtil.Pad(str.Length + 4, UTF8Constants.RoundBoxHorizontal)}{UTF8Constants.RoundBoxBottomRight}{DisplayUtil.Pad(leftPadding)}", color ?? _colorScheme.Highlight);
 }
        public override ColorTextBuilder Write(object parameters = null)
        {
            var allReports    = (IEnumerable <DataEvent>)parameters;
            var builder       = new ColorTextBuilder();
            var lineSeparator = new string(UTF8Constants.HorizontalLine, !_console.IsOutputRedirected ? (Console.WindowWidth - 1) : DefaultBorderWidth);

            var showErrors      = _configuration.GenerateReportType.HasFlag(GenerateReportType.Errors);
            var showStackTraces = _configuration.GenerateReportType.HasFlag(GenerateReportType.StackTraces);
            var showTestOutput  = _configuration.GenerateReportType.HasFlag(GenerateReportType.TestOutput);

            if (showErrors || showStackTraces || showTestOutput)
            {
                var isPassed = allReports
                               .SelectMany(x => x.Report.TestReports)
                               .Count(x => x.TestStatus == TestStatus.Fail) == 0;

                if (!isPassed)
                {
                    WriteRoundBox(builder, "FAILED TESTS", 0, _colorScheme.Error);
                }

                var testIndex    = 0;
                var groupedByRun = allReports
                                   .GroupBy(x => x.RunNumber);
                var failedTestCases = groupedByRun
                                      .ToDictionary(x => x.Key, value => value.SelectMany(y => y.Report.TestReports.Where(z => z.TestStatus == TestStatus.Fail)));
                var lineSeparatorColor = _colorScheme.RaisedBackground;
                foreach (var testGroup in failedTestCases)
                {
                    var runNumber = testGroup.Key;
                    foreach (var test in testGroup.Value)
                    {
                        testIndex++;

                        // write the failed test header
                        if (!_console.IsOutputRedirected)
                        {
                            builder.AppendLine($"{DisplayUtil.Pad(Console.WindowWidth - 1)}", _colorScheme.Error, _colorScheme.DarkError);
                        }
                        var testIndexStr = $"#{testIndex}) ";
                        if (failedTestCases.Count() > 1)
                        {
                            testIndexStr = $"#{runNumber}-{testIndex}) ";
                        }
                        builder.Append(testIndexStr, _colorScheme.DarkError);

                        // write the test name only
                        builder.Append(test.TestName, _colorScheme.Error);
                        if (!_console.IsOutputRedirected)
                        {
                            builder.AppendLine($"{DisplayUtil.Pad(Console.WindowWidth - testIndexStr.Length - test.TestName.Length - 1)}", _colorScheme.Error);
                        }
                        else
                        {
                            builder.AppendLine();
                        }

                        // write the test path + name (for Stack Trace Explorer)
                        var fullName = $"{DisplayUtil.Pad(testIndexStr.Length)}{test.FullName}";
                        builder.Append(fullName, _colorScheme.DarkDuration);
                        if (!_console.IsOutputRedirected)
                        {
                            builder.AppendLine($"{DisplayUtil.Pad(Console.WindowWidth - fullName.Length - 1)}", _colorScheme.Error);
                        }
                        else
                        {
                            builder.AppendLine();
                        }

                        var runtimeVersion = $"{DisplayUtil.Pad(testIndexStr.Length)}{test.RuntimeVersion}";
                        builder.Append($"{runtimeVersion}", _colorScheme.DarkDefault);
                        if (!_console.IsOutputRedirected)
                        {
                            builder.AppendLine($"{DisplayUtil.Pad(Console.WindowWidth - runtimeVersion.Length)}", _colorScheme.Error);
                        }
                        else
                        {
                            builder.AppendLine();
                        }

                        builder.Append($"  Duration ", _colorScheme.DarkDefault);
                        builder.AppendLine($"{test.Duration.ToElapsedTime()}", _colorScheme.Duration);

                        if (showErrors && !string.IsNullOrEmpty(test.ErrorMessage))
                        {
                            builder.AppendLine($"  Error Output ", _colorScheme.Bright);
                            builder.AppendLine(lineSeparator, lineSeparatorColor);
                            if (!_configuration.DontPrettify)
                            {
                                var errorMessage = ErrorEncoding.Format(test.ErrorMessage, _colorScheme);
                                builder.Append(errorMessage);
                            }
                            else
                            {
                                builder.Append(test.ErrorMessage);
                            }
                            builder.AppendLine();
                            builder.AppendLine(lineSeparator, lineSeparatorColor);
                        }
                        if (showStackTraces && !string.IsNullOrEmpty(test.StackTrace))
                        {
                            builder.AppendLine($"  Stack Trace:", _colorScheme.Bright);
                            builder.AppendLine(lineSeparator, lineSeparatorColor);
                            if (!_configuration.DontPrettify)
                            {
                                builder.Append(StackTracePrettify.Format(test.StackTrace, _colorScheme));
                            }
                            else
                            {
                                builder.Append(test.StackTrace);
                            }
                            builder.AppendLine();
                            builder.AppendLine(lineSeparator, lineSeparatorColor);
                        }
                        if (showTestOutput && !string.IsNullOrEmpty(test.TestOutput))
                        {
                            builder.AppendLine($"  Test Output: ", _colorScheme.Bright);
                            builder.AppendLine(lineSeparator, lineSeparatorColor);
                            if (!_configuration.DontPrettify)
                            {
                                builder.Append(ErrorEncoding.Format(test.TestOutput, _colorScheme));
                            }
                            else
                            {
                                builder.Append(test.TestOutput);
                            }
                            builder.AppendLine();
                            builder.AppendLine(lineSeparator, lineSeparatorColor);
                        }
                        builder.AppendLine(Environment.NewLine);
                    }
                }
            }
            return(builder);
        }
        public void Draw(ViewContext context, long ticks)
        {
            if (_startTicks == 0)
            {
                _startTicks = ticks;
                context.Console.Clear();
            }

            if (!context.Console.IsOutputRedirected)
            {
                // if the user has scrolled the page don't perform any drawing
                if (Console.WindowTop > 0)
                {
                    return;
                }

                Console.CursorVisible = false;
                _performFullDraw      = (_startTicks - ticks) % DefaultTickWait == 0;
                var windowWidth  = Console.WindowWidth;
                var windowHeight = Console.WindowHeight;
                if (windowHeight != _previousWindowHeight)
                {
                    _performFullDraw = true;
                }
                _previousWindowHeight = windowHeight;

                if (_performFullDraw)
                {
                    ClearScreen(context);
                }

                var lineSeparator = DisplayUtil.Pad(Console.WindowWidth - 1, UTF8Constants.HorizontalLine);
                var yPos          = 0;
                // figure out how many tests we can fit on screen
                var maxActiveTestsToDisplay = Console.WindowHeight - yPos - 2;

                var totalActiveTests        = context.ActiveTests.Count(x => !x.IsQueuedForRemoval);
                var totalActiveTestFixtures = context.ActiveTestFixtures.Count(x => !x.IsQueuedForRemoval);
                var totalActiveAssemblies   = context.ActiveAssemblies.Count(x => !x.IsQueuedForRemoval);
                if (totalActiveTests > 0 && totalActiveTestFixtures == 0)
                {
                    // fix for older versions of nUnit that don't send the start test fixture event
                    // it will still be incorrect, as it will treat parents of tests with multiple cases as a testfixture
                    var parentIds = context.ActiveTests
                                    .Where(x => !x.IsQueuedForRemoval && !string.IsNullOrEmpty(x.Event.ParentId))
                                    .Select(x => x.Event.ParentId)
                                    .Distinct();
                    totalActiveTestFixtures = context.ActiveTestSuites.Count(x => !x.IsQueuedForRemoval && parentIds.Contains(x.Event.Id));
                }
                var totalPasses         = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Pass);
                var totalFails          = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Fail);
                var totalIgnored        = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Skipped);
                var totalTestsProcessed = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest);
                WriteHeader(context);

                // write the summary of all test state
                context.Console.WriteAt(ColorTextBuilder.Create
                                        .Append("Tests state: ", context.ColorScheme.Bright)
                                        .Append($"Tests=", context.ColorScheme.Default)
                                        .Append($"{totalActiveTests} ", context.ColorScheme.Highlight)
                                        .Append($"Fixtures=", context.ColorScheme.Default)
                                        .Append($"{totalActiveTestFixtures} ", context.ColorScheme.Highlight)
                                        .Append($"Assemblies=", context.ColorScheme.Default)
                                        .Append($"{totalActiveAssemblies} ", context.ColorScheme.Highlight)
                                        .Append($"Pass="******"{totalPasses} ", context.ColorScheme.Success)
                                        .AppendIf(totalPasses == 0, $"{totalPasses} ", context.ColorScheme.Default)
                                        .Append($"Fail=", context.ColorScheme.Default)
                                        .AppendIf(totalFails > 0, $"{totalFails} ", context.ColorScheme.DarkError)
                                        .AppendIf(totalFails == 0, $"{totalFails} ", context.ColorScheme.Default)
                                        .AppendIf(!context.Console.IsOutputRedirected, $"Ignored=", context.ColorScheme.Default)
                                        .AppendIf(!context.Console.IsOutputRedirected, $"{totalIgnored} ", context.ColorScheme.DarkDefault)
                                        .Append($"Total=", context.ColorScheme.Default)
                                        .Append($"{totalTestsProcessed} ", context.ColorScheme.DarkDefault)
                                        .AppendIf(context.TotalTestsQueued > 0, $"of ", context.ColorScheme.Default)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{context.TotalTestsQueued} ", context.ColorScheme.DarkDefault)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{UTF8Constants.LeftBracket}", context.ColorScheme.Bright)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{((totalTestsProcessed / (double)context.TotalTestsQueued) * 100.0):F0}%", context.ColorScheme.DarkDuration)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{UTF8Constants.RightBracket}", context.ColorScheme.Bright)
                                        //.Append(context.Client.IsWaitingForConnection ? $"{UTF8Constants.LeftBracket}waiting{UTF8Constants.RightBracket}" : "", context.ColorScheme.DarkDuration)
                                        .AppendIf(!context.Console.IsOutputRedirected, (length) => DisplayUtil.Pad(windowWidth - length)),
                                        0,
                                        yPos + 1,
                                        DirectOutputMode.Static);

                // don't display this as often as its expensive to write
                if (_performFullDraw)
                {
                    context.Console.SetCursorPosition(0, 2);
                    var allCompletedAssemblies = context.EventLog
                                                 .Where(x => x.Event.Event == EventNames.EndAssembly)
                                                 .GroupBy(x => x.Event.TestSuite)
                                                 .Select(x => x.FirstOrDefault())
                                                 .OrderByDescending(x => x.Event.Duration);
                    var allPendingAssemblies = context.EventLog
                                               .Where(x => x.Event.Event == EventNames.StartAssembly && !allCompletedAssemblies.Select(y => y.Event.TestSuite).Contains(x.Event.TestSuite))
                                               .GroupBy(x => x.Event.TestSuite)
                                               .Select(x => x.FirstOrDefault())
                                               .OrderByDescending(x => x.DateAdded);
                    var completedAssemblies        = allCompletedAssemblies.Take(20);
                    var pendingAssemblies          = allPendingAssemblies.Take(20);
                    var completedAssembliesBuilder = new ColorTextBuilder();
                    completedAssembliesBuilder.Append($"Completed Assemblies ", context.ColorScheme.Bright)
                    .Append("[").Append($"{allCompletedAssemblies.Count()}", context.ColorScheme.Duration).Append("]")
                    .AppendLine();

                    if (completedAssemblies.Any())
                    {
                        foreach (var assembly in completedAssemblies)
                        {
                            var entryOutput    = new ColorTextBuilder();
                            var completionTime = assembly.DateAdded;
                            var duration       = DateTime.Now.Subtract(assembly.Event.StartTime);
                            if (assembly.Event.EndTime != DateTime.MinValue)
                            {
                                duration = assembly.Event.Duration;
                            }
                            var prettyTestName = DisplayUtil.GetPrettyTestName(assembly.Event.TestSuite, context.ColorScheme.DarkDefault, context.ColorScheme.Default, context.ColorScheme.DarkDefault, context.MaxTestCaseArgumentLength);
                            // print out this test name and duration
                            entryOutput
                            .Append($"[{completionTime.ToString(TimeFormat)}] ", context.ColorScheme.DarkDuration)
                            .Append(prettyTestName)
                            .Append($" {duration.ToTotalElapsedTime()}", context.ColorScheme.Duration);

                            completedAssembliesBuilder.AppendLine(entryOutput);
                        }
                    }

                    var activeAssembliesBuilder = new ColorTextBuilder();
                    activeAssembliesBuilder.Append($"Running Assemblies", context.ColorScheme.Bright)
                    .Append("[").Append($"{allPendingAssemblies.Count()}", context.ColorScheme.Duration).Append("]")
                    .AppendLine();
                    if (pendingAssemblies.Any())
                    {
                        foreach (var assembly in pendingAssemblies)
                        {
                            var entryOutput    = new ColorTextBuilder();
                            var completionTime = assembly.DateAdded;
                            var duration       = DateTime.Now.Subtract(assembly.Event.StartTime);
                            if (assembly.Event.EndTime != DateTime.MinValue)
                            {
                                duration = assembly.Event.Duration;
                            }
                            var prettyTestName = DisplayUtil.GetPrettyTestName(assembly.Event.TestSuite, context.ColorScheme.DarkDefault, context.ColorScheme.Default, context.ColorScheme.DarkDefault, context.MaxTestCaseArgumentLength);
                            // print out this test name and duration
                            entryOutput
                            .Append(prettyTestName)
                            .Append($" {duration.ToTotalElapsedTime()}", context.ColorScheme.Duration);

                            activeAssembliesBuilder.AppendLine(entryOutput);
                        }
                    }

                    // write builders side-by-side
                    var columnSpacing = 2;
                    var columnWidth   = (context.Console.WindowWidth / 2) - (columnSpacing * 2);
                    var output        = completedAssembliesBuilder.Interlace(activeAssembliesBuilder, columnSpacing, columnWidth);
                    context.Console.WriteLine(output);

                    // output complete
                    context.Console.SetCursorPosition(0, 0);
                }
            }
        }
        public void Draw(ViewContext context, long ticks)
        {
            if (_startTicks == 0)
            {
                _startTicks = ticks;
                ClearScreen(context);
            }

            var yPos                    = context.BeginY;
            var drawChecksum            = 0;
            var performDrawByTime       = true;
            var performDrawByDataChange = true;
            var activeTestsCountChanged = context.ActiveTests.Count != context.LastNumberOfTestsRunning;
            var windowWidth             = 160;
            var windowHeight            = 40;

            if (!context.Console.IsOutputRedirected)
            {
                Console.CursorVisible = false;
                windowWidth           = Console.WindowWidth;
                windowHeight          = Console.WindowHeight;
                if (windowHeight != _previousWindowHeight)
                {
                    ClearScreen(context);
                }
                _previousWindowHeight = windowHeight;
            }

            if (context.Console.IsOutputRedirected)
            {
                // if any tests have changed state based on checksum, allow a redraw
                drawChecksum            = ComputeActiveTestChecksum(context);
                performDrawByTime       = DateTime.Now.Subtract(context.LastDrawTime).TotalMilliseconds > context.DrawIntervalMilliseconds;
                performDrawByDataChange = drawChecksum != context.LastDrawChecksum;
            }
            if ((performDrawByTime || performDrawByDataChange))
            {
                if (!context.Console.IsOutputRedirected)
                {
                    WriteHeader(context);
                }

                // figure out how many tests we can fit on screen
                var maxActiveTestsToDisplay = context.Configuration.MaxActiveTestsToDisplay;
                if (!context.Console.IsOutputRedirected && maxActiveTestsToDisplay == 0)
                {
                    maxActiveTestsToDisplay = Console.WindowHeight - yPos - 2 - context.Configuration.MaxFailedTestsToDisplay - 4;
                }

                context.LastDrawChecksum = drawChecksum;
                if (context.Console.IsOutputRedirected)
                {
                    context.Console.WriteLine();
                }
                else if (activeTestsCountChanged)
                {
                    // number of tests changed
                    var nextNumberOfTestsDrawn = Math.Min(context.ActiveTests.Count, maxActiveTestsToDisplay);
                    if (nextNumberOfTestsDrawn < context.LastNumberOfTestsDrawn)
                    {
                        // clear the static display if we are displaying less tests than the previous draw
                        if (!context.Console.IsOutputRedirected)
                        {
                            var startY = yPos + nextNumberOfTestsDrawn + 3;
                            var endY   = startY + (context.LastNumberOfTestsDrawn - nextNumberOfTestsDrawn);
                            context.Console.ClearAtRange(0, startY, 0, endY);
                        }
                    }
                    context.LastNumberOfTestsRunning = context.ActiveTests.Count;
                }
                var testNumber              = 0;
                var totalActiveTests        = context.ActiveTests.Count(x => !x.IsQueuedForRemoval);
                var totalActiveTestFixtures = context.ActiveTestFixtures.Count(x => !x.IsQueuedForRemoval);
                var totalActiveAssemblies   = context.ActiveAssemblies.Count(x => !x.IsQueuedForRemoval);
                if (totalActiveTests > 0 && totalActiveTestFixtures == 0)
                {
                    // fix for older versions of nUnit that don't send the start test fixture event
                    // it will still be incorrect, as it will treat parents of tests with multiple cases as a testfixture
                    var parentIds = context.ActiveTests
                                    .Where(x => !x.IsQueuedForRemoval && !string.IsNullOrEmpty(x.Event.ParentId))
                                    .Select(x => x.Event.ParentId)
                                    .Distinct();
                    totalActiveTestFixtures = context.ActiveTestSuites.Count(x => !x.IsQueuedForRemoval && parentIds.Contains(x.Event.Id));
                }
                var totalPasses         = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Pass);
                var totalFails          = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Fail);
                var totalIgnored        = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Skipped);
                var totalTestsProcessed = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest);

                context.LastNumberOfLinesDrawn = 0;
                // write the summary of all test state
                context.Console.WriteAt(ColorTextBuilder.Create
                                        .Append("Tests state: ", context.ColorScheme.Bright)
                                        .Append($"Tests=", context.ColorScheme.Default)
                                        .Append($"{totalActiveTests} ", context.ColorScheme.Highlight)
                                        .Append($"Fixtures=", context.ColorScheme.Default)
                                        .Append($"{totalActiveTestFixtures} ", context.ColorScheme.Highlight)
                                        .Append($"Assemblies=", context.ColorScheme.Default)
                                        .Append($"{totalActiveAssemblies} ", context.ColorScheme.Highlight)
                                        .Append($"Pass="******"{totalPasses} ", context.ColorScheme.Success)
                                        .AppendIf(totalPasses == 0, $"{totalPasses} ", context.ColorScheme.Default)
                                        .Append($"Fail=", context.ColorScheme.Default)
                                        .AppendIf(totalFails > 0, $"{totalFails} ", context.ColorScheme.DarkError)
                                        .AppendIf(totalFails == 0, $"{totalFails} ", context.ColorScheme.Default)
                                        .AppendIf(!context.Console.IsOutputRedirected, $"Ignored=", context.ColorScheme.Default)
                                        .AppendIf(!context.Console.IsOutputRedirected, $"{totalIgnored} ", context.ColorScheme.DarkDefault)
                                        .Append($"Total=", context.ColorScheme.Default)
                                        .Append($"{totalTestsProcessed} ", context.ColorScheme.DarkDefault)
                                        .AppendIf(context.TotalTestsQueued > 0, $"of ", context.ColorScheme.Default)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{context.TotalTestsQueued} ", context.ColorScheme.DarkDefault)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{UTF8Constants.LeftBracket}", context.ColorScheme.Bright)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{((totalTestsProcessed / (double)context.TotalTestsQueued) * 100.0):F0}%", context.ColorScheme.DarkDuration)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{UTF8Constants.RightBracket}", context.ColorScheme.Bright)
                                        //.Append(context.Client.IsWaitingForConnection ? $"{UTF8Constants.LeftBracket}waiting{UTF8Constants.RightBracket}" : "", context.ColorScheme.DarkDuration)
                                        .AppendIf(!context.Console.IsOutputRedirected, (length) => DisplayUtil.Pad(windowWidth - length)),
                                        0,
                                        yPos,
                                        DirectOutputMode.Static);
                context.LastNumberOfLinesDrawn++;
                if (!context.Console.IsOutputRedirected)
                {
                    context.Console.WriteAt(ColorTextBuilder.Create
                                            .Append("Runtime: ", context.ColorScheme.Bright)
                                            .Append($"{DateTime.Now.Subtract(context.StartTime).ToTotalElapsedTime()} ", context.ColorScheme.Duration)
                                            .Append((length) => DisplayUtil.Pad(windowWidth - length)),
                                            0,
                                            yPos + 1,
                                            DirectOutputMode.Static);
                    context.LastNumberOfLinesDrawn++;
                }
                if (!context.Console.IsOutputRedirected && !string.IsNullOrEmpty(context.CurrentFrameworkVersion))
                {
                    context.Console.WriteAt(ColorTextBuilder.Create
                                            .Append($"{context.CurrentFrameworkVersion}", context.ColorScheme.DarkDuration)
                                            .AppendIf(!context.Console.IsOutputRedirected, (length) => DisplayUtil.Pad(windowWidth - length)),
                                            0, yPos + context.LastNumberOfLinesDrawn, DirectOutputMode.Static);
                    context.LastNumberOfLinesDrawn++;
                }

                // **************************
                // Draw Active Tests
                // **************************
                IEnumerable <EventEntry> activeTestsToDisplay;
                if (context.Console.IsOutputRedirected)
                {
                    // for log file output only show running tests
                    activeTestsToDisplay = context.ActiveTests
                                           .Where(x => x.Event.TestStatus == TestStatus.Running && !x.IsQueuedForRemoval)
                                           .OrderByDescending(x => x.Elapsed);
                }
                else
                {
                    activeTestsToDisplay = context.ActiveTests
                                           .OrderBy(x => x.Event.TestStatus)
                                           .ThenByDescending(x => x.Elapsed)
                                           .Take(maxActiveTestsToDisplay);
                }

                foreach (var test in activeTestsToDisplay)
                {
                    testNumber++;
                    var lifetime = DateTime.Now.Subtract(test.Event.StartTime);
                    if (test.Event.EndTime != DateTime.MinValue)
                    {
                        lifetime = test.Event.Duration;
                    }
                    var testColor  = context.ColorScheme.Highlight;
                    var testStatus = "INVD";
                    switch (test.Event.TestStatus)
                    {
                    case TestStatus.Pass:
                        testStatus = "PASS";
                        testColor  = context.ColorScheme.Success;
                        break;

                    case TestStatus.Fail:
                        testStatus = "FAIL";
                        testColor  = context.ColorScheme.Error;
                        break;

                    case TestStatus.Skipped:
                        testStatus = "SKIP";
                        testColor  = context.ColorScheme.DarkDefault;
                        break;

                    case TestStatus.Running:
                    default:
                        testStatus = $"RUN{GetRunningAnimationStep(context)}";
                        testColor  = context.ColorScheme.Highlight;
                        break;
                    }

                    var testName = test.Event.TestName;
                    // try to get the parent fixture if its available
                    var testFixtureName = !string.IsNullOrEmpty(test.Event.ParentId) ? context.ActiveTestFixtures
                                          .Where(x => x.Event.Id == test.Event.ParentId)
                                          .Select(x => x.Event.TestSuite)
                                          .FirstOrDefault() : string.Empty;
                    if (string.IsNullOrEmpty(testFixtureName))
                    {
                        // if this is an older version of nUnit, try getting the parent suite name
                        testFixtureName = !string.IsNullOrEmpty(test.Event.ParentId) ? context.ActiveTestSuites
                                          .Where(x => x.Event.Id == test.Event.ParentId)
                                          .Select(x => x.Event.TestSuite)
                                          .FirstOrDefault() : string.Empty;
                    }
                    var prettyTestName = DisplayUtil.GetPrettyTestName(testName, testFixtureName ?? string.Empty, context.ColorScheme.DarkDefault, context.ColorScheme.Default, context.ColorScheme.DarkDefault, context.MaxTestCaseArgumentLength);
                    // print out this test name and duration
                    context.Console.WriteAt(ColorTextBuilder.Create
                                            // test number
                                            .Append($"{testNumber}: ", context.ColorScheme.DarkDefault)
                                            // spaced in columns
                                            .AppendIf(testNumber < 10 && !context.Console.IsOutputRedirected, $" ")
                                            // test status if not logging to file
                                            .AppendIf(!context.Console.IsOutputRedirected, $"{UTF8Constants.LeftBracket}", context.ColorScheme.DarkDefault)
                                            .AppendIf(!context.Console.IsOutputRedirected, testStatus, testColor)
                                            .AppendIf(!context.Console.IsOutputRedirected, $"{UTF8Constants.RightBracket} ", context.ColorScheme.DarkDefault)
                                            // test name
                                            .Append(prettyTestName)
                                            // test duration
                                            .Append($" {lifetime.ToTotalElapsedTime()}", context.ColorScheme.Duration)
                                            // clear out the rest of the line
                                            .AppendIf((length) => !context.Console.IsOutputRedirected && length < windowWidth, (length) => DisplayUtil.Pad(windowWidth - length))
                                            .Truncate(windowWidth),
                                            0,
                                            yPos + context.LastNumberOfLinesDrawn,
                                            DirectOutputMode.Static);
                    context.LastNumberOfLinesDrawn++;
                }
                context.LastNumberOfTestsDrawn = testNumber;
                IncrementRunningAnimationStep(context);

                // **************************
                // Draw Test Failures
                // **************************
                if (!context.Console.IsOutputRedirected && context.Configuration.MaxFailedTestsToDisplay > 0)
                {
                    var failedTests = context.EventLog
                                      .Where(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Fail)
                                      .GroupBy(x => x.Event.TestName)
                                      .Select(x => x.FirstOrDefault())
                                      .OrderByDescending(x => x.DateAdded)
                                      .Take(context.Configuration.MaxFailedTestsToDisplay);
                    if (failedTests.Any())
                    {
                        var totalFailedTests = failedTests.Count();
                        context.Console.WriteAt(ColorTextBuilder.Create.AppendLine($"Most Recent Failed Tests", context.ColorScheme.Error), 0, windowHeight - totalFailedTests - _bottomPadding - 1, DirectOutputMode.Static);
                        var failedTestNumber = 0;
                        foreach (var test in failedTests)
                        {
                            failedTestNumber++;
                            var duration = DateTime.Now.Subtract(test.Event.StartTime);
                            if (test.Event.EndTime != DateTime.MinValue)
                            {
                                duration = test.Event.Duration;
                            }
                            var prettyTestName = DisplayUtil.GetPrettyTestName(test.Event.FullName, context.ColorScheme.DarkDefault, context.ColorScheme.Default, context.ColorScheme.DarkDefault, context.MaxTestCaseArgumentLength);
                            // print out this test name and duration
                            context.Console.WriteAt(ColorTextBuilder.Create
                                                    .Append($" {UTF8Constants.Bullet} ")
                                                    .Append(prettyTestName)
                                                    .Append($" {duration.ToTotalElapsedTime()}", context.ColorScheme.Duration)
                                                    .Append($" {UTF8Constants.LeftBracket}", context.ColorScheme.Bright)
                                                    .Append("FAILED", context.ColorScheme.Error)
                                                    .Append($"{UTF8Constants.RightBracket} ", context.ColorScheme.Bright)
                                                    .Append($"{test.DateAdded.ToString(Constants.TimeFormat)}", context.ColorScheme.DarkDuration)
                                                    // clear out the rest of the line
                                                    .AppendIf((length) => !context.Console.IsOutputRedirected && length < windowWidth, (length) => DisplayUtil.Pad(windowWidth - length))
                                                    .Truncate(windowWidth),
                                                    0,
                                                    windowHeight - totalFailedTests + failedTestNumber - _bottomPadding - 1,
                                                    DirectOutputMode.Static);
                        }
                    }
                }
            }
        }
        public void Draw(ViewContext context, long ticks)
        {
            if (_startTicks == 0)
            {
                _startTicks = ticks;
                ClearScreen(context);
            }

            if (!context.Console.IsOutputRedirected)
            {
                // if the user has scrolled the page don't perform any drawing
                if (Console.WindowTop > 0)
                {
                    return;
                }

                Console.CursorVisible = false;
                _performFullDraw      = (_startTicks - ticks) % DefaultTickWait == 0;
                var windowWidth  = Console.WindowWidth;
                var windowHeight = Console.WindowHeight;
                if (windowHeight != _previousWindowHeight)
                {
                    _performFullDraw = true;
                }
                _previousWindowHeight = windowHeight;

                if (_performFullDraw)
                {
                    ClearScreen(context);
                }

                var yPos                    = 0;
                var totalActiveTests        = context.ActiveTests.Count(x => !x.IsQueuedForRemoval);
                var totalActiveTestFixtures = context.ActiveTestFixtures.Count(x => !x.IsQueuedForRemoval);
                var totalActiveAssemblies   = context.ActiveAssemblies.Count(x => !x.IsQueuedForRemoval);
                if (totalActiveTests > 0 && totalActiveTestFixtures == 0)
                {
                    // fix for older versions of nUnit that don't send the start test fixture event
                    // it will still be incorrect, as it will treat parents of tests with multiple cases as a testfixture
                    var parentIds = context.ActiveTests
                                    .Where(x => !x.IsQueuedForRemoval && !string.IsNullOrEmpty(x.Event.ParentId))
                                    .Select(x => x.Event.ParentId)
                                    .Distinct();
                    totalActiveTestFixtures = context.ActiveTestSuites.Count(x => !x.IsQueuedForRemoval && parentIds.Contains(x.Event.Id));
                }
                var totalPasses         = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Pass);
                var totalFails          = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Fail);
                var totalIgnored        = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Skipped);
                var totalTestsProcessed = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest);

                WriteHeader(context);

                // write the summary of all test state
                context.Console.WriteAt(ColorTextBuilder.Create
                                        .Append("Tests state: ", context.ColorScheme.Bright)
                                        .Append($"Tests=", context.ColorScheme.Default)
                                        .Append($"{totalActiveTests} ", context.ColorScheme.Highlight)
                                        .Append($"Fixtures=", context.ColorScheme.Default)
                                        .Append($"{totalActiveTestFixtures} ", context.ColorScheme.Highlight)
                                        .Append($"Assemblies=", context.ColorScheme.Default)
                                        .Append($"{totalActiveAssemblies} ", context.ColorScheme.Highlight)
                                        .Append($"Pass="******"{totalPasses} ", context.ColorScheme.Success)
                                        .AppendIf(totalPasses == 0, $"{totalPasses} ", context.ColorScheme.Default)
                                        .Append($"Fail=", context.ColorScheme.Default)
                                        .AppendIf(totalFails > 0, $"{totalFails} ", context.ColorScheme.DarkError)
                                        .AppendIf(totalFails == 0, $"{totalFails} ", context.ColorScheme.Default)
                                        .AppendIf(!context.Console.IsOutputRedirected, $"Ignored=", context.ColorScheme.Default)
                                        .AppendIf(!context.Console.IsOutputRedirected, $"{totalIgnored} ", context.ColorScheme.DarkDefault)
                                        .Append($"Total=", context.ColorScheme.Default)
                                        .Append($"{totalTestsProcessed} ", context.ColorScheme.DarkDefault)
                                        .AppendIf(context.TotalTestsQueued > 0, $"of ", context.ColorScheme.Default)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{context.TotalTestsQueued} ", context.ColorScheme.DarkDefault)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{UTF8Constants.LeftBracket}", context.ColorScheme.Bright)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{((totalTestsProcessed / (double)context.TotalTestsQueued) * 100.0):F0}%", context.ColorScheme.DarkDuration)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{UTF8Constants.RightBracket}", context.ColorScheme.Bright)
                                        //.Append(context.Client.IsWaitingForConnection ? $"{UTF8Constants.LeftBracket}waiting{UTF8Constants.RightBracket}" : "", context.ColorScheme.DarkDuration)
                                        .AppendIf(!context.Console.IsOutputRedirected, (length) => DisplayUtil.Pad(windowWidth - length)),
                                        0,
                                        yPos + 1,
                                        DirectOutputMode.Static);

                if (_performFullDraw)
                {
                    // build a report
                    var runContext = new RunContext();
                    var report     = context.Commander.CreateReportFromHistory(false);
                    runContext.Runs.Add(context.Commander.GenerateReportContext(false), new List <DataEvent> {
                        report
                    });

                    var reportWriter = new ReportWriter(context.Console, context.ColorScheme, context.Configuration, runContext, false);
                    reportWriter.WriteFinalReport();
                    context.Console.SetCursorPosition(0, 0);
                }
            }
        }
        public void Draw(ViewContext context, long ticks)
        {
            if (_startTicks == 0)
            {
                _startTicks = ticks;
                context.Console.Clear();
            }

            if (!context.Console.IsOutputRedirected)
            {
                // if the user has scrolled the page don't perform any drawing
                if (Console.WindowTop > 0)
                {
                    return;
                }

                Console.CursorVisible = false;
                _performFullDraw      = (_startTicks - ticks) % DefaultTickWait == 0;
                var windowWidth  = Console.WindowWidth;
                var windowHeight = Console.WindowHeight;
                if (windowHeight != _previousWindowHeight)
                {
                    _performFullDraw = true;
                }
                _previousWindowHeight = windowHeight;

                if (_performFullDraw)
                {
                    ClearScreen(context);
                }

                var lineSeparator = DisplayUtil.Pad(Console.WindowWidth - 1, UTF8Constants.HorizontalLine);
                var yPos          = 0;
                WriteHeader(context);
                // figure out how many tests we can fit on screen
                var maxActiveTestsToDisplay = Console.WindowHeight - yPos - 2;

                var totalActive         = context.ActiveTests.Count(x => !x.IsQueuedForRemoval);
                var totalPasses         = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Pass);
                var totalFails          = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Fail);
                var totalIgnored        = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Skipped);
                var totalTestsProcessed = context.EventLog.Count(x => x.Event.Event == EventNames.EndTest);

                // write the summary of all test state
                context.Console.WriteAt(ColorTextBuilder.Create
                                        .Append("Tests state: ", context.ColorScheme.Bright)
                                        .Append($"Active=", context.ColorScheme.Default)
                                        .Append($"{totalActive} ", context.ColorScheme.Highlight)
                                        .Append($"Pass="******"{totalPasses} ", context.ColorScheme.Success)
                                        .AppendIf(totalPasses == 0, $"{totalPasses} ", context.ColorScheme.Default)
                                        .Append($"Fail=", context.ColorScheme.Default)
                                        .AppendIf(totalFails > 0, $"{totalFails} ", context.ColorScheme.DarkError)
                                        .AppendIf(totalFails == 0, $"{totalFails} ", context.ColorScheme.Default)
                                        .AppendIf(!context.Console.IsOutputRedirected, $"Ignored=", context.ColorScheme.Default)
                                        .AppendIf(!context.Console.IsOutputRedirected, $"{totalIgnored} ", context.ColorScheme.DarkDefault)
                                        .Append($"Total=", context.ColorScheme.Default)
                                        .Append($"{totalTestsProcessed} ", context.ColorScheme.DarkDefault)
                                        .AppendIf(context.TotalTestsQueued > 0, $"of ", context.ColorScheme.Default)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{context.TotalTestsQueued} ", context.ColorScheme.DarkDefault)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{UTF8Constants.LeftBracket}", context.ColorScheme.Bright)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{((totalTestsProcessed / (double)context.TotalTestsQueued) * 100.0):F0}%", context.ColorScheme.DarkDuration)
                                        .AppendIf(context.TotalTestsQueued > 0, $"{UTF8Constants.RightBracket}", context.ColorScheme.Bright)
                                        //.Append(context.Client.IsWaitingForConnection ? $"{UTF8Constants.LeftBracket}waiting{UTF8Constants.RightBracket}" : "", context.ColorScheme.DarkDuration)
                                        .AppendIf(!context.Console.IsOutputRedirected, (length) => DisplayUtil.Pad(windowWidth - length)),
                                        0,
                                        yPos + 1,
                                        DirectOutputMode.Static);

                var failedTests = context.EventLog
                                  .Where(x => x.Event.Event == EventNames.EndTest && x.Event.TestStatus == TestStatus.Fail)
                                  .GroupBy(x => x.Event.TestName)
                                  .Select(x => x.FirstOrDefault())
                                  .OrderByDescending(x => x.DateAdded)
                                  .Take(context.Configuration.MaxFailedTestsToDisplay);
                context.Console.WriteAt(ColorTextBuilder.Create.AppendLine($"Failed Tests Report", context.ColorScheme.Error), 0, yPos + 2, DirectOutputMode.Static);
                if (!failedTests.Any())
                {
                    context.Console.WriteAt(ColorTextBuilder.Create.AppendLine($"There are no failed tests.", context.ColorScheme.Default), 0, yPos + 4, DirectOutputMode.Static);
                }
                else
                {
                    // don't display this as often as its expensive to write
                    if (_performFullDraw)
                    {
                        context.Console.SetCursorPosition(0, yPos + 5);
                        foreach (var test in failedTests)
                        {
                            var testOutput = new ColorTextBuilder();
                            var errorTime  = test.DateAdded;
                            var duration   = DateTime.Now.Subtract(test.Event.StartTime);
                            if (test.Event.EndTime != DateTime.MinValue)
                            {
                                duration = test.Event.Duration;
                            }
                            var prettyTestName = DisplayUtil.GetPrettyTestName(test.Event.FullName, context.ColorScheme.DarkDefault, context.ColorScheme.Default, context.ColorScheme.DarkDefault, context.MaxTestCaseArgumentLength);
                            // print out this test name and duration
                            testOutput.Append(ColorTextBuilder.Create
                                              .Append($" {UTF8Constants.Bullet} ")
                                              .Append(prettyTestName)
                                              .Append($" {duration.ToTotalElapsedTime()}", context.ColorScheme.Duration)
                                              .Append($" {UTF8Constants.LeftBracket}", context.ColorScheme.Bright)
                                              .Append("FAILED", context.ColorScheme.Error)
                                              .Append($"{UTF8Constants.RightBracket}", context.ColorScheme.Bright)
                                              // clear out the rest of the line
                                              .AppendIf((length) => !context.Console.IsOutputRedirected && length < windowWidth, (length) => DisplayUtil.Pad(windowWidth - length))
                                              .Truncate(windowWidth));
                            testOutput.Append(ColorTextBuilder.Create.Append("  Failed at: ", context.ColorScheme.Default).AppendLine($"{errorTime.ToString(Constants.TimeFormat)}", context.ColorScheme.DarkDuration));

                            // print out errors
                            if (!string.IsNullOrEmpty(test.Event.ErrorMessage))
                            {
                                testOutput.AppendLine($"  Error Output: ", context.ColorScheme.Bright);
                                testOutput.AppendLine(lineSeparator, context.ColorScheme.DarkDefault);
                                if (!context.Configuration.DontPrettify)
                                {
                                    testOutput.Append(ErrorEncoding.Format(test.Event.ErrorMessage, context.ColorScheme));
                                }
                                else
                                {
                                    testOutput.Append(test.Event.ErrorMessage);
                                }
                                testOutput.AppendLine();
                                testOutput.AppendLine(lineSeparator, context.ColorScheme.DarkDefault);
                            }
                            if (!string.IsNullOrEmpty(test.Event.StackTrace))
                            {
                                testOutput.AppendLine($"  Stack Trace:", context.ColorScheme.Bright);
                                testOutput.AppendLine(lineSeparator, context.ColorScheme.DarkDefault);
                                if (!context.Configuration.DontPrettify)
                                {
                                    testOutput.Append(StackTracePrettify.Format(test.Event.StackTrace, context.ColorScheme));
                                }
                                else
                                {
                                    testOutput.Append(test.Event.StackTrace);
                                }
                                testOutput.AppendLine();
                                testOutput.AppendLine(lineSeparator, context.ColorScheme.DarkDefault);
                            }
                            if (!string.IsNullOrEmpty(test.Event.TestOutput))
                            {
                                testOutput.AppendLine($"  Test Output: ", context.ColorScheme.Bright);
                                testOutput.AppendLine(lineSeparator, context.ColorScheme.DarkDefault);
                                if (!context.Configuration.DontPrettify)
                                {
                                    testOutput.Append(ErrorEncoding.Format(test.Event.TestOutput, context.ColorScheme));
                                }
                                else
                                {
                                    testOutput.Append(test.Event.TestOutput);
                                }
                                testOutput.AppendLine();
                                testOutput.AppendLine(lineSeparator, context.ColorScheme.DarkDefault);
                            }
                            testOutput.AppendLine();
                            testOutput.AppendLine();

                            context.Console.WriteLine(testOutput);
                        }

                        context.Console.SetCursorPosition(0, 0);
                    }
                }
            }
        }