From dc715e2a0d157657b303b7a96ef72ab168e98284 Mon Sep 17 00:00:00 2001 From: ladeak Date: Sun, 21 Jun 2026 16:23:37 +0200 Subject: [PATCH] Add color annotations to statistics printer output Enhanced statistics printer to include color tags for histograms and status code lines (Cyan, Green, Red) in console output. Improving visual distinction of success and error cases. --- .../Statitics/StatisticsPrinter.cs | 12 ++++- .../Performance/CHttpDiffFunctionalTests.cs | 32 ++++++------- .../Statistics/StatisticsPrinterTests.cs | 45 ++++++++++++++++++- tests/CHttp.Tests/TestConsoleAsOuput.cs | 12 ++++- 4 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/CHttp/Performance/Statitics/StatisticsPrinter.cs b/src/CHttp/Performance/Statitics/StatisticsPrinter.cs index f9611fb..aa0c080 100644 --- a/src/CHttp/Performance/Statitics/StatisticsPrinter.cs +++ b/src/CHttp/Performance/Statitics/StatisticsPrinter.cs @@ -1,4 +1,5 @@ -using System.Numerics; +using System.Drawing; +using System.Numerics; using CHttp.Abstractions; using CHttp.Data; using CHttp.Performance.Data; @@ -51,11 +52,15 @@ public ValueTask HandleStats(PerformanceMeasurementResults session, Stats stats) int lineLength = _console.WindowWidth; var scaleNormalize = (double)lineLength / session.Summaries.Count; string separator = new string('-', lineLength); + // Histogram if (session.Summaries.Count >= 100) { _console.WriteLine(separator); - PrintHistogram(stats, scaleNormalize); + var oldColor = _console.ForegroundColor; + _console.ForegroundColor = ConsoleColor.Cyan; + PrintHistogram(stats, scaleNormalize); + _console.ForegroundColor = oldColor; } _console.WriteLine(separator); PrintStatusCodes(stats.StatusCodes); @@ -65,8 +70,11 @@ public ValueTask HandleStats(PerformanceMeasurementResults session, Stats stats) private void PrintStatusCodes(int[] statusCodes) { + var oldColor = _console.ForegroundColor; + _console.ForegroundColor = statusCodes[0] + statusCodes[2]+ statusCodes[3]+ statusCodes[4]+ statusCodes[5] > 0 ? ConsoleColor.Red : ConsoleColor.Green; _console.WriteLine("HTTP status codes:"); _console.WriteLine($"1xx: {statusCodes[0]}, 2xx: {statusCodes[1]}, 3xx: {statusCodes[2]}, 4xx: {statusCodes[3]}, 5xx: {statusCodes[4]}, Other: {statusCodes[5]}"); + _console.ForegroundColor = oldColor; } private void PrintHistogram(Stats stats, double scaleNormalize) diff --git a/tests/CHttp.Tests/Performance/CHttpDiffFunctionalTests.cs b/tests/CHttp.Tests/Performance/CHttpDiffFunctionalTests.cs index 9ba65fe..9f5c9cb 100644 --- a/tests/CHttp.Tests/Performance/CHttpDiffFunctionalTests.cs +++ b/tests/CHttp.Tests/Performance/CHttpDiffFunctionalTests.cs @@ -87,15 +87,15 @@ public async Task DisplayingMultipleEqualFile() .InvokeAsync(cancellationToken: TestContext.Current.CancellationToken) .WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken); - Assert.Contains("| Mean: 1.000 s 0 ns |", console.Text); - Assert.Contains("| StdDev: 0.000 ns 0 ns |", console.Text); - Assert.Contains("| Error: 0.000 ns 0 ns |", console.Text); - Assert.Contains("| Median: 1.000 s 0 ns |", console.Text); - Assert.Contains("| Min: 1.000 s 0 ns |", console.Text); - Assert.Contains("| Max: 1.000 s 0 ns |", console.Text); - Assert.Contains("| 95th: 1.000 s 0 ns |", console.Text); + Assert.Contains("| Mean: 1.000 s [color:Green] 0 ns[color:Black] |", console.Text); + Assert.Contains("| StdDev: 0.000 ns [color:Green] 0 ns[color:Black] |", console.Text); + Assert.Contains("| Error: 0.000 ns [color:Green] 0 ns[color:Black] |", console.Text); + Assert.Contains("| Median: 1.000 s [color:Green] 0 ns[color:Black] |", console.Text); + Assert.Contains("| Min: 1.000 s [color:Green] 0 ns[color:Black] |", console.Text); + Assert.Contains("| Max: 1.000 s [color:Green] 0 ns[color:Black] |", console.Text); + Assert.Contains("| 95th: 1.000 s [color:Green] 0 ns[color:Black] |", console.Text); Assert.Contains("| Throughput: 100.000 B/s 0 B/s |", console.Text); - Assert.Contains("| Req/Sec: 1 0 |", console.Text); + Assert.Contains("| Req/Sec: 1 [color:Green] 0[color:Black] |", console.Text); Assert.Contains("1xx: 0 +0, 2xx: 1 +0, 3xx: 0 +0, 4xx: 0 +0, 5xx: 0 +0, Other: 0 +0", console.Text); } @@ -128,15 +128,15 @@ public async Task DisplayingMultipleDifferentFile() .WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken); Assert.Contains($"RequestCount: 1, Clients: 1", console.Text); - Assert.Contains("| Mean: 1.000 s +1.000 s |", console.Text); - Assert.Contains("| StdDev: 0.000 ns 0 ns |", console.Text); - Assert.Contains("| Error: 0.000 ns 0 ns |", console.Text); - Assert.Contains("| Median: 1.000 s +1.000 s |", console.Text); - Assert.Contains("| Min: 1.000 s +1.000 s |", console.Text); - Assert.Contains("| Max: 1.000 s +1.000 s |", console.Text); - Assert.Contains("| 95th: 1.000 s +1.000 s |", console.Text); + Assert.Contains("| Mean: 1.000 s [color:Red] +1.000 s [color:Black] |", console.Text); + Assert.Contains("| StdDev: 0.000 ns [color:Green] 0 ns[color:Black] |", console.Text); + Assert.Contains("| Error: 0.000 ns [color:Green] 0 ns[color:Black] |", console.Text); + Assert.Contains("| Median: 1.000 s [color:Red] +1.000 s [color:Black] |", console.Text); + Assert.Contains("| Min: 1.000 s [color:Red] +1.000 s [color:Black] |", console.Text); + Assert.Contains("| Max: 1.000 s [color:Red] +1.000 s [color:Black] |", console.Text); + Assert.Contains("| 95th: 1.000 s [color:Red] +1.000 s [color:Black] |", console.Text); Assert.Contains("| Throughput: 100.000 B/s +100.000 B/s |", console.Text); - Assert.Contains("| Req/Sec: 1 -0.5 |", console.Text); + Assert.Contains("| Req/Sec: 1 [color:Red] -0.5[color:Black] |", console.Text); Assert.Contains("1xx: 0 +0, 2xx: 1 -1, 3xx: 0 +0, 4xx: 0 +1, 5xx: 0 +0, Other: 0 +0", console.Text); } diff --git a/tests/CHttp.Tests/Performance/Statistics/StatisticsPrinterTests.cs b/tests/CHttp.Tests/Performance/Statistics/StatisticsPrinterTests.cs index ecaab93..9e5f4b4 100644 --- a/tests/CHttp.Tests/Performance/Statistics/StatisticsPrinterTests.cs +++ b/tests/CHttp.Tests/Performance/Statistics/StatisticsPrinterTests.cs @@ -1,4 +1,5 @@ using System.Globalization; +using System.Net; using CHttp.Abstractions; using CHttp.Data; using CHttp.Performance.Data; @@ -159,10 +160,52 @@ public async Task Histogram() var sut = new StatisticsPrinter(console); await sut.SummarizeResultsAsync(new PerformanceMeasurementResults() { Summaries = input, TotalBytesRead = 1, MaxConnections = 1, Behavior = new(input.Count, 1, false) }); - Assert.Contains(" 1.300 s ##################################################", console.Text); + Assert.Contains("[color:Cyan] 1.300 s ##################################################", console.Text); Assert.Contains(" 2.200 s ##################################################", console.Text); Assert.Contains(" 3.100 s ##################################################", console.Text); Assert.Contains(" 4.000 s ##################################################", console.Text); + Assert.Contains("[color:Black]---", console.Text); + Assert.Contains("[color:Green]HTTP status codes:", console.Text); Assert.Equal(200, console.Text.Count(x => x == '#')); } + + [Theory] + [InlineData(HttpStatusCode.Continue)] // 100 + [InlineData(HttpStatusCode.Ambiguous)] // 300 + [InlineData(HttpStatusCode.BadRequest)] // 400 + [InlineData(HttpStatusCode.InternalServerError)] // 500 + public async Task ErrorCodes_Print_RedStatusLine(HttpStatusCode statusCode) + { + var input = new List(); + for (int i = 0; i < 100; i++) + { + var summary = new Summary("url", new DateTime(2023, 04, 05, 21, 32, 00, DateTimeKind.Utc), TimeSpan.FromSeconds(1)); + summary.RequestCompleted(statusCode); + input.Add(summary); + } + var console = new TestConsoleAsOuput(59); + var sut = new StatisticsPrinter(console); + await sut.SummarizeResultsAsync(new PerformanceMeasurementResults() { Summaries = input, TotalBytesRead = 1, MaxConnections = 1, Behavior = new(input.Count, 1, false) }); + + Assert.Contains("[color:Red]HTTP status codes:", console.Text); + Assert.Contains("[color:Black]---", console.Text); + } + + [Fact] + public async Task OtherError_Print_RedStatusLine() + { + var input = new List(); + for (int i = 0; i < 100; i++) + { + var summary = new Summary("url", new DateTime(2023, 04, 05, 21, 32, 00, DateTimeKind.Utc), TimeSpan.FromSeconds(1)) { ErrorCode = ErrorType.Other }; + summary.RequestCompleted((HttpStatusCode)999); + input.Add(summary); + } + var console = new TestConsoleAsOuput(59); + var sut = new StatisticsPrinter(console); + await sut.SummarizeResultsAsync(new PerformanceMeasurementResults() { Summaries = input, TotalBytesRead = 1, MaxConnections = 1, Behavior = new(input.Count, 1, false) }); + + Assert.Contains("[color:Red]HTTP status codes:", console.Text); + Assert.Contains("[color:Black]---", console.Text); + } } \ No newline at end of file diff --git a/tests/CHttp.Tests/TestConsoleAsOuput.cs b/tests/CHttp.Tests/TestConsoleAsOuput.cs index 486c282..4ac7b13 100644 --- a/tests/CHttp.Tests/TestConsoleAsOuput.cs +++ b/tests/CHttp.Tests/TestConsoleAsOuput.cs @@ -18,11 +18,19 @@ public TestConsoleAsOuput(int windowWidth) WindowWidth = windowWidth; } - public string Text { get => _sb.ToString(); } + public string Text => field ??= _sb.ToString(); public int WindowWidth { get; } - public ConsoleColor ForegroundColor { get; set; } + public ConsoleColor ForegroundColor + { + get; + set + { + field = value; + _sb.Append($"[color:{value}]"); + } + } public (int Left, int Top) GetCursorPosition() => (0, 0);