From b20190c2ed9c254578796d9c098a7db8b9e61409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 18 Sep 2025 09:53:47 +0200 Subject: [PATCH 1/3] discovery indicator fixes --- sdk.slnx | 3 - .../Terminal/AnsiTerminalTestProgressFrame.cs | 92 +++++++++++-------- .../Test/MTP/Terminal/SimpleTerminalBase.cs | 53 +++++++---- .../Test/MTP/Terminal/TerminalTestReporter.cs | 10 +- .../Test/MTP/Terminal/TestProgressState.cs | 14 ++- .../Test/TestProgressStateTests.cs | 27 +++--- 6 files changed, 118 insertions(+), 81 deletions(-) diff --git a/sdk.slnx b/sdk.slnx index 0246a040f447..c9ebd7d9e682 100644 --- a/sdk.slnx +++ b/sdk.slnx @@ -54,8 +54,6 @@ - - @@ -318,7 +316,6 @@ - diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/AnsiTerminalTestProgressFrame.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/AnsiTerminalTestProgressFrame.cs index 6a6974bd97f1..822b0c4e9791 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/AnsiTerminalTestProgressFrame.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/AnsiTerminalTestProgressFrame.cs @@ -26,60 +26,78 @@ public void AppendTestWorkerProgress(TestProgressState progress, RenderedProgres int nonReservedWidth = Width - (durationString.Length + 2); + int discovered = progress.DiscoveredTests; int passed = progress.PassedTests; int failed = progress.FailedTests; int skipped = progress.SkippedTests; int retried = progress.RetriedFailedTests; int charsTaken = 0; - terminal.Append('['); - charsTaken++; - terminal.SetColor(TerminalColor.DarkGreen); - terminal.Append('✓'); - charsTaken++; - string passedText = passed.ToString(CultureInfo.CurrentCulture); - terminal.Append(passedText); - charsTaken += passedText.Length; - terminal.ResetColor(); + if (!progress.IsDiscovery) + { + terminal.Append('['); + charsTaken++; + terminal.SetColor(TerminalColor.DarkGreen); + terminal.Append('✓'); + charsTaken++; + string passedText = passed.ToString(CultureInfo.CurrentCulture); + terminal.Append(passedText); + charsTaken += passedText.Length; + terminal.ResetColor(); - terminal.Append('/'); - charsTaken++; + terminal.Append('/'); + charsTaken++; - terminal.SetColor(TerminalColor.DarkRed); - terminal.Append('x'); - charsTaken++; - string failedText = failed.ToString(CultureInfo.CurrentCulture); - terminal.Append(failedText); - charsTaken += failedText.Length; - terminal.ResetColor(); + terminal.SetColor(TerminalColor.DarkRed); + terminal.Append('x'); + charsTaken++; + string failedText = failed.ToString(CultureInfo.CurrentCulture); + terminal.Append(failedText); + charsTaken += failedText.Length; + terminal.ResetColor(); - terminal.Append('/'); - charsTaken++; + terminal.Append('/'); + charsTaken++; - terminal.SetColor(TerminalColor.DarkYellow); - terminal.Append('↓'); - charsTaken++; - string skippedText = skipped.ToString(CultureInfo.CurrentCulture); - terminal.Append(skippedText); - charsTaken += skippedText.Length; - terminal.ResetColor(); + terminal.SetColor(TerminalColor.DarkYellow); + terminal.Append('↓'); + charsTaken++; + string skippedText = skipped.ToString(CultureInfo.CurrentCulture); + terminal.Append(skippedText); + charsTaken += skippedText.Length; + terminal.ResetColor(); + + if (retried > 0) + { + terminal.Append('/'); + charsTaken++; + terminal.SetColor(TerminalColor.Gray); + terminal.Append('r'); + charsTaken++; + string retriedText = retried.ToString(CultureInfo.CurrentCulture); + terminal.Append(retriedText); + charsTaken += retriedText.Length; + terminal.ResetColor(); + } - if (retried > 0) + terminal.Append(']'); + charsTaken++; + } + else { - terminal.Append('/'); + string discoveredText = progress.DiscoveredTests.ToString(CultureInfo.CurrentCulture); + terminal.Append('['); charsTaken++; - terminal.SetColor(TerminalColor.Gray); - terminal.Append('r'); + terminal.SetColor(TerminalColor.DarkMagenta); + terminal.Append('+'); charsTaken++; - string retriedText = retried.ToString(CultureInfo.CurrentCulture); - terminal.Append(retriedText); - charsTaken += retriedText.Length; + terminal.Append(discoveredText); + charsTaken += discoveredText.Length; terminal.ResetColor(); + terminal.Append(']'); + charsTaken++; } - terminal.Append(']'); - charsTaken++; - terminal.Append(' '); charsTaken++; AppendToWidth(terminal, progress.AssemblyName, nonReservedWidth, ref charsTaken); diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/SimpleTerminalBase.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/SimpleTerminalBase.cs index 310b3edd271e..760aff97d746 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/SimpleTerminalBase.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/SimpleTerminalBase.cs @@ -67,32 +67,49 @@ public void RenderProgress(TestProgressState?[] progress) string durationString = HumanReadableDurationFormatter.Render(p.Stopwatch.Elapsed); + int discovered = p.DiscoveredTests; int passed = p.PassedTests; int failed = p.FailedTests; int skipped = p.SkippedTests; - // Use just ascii here, so we don't put too many restrictions on fonts needing to - // properly show unicode, or logs being saved in particular encoding. - Append('['); - SetColor(TerminalColor.DarkGreen); - Append('+'); - Append(passed.ToString(CultureInfo.CurrentCulture)); - ResetColor(); + if (!p.IsDiscovery) + { + // Use just ascii here, so we don't put too many restrictions on fonts needing to + // properly show unicode, or logs being saved in particular encoding. + Append('['); + SetColor(TerminalColor.DarkGreen); + Append('+'); + Append(passed.ToString(CultureInfo.CurrentCulture)); + ResetColor(); + + Append('/'); - Append('/'); + SetColor(TerminalColor.DarkRed); + Append('x'); + Append(failed.ToString(CultureInfo.CurrentCulture)); + ResetColor(); - SetColor(TerminalColor.DarkRed); - Append('x'); - Append(failed.ToString(CultureInfo.CurrentCulture)); - ResetColor(); + Append('/'); - Append('/'); + SetColor(TerminalColor.DarkYellow); + Append('?'); + Append(skipped.ToString(CultureInfo.CurrentCulture)); + ResetColor(); - SetColor(TerminalColor.DarkYellow); - Append('?'); - Append(skipped.ToString(CultureInfo.CurrentCulture)); - ResetColor(); - Append(']'); + Append(']'); + } + else + { + // Use just ascii here, so we don't put too many restrictions on fonts needing to + // properly show unicode, or logs being saved in particular encoding. + Append('['); + SetColor(TerminalColor.DarkMagenta); + Append('+'); + Append(discovered.ToString(CultureInfo.CurrentCulture)); + ResetColor(); + + Append(']'); + } Append(' '); Append(p.AssemblyName); diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs index 4494ea61535c..3e320fa8a06b 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs @@ -139,7 +139,7 @@ private TestProgressState GetOrAddAssemblyRun(string assembly, string? targetFra return _assemblies.GetOrAdd(executionId, _ => { IStopwatch sw = CreateStopwatch(); - var assemblyRun = new TestProgressState(Interlocked.Increment(ref _counter), assembly, targetFramework, architecture, sw); + var assemblyRun = new TestProgressState(Interlocked.Increment(ref _counter), assembly, targetFramework, architecture, sw, _isDiscovery); int slotIndex = _terminalWithProgress.AddWorker(assemblyRun); assemblyRun.SlotIndex = slotIndex; @@ -801,7 +801,7 @@ void AppendOutputWhenPresent(string description, string? output) private static void AppendAssemblySummary(TestProgressState assemblyRun, ITerminal terminal) { terminal.ResetColor(); - + AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assemblyRun.Assembly, assemblyRun.TargetFramework, assemblyRun.Architecture); terminal.Append(' '); AppendAssemblyResult(terminal, assemblyRun); @@ -914,15 +914,15 @@ public void AppendTestDiscoverySummary(ITerminal terminal, int? exitCode) var assemblies = _assemblies.Select(asm => asm.Value).OrderBy(a => a.Assembly).Where(a => a is not null).ToList(); int totalTests = _assemblies.Values.Sum(a => a.TotalTests); - bool runFailed = _wasCancelled; + bool runFailed = _wasCancelled || totalTests < 1; foreach (TestProgressState assembly in assemblies) { - terminal.Append(string.Format(CultureInfo.CurrentCulture, CliCommandStrings.DiscoveredTestsInAssembly, assembly.DiscoveredTests.Count)); + terminal.Append(string.Format(CultureInfo.CurrentCulture, CliCommandStrings.DiscoveredTestsInAssembly, assembly.DiscoveredTestNames.Count)); terminal.Append(" - "); AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assembly.Assembly, assembly.TargetFramework, assembly.Architecture); terminal.AppendLine(); - foreach ((string? displayName, string? uid) in assembly.DiscoveredTests) + foreach ((string? displayName, string? uid) in assembly.DiscoveredTestNames) { if (displayName is not null) { diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TestProgressState.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TestProgressState.cs index a39d0a0fbe48..fd801f6a5a6c 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TestProgressState.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TestProgressState.cs @@ -6,7 +6,7 @@ namespace Microsoft.DotNet.Cli.Commands.Test.Terminal; -internal sealed class TestProgressState(long id, string assembly, string? targetFramework, string? architecture, IStopwatch stopwatch) +internal sealed class TestProgressState(long id, string assembly, string? targetFramework, string? architecture, IStopwatch stopwatch, bool isDiscovery) { private readonly Dictionary _testUidToResults = new(); @@ -24,13 +24,15 @@ internal sealed class TestProgressState(long id, string assembly, string? target public IStopwatch Stopwatch { get; } = stopwatch; + public int DiscoveredTests { get; private set; } + public int FailedTests { get; private set; } public int PassedTests { get; private set; } public int SkippedTests { get; private set; } - public int TotalTests => PassedTests + SkippedTests + FailedTests; + public int TotalTests => IsDiscovery ? DiscoveredTests : PassedTests + SkippedTests + FailedTests; public int RetriedFailedTests { get; private set; } @@ -42,10 +44,12 @@ internal sealed class TestProgressState(long id, string assembly, string? target public long Version { get; internal set; } - public List<(string? DisplayName, string? UID)> DiscoveredTests { get; internal set; } = []; + public List<(string? DisplayName, string? UID)> DiscoveredTestNames { get; internal set; } = []; public bool Success { get; internal set; } + public bool IsDiscovery = isDiscovery; + public int TryCount { get; private set; } private void ReportGenericTestResult( @@ -122,8 +126,8 @@ public void ReportFailedTest(string testNodeUid, string instanceId) public void DiscoverTest(string? displayName, string? uid) { - PassedTests++; - DiscoveredTests.Add(new(displayName, uid)); + DiscoveredTests++; + DiscoveredTestNames.Add(new(displayName, uid)); } internal void NotifyHandshake(string instanceId) diff --git a/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs b/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs index 257338656fc3..ac5b8bd408bb 100644 --- a/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs +++ b/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs @@ -18,7 +18,7 @@ public class TestProgressStateTests public void ReportSkippedTest_MultipleCalls_DifferentInstanceId() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); string testUid = "test1"; string instanceA = "instanceA"; string instanceB = "instanceB"; @@ -48,7 +48,7 @@ public void ReportSkippedTest_MultipleCalls_DifferentInstanceId() public void ReportSkippedTest_RepeatedInstanceAfterRetry_ThrowsInvalidOperationException() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); string testUid = "test1"; string instanceA = "instanceA"; string instanceB = "instanceB"; @@ -75,7 +75,7 @@ public void ReportSkippedTest_RepeatedInstanceAfterRetry_ThrowsInvalidOperationE public void ReportFailedTest_RepeatedCalls_IncrementsFailedTests(int callCount) { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); state.NotifyHandshake("instance1"); for (int i = 0; i < callCount; i++) { @@ -95,7 +95,7 @@ public void ReportFailedTest_RepeatedCalls_IncrementsFailedTests(int callCount) public void ReportFailedTest_DifferentInstanceId_RetriesFailureAndResetsCount() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); state.NotifyHandshake("id1"); state.ReportFailedTest("testUid", "id1"); state.ReportFailedTest("testUid", "id1"); @@ -114,7 +114,7 @@ public void ReportFailedTest_DifferentInstanceId_RetriesFailureAndResetsCount() public void ReportFailedTest_ReusingOldInstanceId_ThrowsInvalidOperationException() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); state.NotifyHandshake("id1"); state.ReportFailedTest("testUid", "id1"); state.NotifyHandshake("id2"); @@ -134,7 +134,7 @@ public void ReportFailedTest_ReusingOldInstanceId_ThrowsInvalidOperationExceptio public void ReportTest_WithNewInstanceId_ClearsOldReports() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); state.NotifyHandshake("id1"); state.ReportFailedTest("testUid", "id1"); state.ReportFailedTest("testUid", "id1"); @@ -173,14 +173,15 @@ public void DiscoverTest_DisplayNameAndUid_AddsEntryAndIncrementsPassedTests(str assembly: "assembly.dll", targetFramework: null, architecture: null, - stopwatch: stopwatchMock.Object); + stopwatch: stopwatchMock.Object, + isDiscovery: true); state.DiscoverTest(displayName, uid); state.PassedTests.Should().Be(1); - state.DiscoveredTests.Count.Should().Be(1); - state.DiscoveredTests[0].DisplayName.Should().Be(displayName); - state.DiscoveredTests[0].UID.Should().Be(uid); + state.DiscoveredTestNames.Count.Should().Be(1); + state.DiscoveredTestNames[0].DisplayName.Should().Be(displayName); + state.DiscoveredTestNames[0].UID.Should().Be(uid); } [Fact] @@ -188,7 +189,7 @@ public void FailedTestRetryShouldShouldShowTheSameTotalCountsInEachRetry() { // Tests are retried, total test count stays 3 to give use comparable counts, no matter how many times we retry. var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); // First run state.NotifyHandshake("run1"); @@ -227,7 +228,7 @@ public void FailedTestRetryShouldNotFailTheRunWhenSecondRunProducesLessDynamicTe { // This is special test for dynamic tests where we don't know how many tests will be produced in the second run. var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); // First run state.NotifyHandshake("run1"); @@ -259,7 +260,7 @@ public void FailedTestRetryShouldAccountPassedTestsInRetry() { // This is special test for dynamic tests where we cannot avoid re-running even non-failing tests from dynamic tests. var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); // First run state.NotifyHandshake("run1"); From c43583c4d815571351cb7a96da2f95683b4c64a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 18 Sep 2025 10:15:51 +0200 Subject: [PATCH 2/3] test fix --- test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs b/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs index ac5b8bd408bb..b55a7f2cdfb4 100644 --- a/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs +++ b/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs @@ -154,6 +154,7 @@ public void ReportTest_WithNewInstanceId_ClearsOldReports() state.SkippedTests.Should().Be(1); state.RetriedFailedTests.Should().Be(3); } + /// /// Tests that DiscoverTest increments PassedTests and adds the displayName and uid to DiscoveredTests. /// @@ -178,7 +179,7 @@ public void DiscoverTest_DisplayNameAndUid_AddsEntryAndIncrementsPassedTests(str state.DiscoverTest(displayName, uid); - state.PassedTests.Should().Be(1); + state.DiscoveredTests.Should().Be(1); state.DiscoveredTestNames.Count.Should().Be(1); state.DiscoveredTestNames[0].DisplayName.Should().Be(displayName); state.DiscoveredTestNames[0].UID.Should().Be(uid); From bd8cce36ecad8916d87ce0abc6fb81d3472c8a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Jare=C5=A1?= Date: Thu, 18 Sep 2025 11:13:12 +0200 Subject: [PATCH 3/3] revert slnx --- sdk.slnx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk.slnx b/sdk.slnx index c9ebd7d9e682..0246a040f447 100644 --- a/sdk.slnx +++ b/sdk.slnx @@ -54,6 +54,8 @@ + + @@ -316,6 +318,7 @@ +