public void GetPrettyTestName_LongTestName_ExpectedBehavior()
        {
            // Arrange
            string fullName      = "TestingPathSuperLongPathThatIsReallyLongAndShouldGetTruncated.TestingTestNameWithAReallyLongPath(Arguments here and more here because this is too long)";
            var    pathColor     = Color.Red;
            var    testNameColor = Color.White;
            var    argsColor     = Color.Yellow;

            // Act
            DisplayUtil.MaxWidth = 94;
            var result = DisplayUtil.GetPrettyTestName(
                fullName,
                pathColor,
                testNameColor,
                argsColor
                );

            // Assert
            Assert.NotNull(result);
            Assert.AreEqual(3, result.TextFragments.Count);
            Assert.AreEqual("Te...", result.TextFragments[0].Text);
            Assert.AreEqual(pathColor, result.TextFragments[0].ForegroundColor);
            Assert.AreEqual("TestingTestNameWithAReallyLongPath", result.TextFragments[1].Text);
            Assert.AreEqual(testNameColor, result.TextFragments[1].ForegroundColor);
            Assert.AreEqual("(Arguments here and more here because this is too long)", result.TextFragments[2].Text);
            Assert.AreEqual(argsColor, result.TextFragments[2].ForegroundColor);
            mockRepository.VerifyAll();
        }
Beispiel #2
0
        public override ColorTextBuilder Write(object parameters = null)
        {
            var builder = new ColorTextBuilder();

            WriteRoundBox(builder, $"Top {_configuration.SlowestTestsCount} slowest assemblies");
            var allAssembliesByName = _runContext.Runs
                                      .SelectMany(x => x.Key.EventEntries)
                                      .Where(x => x.Event.Event == EventNames.StartAssembly || x.Event.Event == EventNames.EndAssembly)
                                      .GroupBy(x => x.Event.TestSuite);
            var slowestAssemblies = allAssembliesByName
                                    .SelectMany(x => x.Select(y => y.Event))
                                    .Where(x => x.Event == EventNames.EndAssembly)
                                    .OrderByDescending(x => x.Duration)
                                    .GroupBy(x => x.TestSuite)
                                    .Take(_configuration.SlowestTestsCount);

            foreach (var test in slowestAssemblies)
            {
                builder.Append($" {UTF8Constants.Bullet} ");
                builder.Append(DisplayUtil.GetPrettyTestName(test.FirstOrDefault().TestSuite, _colorScheme.DarkDefault, _colorScheme.Default, _colorScheme.DarkDefault));
                builder.AppendLine($" {test.FirstOrDefault().Duration.ToElapsedTime()}", _colorScheme.Duration);
            }
            builder.AppendLine(Environment.NewLine);
            return(builder);
        }
        public void GetPrettyTestName_TestName_ExpectedBehavior()
        {
            // Arrange
            string fullName                  = "TestingPath.TestingTestName(Arguments here)";
            var    pathColor                 = Color.Red;
            var    testNameColor             = Color.White;
            var    argsColor                 = Color.Yellow;
            int    maxTestCaseArgumentLength = 0;

            // Act
            var result = DisplayUtil.GetPrettyTestName(
                fullName,
                pathColor,
                testNameColor,
                argsColor,
                maxTestCaseArgumentLength);

            // Assert
            Assert.NotNull(result);
            Assert.AreEqual(fullName.Length, result.Length);
            Assert.AreEqual("TestingPath.", result.TextFragments[0].Text);
            Assert.AreEqual(pathColor, result.TextFragments[0].ForegroundColor);
            Assert.AreEqual("TestingTestName", result.TextFragments[1].Text);
            Assert.AreEqual(testNameColor, result.TextFragments[1].ForegroundColor);
            Assert.AreEqual("(Arguments here)", result.TextFragments[2].Text);
            Assert.AreEqual(argsColor, result.TextFragments[2].ForegroundColor);
            mockRepository.VerifyAll();
        }
Beispiel #4
0
        public ColorTextBuilder BuildReport()
        {
            var str = new ColorTextBuilder();

            str.Append($"  Total Runs Analyzed: {UTF8Constants.LeftBracket}", _colorScheme.Bright).Append($"{TotalDataPoints}", _colorScheme.Highlight).AppendLine($"{UTF8Constants.RightBracket}", _colorScheme.Bright);
            str.AppendLine();
            str.Append("  Unstable Tests:", _colorScheme.Bright);
            if (UnstableTests.Count == 0)
            {
                str.AppendLine(" <none>");
            }
            else
            {
                str.AppendLine();
                foreach (var test in UnstableTests)
                {
                    str.Append($" {_bulletChar} ")
                    .Append(DisplayUtil.GetPrettyTestName(test.FullName, _colorScheme.DarkDefault, _colorScheme.Default, _colorScheme.DarkDefault))
                    .Append($"{UTF8Constants.LeftBracket}", _colorScheme.Bright)
                    .Append(string.Format("{0:0.0}% failures", test.Percentage * 100.0), _colorScheme.Error)
                    .Append(", ")
                    .Append(test.Ratio, _colorScheme.Error)
                    .AppendLine($"{UTF8Constants.RightBracket}", _colorScheme.Bright);
                }
            }

            str.AppendLine();
            str.Append("  Duration Anomalies:", _colorScheme.Bright);
            if (DurationAnomalyTests.Count == 0)
            {
                str.AppendLine(" <none>");
            }
            else
            {
                str.AppendLine();
                foreach (var test in DurationAnomalyTests)
                {
                    // test got slower
                    var color     = _colorScheme.Error;
                    var direction = "+";
                    if (test.DurationChange.Ticks < 0)
                    {
                        // test got faster
                        color     = _colorScheme.DarkSuccess;
                        direction = "";
                    }
                    str.Append($" {_bulletChar} ")
                    .Append(DisplayUtil.GetPrettyTestName(test.FullName, _colorScheme.DarkDefault, _colorScheme.Default, _colorScheme.DarkDefault))
                    .Append($"Diff", _colorScheme.Bright)
                    .Append($"{UTF8Constants.LeftBracket}", _colorScheme.Bright)
                    .Append($"{direction}{test.DurationChange.ToElapsedTime()}", color)
                    .Append($"{UTF8Constants.RightBracket}", _colorScheme.Bright)
                    .Append($" Time", _colorScheme.Bright)
                    .Append($"{UTF8Constants.LeftBracket}", _colorScheme.Bright)
                    .Append($"{test.Duration.ToElapsedTime()}", _colorScheme.Duration)
                    .Append($"{UTF8Constants.RightBracket}", _colorScheme.Bright)
                    .Append($" Median", _colorScheme.Bright)
                    .Append($"{UTF8Constants.LeftBracket}", _colorScheme.Bright)
                    .Append($"{test.Average.ToElapsedTime()}", _colorScheme.Duration)
                    .AppendLine($"{UTF8Constants.RightBracket}", _colorScheme.Bright);
                }
            }
            str.AppendLine();

            return(str);
        }
        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;
                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);
                    }
                }
            }
        }