From 40a0f946782bee3ca7c7788b32fb4d262b2b3394 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Mon, 5 Jan 2026 10:53:47 +0100 Subject: [PATCH 1/5] Handle assemblies gracefully --- .../Test/MTP/Terminal/TerminalTestReporter.cs | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs index b41c516cb27b..6d2a82f92a6c 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs @@ -404,7 +404,11 @@ internal void TestCompleted( string? standardOutput, string? errorOutput) { - TestProgressState asm = _assemblies[executionId]; + if (!_assemblies.TryGetValue(executionId, out TestProgressState? asm)) + { + // This should not happen in normal scenarios, but handle gracefully + return; + } var attempt = asm.TryCount; if (_options.ShowActiveTests) @@ -731,7 +735,21 @@ internal void AssemblyRunCompleted(string executionId, // In single process run, like with testing platform .exe we report these via messages, and run exit. int exitCode, string? outputData, string? errorData) { - TestProgressState assemblyRun = _assemblies[executionId]; + // The executionId may not exist in the dictionary if the host type is not "TestHost" + // (e.g., TestHostController), since AssemblyRunStarted is only called for TestHost. + if (!_assemblies.TryGetValue(executionId, out TestProgressState? assemblyRun)) + { + // No assembly run state to update, but still report on non-zero exit codes + if (exitCode != 0) + { + _terminalWithProgress.WriteToTerminal(terminal => + { + AppendExecutableSummary(terminal, exitCode, outputData, errorData); + }); + } + return; + } + assemblyRun.Success = exitCode == 0 && assemblyRun.FailedTests == 0; assemblyRun.Stopwatch.Stop(); @@ -903,7 +921,11 @@ internal void TestDiscovered( return; } - TestProgressState asm = _assemblies[executionId]; + if (!_assemblies.TryGetValue(executionId, out TestProgressState? asm)) + { + // This should not happen in normal scenarios, but handle gracefully + return; + } // TODO: add mode for discovered tests to the progress bar - jajares asm.DiscoverTest(displayName, uid); @@ -989,7 +1011,11 @@ public void TestInProgress( string displayName, string executionId) { - TestProgressState asm = _assemblies[executionId]; + if (!_assemblies.TryGetValue(executionId, out TestProgressState? asm)) + { + // This should not happen in normal scenarios, but handle gracefully + return; + } if (_options.ShowActiveTests) { From 90672464fef45c5e7fa56730c4cdb52571211502 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Thu, 15 Jan 2026 14:05:27 +0100 Subject: [PATCH 2/5] Call HandshakeFailure in case of testhost controller --- .../Test/MTP/Terminal/TerminalTestReporter.cs | 34 +++---------------- .../Test/MTP/TestApplicationHandler.cs | 13 ++++++- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs index 6d2a82f92a6c..b41c516cb27b 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs @@ -404,11 +404,7 @@ internal void TestCompleted( string? standardOutput, string? errorOutput) { - if (!_assemblies.TryGetValue(executionId, out TestProgressState? asm)) - { - // This should not happen in normal scenarios, but handle gracefully - return; - } + TestProgressState asm = _assemblies[executionId]; var attempt = asm.TryCount; if (_options.ShowActiveTests) @@ -735,21 +731,7 @@ internal void AssemblyRunCompleted(string executionId, // In single process run, like with testing platform .exe we report these via messages, and run exit. int exitCode, string? outputData, string? errorData) { - // The executionId may not exist in the dictionary if the host type is not "TestHost" - // (e.g., TestHostController), since AssemblyRunStarted is only called for TestHost. - if (!_assemblies.TryGetValue(executionId, out TestProgressState? assemblyRun)) - { - // No assembly run state to update, but still report on non-zero exit codes - if (exitCode != 0) - { - _terminalWithProgress.WriteToTerminal(terminal => - { - AppendExecutableSummary(terminal, exitCode, outputData, errorData); - }); - } - return; - } - + TestProgressState assemblyRun = _assemblies[executionId]; assemblyRun.Success = exitCode == 0 && assemblyRun.FailedTests == 0; assemblyRun.Stopwatch.Stop(); @@ -921,11 +903,7 @@ internal void TestDiscovered( return; } - if (!_assemblies.TryGetValue(executionId, out TestProgressState? asm)) - { - // This should not happen in normal scenarios, but handle gracefully - return; - } + TestProgressState asm = _assemblies[executionId]; // TODO: add mode for discovered tests to the progress bar - jajares asm.DiscoverTest(displayName, uid); @@ -1011,11 +989,7 @@ public void TestInProgress( string displayName, string executionId) { - if (!_assemblies.TryGetValue(executionId, out TestProgressState? asm)) - { - // This should not happen in normal scenarios, but handle gracefully - return; - } + TestProgressState asm = _assemblies[executionId]; if (_options.ShowActiveTests) { diff --git a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs index 8db706d7c382..839ae1ea6c08 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs @@ -15,6 +15,7 @@ internal sealed class TestApplicationHandler private readonly Dictionary _testSessionEventCountPerSessionUid = new(); private (string? TargetFramework, string? Architecture, string ExecutionId)? _handshakeInfo; + private bool _receivedTestHostHandshake; public TestApplicationHandler(TerminalTestReporter output, TestModule module, TestOptions options) { @@ -62,6 +63,7 @@ internal void OnHandshakeReceived(HandshakeMessage handshakeMessage, bool gotSup // https://github.com/microsoft/testfx/blob/2a9a353ec2bb4ce403f72e8ba1f29e01e7cf1fd4/src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs#L87-L97 if (hostType == "TestHost") { + _receivedTestHostHandshake = true; // AssemblyRunStarted counts "retry count", and writes to terminal "(Try ) Running tests from " // So, we want to call it only for test host, and not for test host controller (or orchestrator, if in future it will handshake as well) // Calling it for both test host and test host controllers means we will count retries incorrectly, and will messages twice. @@ -265,7 +267,16 @@ internal void OnTestProcessExited(int exitCode, string outputData, string errorD { if (_handshakeInfo.HasValue) { - _output.AssemblyRunCompleted(_handshakeInfo.Value.ExecutionId, exitCode, outputData, errorData); + // If we received a handshake from TestHostController but not from TestHost, + // call HandshakeFailure instead of AssemblyRunCompleted + if (!_receivedTestHostHandshake) + { + _output.HandshakeFailure(_module.TargetPath ?? _module.ProjectFullPath ?? string.Empty, _module.TargetFramework, exitCode, outputData, errorData); + } + else + { + _output.AssemblyRunCompleted(_handshakeInfo.Value.ExecutionId, exitCode, outputData, errorData); + } } else { From f09583147709a18fbdf9b71c18280a9172b3866c Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:50:13 +0100 Subject: [PATCH 3/5] Update src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs Co-authored-by: Youssef Victor --- src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs index 839ae1ea6c08..7e589859fd98 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs @@ -265,7 +265,7 @@ internal bool HasMismatchingTestSessionEventCount() internal void OnTestProcessExited(int exitCode, string outputData, string errorData) { - if (_handshakeInfo.HasValue) + if (_receivedTestHostHandshake) { // If we received a handshake from TestHostController but not from TestHost, // call HandshakeFailure instead of AssemblyRunCompleted From 770c8f8d6a6324b8b15dbc161e57543c86b44b14 Mon Sep 17 00:00:00 2001 From: Mariam Abdullah <122357303+mariam-abdulla@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:50:22 +0100 Subject: [PATCH 4/5] Update src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs Co-authored-by: Youssef Victor --- .../dotnet/Commands/Test/MTP/TestApplicationHandler.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs index 7e589859fd98..7d7c8f5591c5 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs @@ -269,14 +269,7 @@ internal void OnTestProcessExited(int exitCode, string outputData, string errorD { // If we received a handshake from TestHostController but not from TestHost, // call HandshakeFailure instead of AssemblyRunCompleted - if (!_receivedTestHostHandshake) - { - _output.HandshakeFailure(_module.TargetPath ?? _module.ProjectFullPath ?? string.Empty, _module.TargetFramework, exitCode, outputData, errorData); - } - else - { - _output.AssemblyRunCompleted(_handshakeInfo.Value.ExecutionId, exitCode, outputData, errorData); - } + _output.AssemblyRunCompleted(_handshakeInfo.Value.ExecutionId, exitCode, outputData, errorData); } else { From 6112f4ab2a18cebee16a5b7f787edaf01e7b4c79 Mon Sep 17 00:00:00 2001 From: mariam-abdulla Date: Thu, 15 Jan 2026 15:09:46 +0100 Subject: [PATCH 5/5] Add check of _handshakeInfo --- src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs index 7d7c8f5591c5..9735d0ab72b2 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/TestApplicationHandler.cs @@ -265,7 +265,7 @@ internal bool HasMismatchingTestSessionEventCount() internal void OnTestProcessExited(int exitCode, string outputData, string errorData) { - if (_receivedTestHostHandshake) + if (_receivedTestHostHandshake && _handshakeInfo.HasValue) { // If we received a handshake from TestHostController but not from TestHost, // call HandshakeFailure instead of AssemblyRunCompleted