@@ -39,21 +39,29 @@ public async Task DotnetTestPipe_Orchestrator_HandshakesWithFeature()
3939 environmentVariables : new ( ) { { EnvironmentVariableConstants . TESTINGPLATFORM_TELEMETRY_OPTOUT , "1" } } ,
4040 cancellationToken : TestContext . CancellationToken ) ;
4141
42- // The orchestrator process and the single (passing) test host it spawned should both have
43- // connected and handshaked .
42+ // The orchestrator process and both test hosts it spawned should have connected and handshaked.
43+ // The first host reports a failure, causing the retry orchestrator to launch a second host that passes .
4444 Dictionary < byte , string > orchestratorHandshake = Assert . ContainsSingle ( result . HandshakesWithHostType ( TestHostOrchestratorHostType ) ) ;
4545
4646 Assert . IsTrue (
4747 orchestratorHandshake . TryGetValue ( DotnetTestPipeProtocol . HandshakeProperties . OrchestratorFeature , out string ? feature ) ,
4848 "Orchestrator handshake is missing the OrchestratorFeature property." ) ;
4949 Assert . AreEqual ( RetryOrchestratorFeature , feature ) ;
50+ Assert . AreEqual ( "1" , orchestratorHandshake [ DotnetTestPipeProtocol . HandshakeProperties . AttemptNumber ] ) ;
5051
51- // The actual test host (spawned by the orchestrator) handshakes as a plain TestHost and must
52- // NOT carry an orchestrator feature.
53- Dictionary < byte , string > testHostHandshake = Assert . ContainsSingle ( result . HandshakesWithHostType ( TestHostHostType ) ) ;
54- Assert . IsFalse (
55- testHostHandshake . ContainsKey ( DotnetTestPipeProtocol . HandshakeProperties . OrchestratorFeature ) ,
56- "Test host handshake should not carry the OrchestratorFeature property." ) ;
52+ // The test hosts handshake as plain TestHosts and must not carry an orchestrator feature. Their explicit
53+ // attempt numbers let dotnet test attribute results without inferring attempts from InstanceId changes.
54+ Dictionary < byte , string > [ ] testHostHandshakes = [ .. result . HandshakesWithHostType ( TestHostHostType ) ] ;
55+ Assert . HasCount ( 2 , testHostHandshakes ) ;
56+ Assert . IsTrue (
57+ testHostHandshakes . All ( handshake => ! handshake . ContainsKey ( DotnetTestPipeProtocol . HandshakeProperties . OrchestratorFeature ) ) ,
58+ "Test host handshakes should not carry the OrchestratorFeature property." ) ;
59+ Assert . AreSequenceEqual (
60+ new [ ] { "1" , "2" } ,
61+ testHostHandshakes
62+ . Select ( handshake => handshake [ DotnetTestPipeProtocol . HandshakeProperties . AttemptNumber ] )
63+ . Order ( )
64+ . ToArray ( ) ) ;
5765 }
5866
5967 public sealed class TestAssetFixture ( ) : TestAssetFixtureBase ( )
@@ -80,7 +88,9 @@ public sealed class TestAssetFixture() : TestAssetFixtureBase()
8088using Microsoft.Testing.Extensions;
8189using Microsoft.Testing.Platform.Builder;
8290using Microsoft.Testing.Platform.Capabilities.TestFramework;
91+ using Microsoft.Testing.Platform.Extensions.Messages;
8392using Microsoft.Testing.Platform.Extensions.TestFramework;
93+ using Microsoft.Testing.Platform.Messages;
8494
8595public class Program
8696{
@@ -94,21 +104,30 @@ public static async Task<int> Main(string[] args)
94104 }
95105}
96106
97- public class DummyTestFramework : ITestFramework
107+ public class DummyTestFramework : ITestFramework, IDataProducer
98108{
99109 public string Uid => nameof(DummyTestFramework);
100110 public string Version => "2.0.0";
101111 public string DisplayName => nameof(DummyTestFramework);
102112 public string Description => nameof(DummyTestFramework);
113+ public Type[] DataTypesProduced => [typeof(TestNodeUpdateMessage)];
103114 public Task<bool> IsEnabledAsync() => Task.FromResult(true);
104115 public Task<CreateTestSessionResult> CreateTestSessionAsync(CreateTestSessionContext context)
105116 => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true });
106117 public Task<CloseTestSessionResult> CloseTestSessionAsync(CloseTestSessionContext context)
107118 => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true });
108- public Task ExecuteRequestAsync(ExecuteRequestContext context)
119+ public async Task ExecuteRequestAsync(ExecuteRequestContext context)
109120 {
121+ string? attemptNumber = Environment.GetEnvironmentVariable("TESTINGPLATFORM_DOTNETTEST_ATTEMPTNUMBER");
122+ TestNodeStateProperty result = attemptNumber == "1"
123+ ? new FailedTestNodeStateProperty("Fail the first attempt to exercise retry.")
124+ : PassedTestNodeStateProperty.CachedInstance;
125+
126+ await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(
127+ context.Request.Session.SessionUid,
128+ new TestNode() { Uid = "test-1", DisplayName = "RetryTest", Properties = new(result) }));
129+
110130 context.Complete();
111- return Task.CompletedTask;
112131 }
113132}
114133""" ;
0 commit comments