diff --git a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs index 6608b34b0e..81a3151552 100644 --- a/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs +++ b/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs @@ -167,11 +167,13 @@ public static TestNode ToTestNode(this TestResult testResult, bool isTrxEnabled, if (testResultMessage.Category == TestResultMessage.StandardErrorCategory) { testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardError", testResultMessage.Text ?? string.Empty)); + testNode.Properties.Add(new StandardErrorProperty(testResultMessage.Text ?? string.Empty)); } if (testResultMessage.Category == TestResultMessage.StandardOutCategory) { testNode.Properties.Add(new SerializableKeyValuePairStringProperty("vstest.TestCase.StandardOutput", testResultMessage.Text ?? string.Empty)); + testNode.Properties.Add(new StandardOutputProperty(testResultMessage.Text ?? string.Empty)); } } diff --git a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs index 013add7468..2b0df73190 100644 --- a/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/IPC/Serializers/BaseSerializer.cs @@ -309,6 +309,18 @@ protected static void WriteField(Stream stream, ushort id, string? value) WriteStringValue(stream, value); } + protected static void WriteField(Stream stream, ushort id, long? value) + { + if (value is null) + { + return; + } + + WriteShort(stream, id); + WriteSize(stream); + WriteLong(stream, value.Value); + } + protected static void WriteField(Stream stream, string? value) { if (value is null) @@ -370,6 +382,7 @@ protected static void WriteAtPosition(Stream stream, int value, long position) Type type when type == typeof(short) => sizeof(short), Type type when type == typeof(bool) => sizeof(bool), Type type when type == typeof(byte) => sizeof(byte), + Type type when type == typeof(long) => sizeof(long), _ => 0, }; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs index bed1ee7671..10fe6862e0 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/DotnetTestDataConsumer.cs @@ -47,9 +47,9 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella { case TestNodeUpdateMessage testNodeUpdateMessage: - GetTestNodeDetails(testNodeUpdateMessage, out byte? state, out string? reason, out string? errorMessage, out string? errorStackTrace); + TestNodeDetails testNodeDetails = GetTestNodeDetails(testNodeUpdateMessage); - switch (state) + switch (testNodeDetails.State) { case TestStates.Discovered: DiscoveredTestMessages discoveredTestMessages = new( @@ -73,8 +73,11 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella new SuccessfulTestResultMessage( testNodeUpdateMessage.TestNode.Uid.Value, testNodeUpdateMessage.TestNode.DisplayName, - state, - reason ?? string.Empty, + testNodeDetails.State, + testNodeDetails.Duration, + testNodeDetails.Reason ?? string.Empty, + testNodeDetails.StandardOutput ?? string.Empty, + testNodeDetails.StandardError ?? string.Empty, testNodeUpdateMessage.SessionUid.Value), }, Array.Empty()); @@ -91,10 +94,13 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella new FailedTestResultMessage( testNodeUpdateMessage.TestNode.Uid.Value, testNodeUpdateMessage.TestNode.DisplayName, - state, - reason ?? string.Empty, - errorMessage ?? string.Empty, - errorStackTrace ?? string.Empty, + testNodeDetails.State, + testNodeDetails.Duration, + testNodeDetails.Reason ?? string.Empty, + testNodeDetails.ErrorMessage ?? string.Empty, + testNodeDetails.ErrorStackTrace ?? string.Empty, + testNodeDetails.StandardOutput ?? string.Empty, + testNodeDetails.StandardError ?? string.Empty, testNodeUpdateMessage.SessionUid.Value), }); @@ -157,13 +163,17 @@ public async Task ConsumeAsync(IDataProducer dataProducer, IData value, Cancella } } - private static void GetTestNodeDetails(TestNodeUpdateMessage testNodeUpdateMessage, out byte? state, out string? reason, out string? errorMessage, out string? errorStackTrace) + private static TestNodeDetails GetTestNodeDetails(TestNodeUpdateMessage testNodeUpdateMessage) { - state = null; - reason = string.Empty; - errorMessage = string.Empty; - errorStackTrace = string.Empty; + byte? state = null; + long? duration = null; + string? reason = string.Empty; + string? errorMessage = string.Empty; + string? errorStackTrace = string.Empty; + TestNodeStateProperty nodeState = testNodeUpdateMessage.TestNode.Properties.Single(); + string? standardOutput = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.StandardOutput; + string? standardError = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.StandardError; switch (nodeState) { @@ -173,6 +183,7 @@ private static void GetTestNodeDetails(TestNodeUpdateMessage testNodeUpdateMessa case PassedTestNodeStateProperty: state = TestStates.Passed; + duration = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.GlobalTiming.Duration.Ticks; reason = nodeState.Explanation; break; @@ -183,6 +194,7 @@ private static void GetTestNodeDetails(TestNodeUpdateMessage testNodeUpdateMessa case FailedTestNodeStateProperty failedTestNodeStateProperty: state = TestStates.Failed; + duration = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.GlobalTiming.Duration.Ticks; reason = nodeState.Explanation; errorMessage = failedTestNodeStateProperty.Exception?.Message; errorStackTrace = failedTestNodeStateProperty.Exception?.StackTrace; @@ -190,6 +202,7 @@ private static void GetTestNodeDetails(TestNodeUpdateMessage testNodeUpdateMessa case ErrorTestNodeStateProperty errorTestNodeStateProperty: state = TestStates.Error; + duration = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.GlobalTiming.Duration.Ticks; reason = nodeState.Explanation; errorMessage = errorTestNodeStateProperty.Exception?.Message; errorStackTrace = errorTestNodeStateProperty.Exception?.StackTrace; @@ -197,6 +210,7 @@ private static void GetTestNodeDetails(TestNodeUpdateMessage testNodeUpdateMessa case TimeoutTestNodeStateProperty timeoutTestNodeStateProperty: state = TestStates.Timeout; + duration = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.GlobalTiming.Duration.Ticks; reason = nodeState.Explanation; errorMessage = timeoutTestNodeStateProperty.Exception?.Message; errorStackTrace = timeoutTestNodeStateProperty.Exception?.StackTrace; @@ -204,13 +218,18 @@ private static void GetTestNodeDetails(TestNodeUpdateMessage testNodeUpdateMessa case CancelledTestNodeStateProperty cancelledTestNodeStateProperty: state = TestStates.Cancelled; + duration = testNodeUpdateMessage.TestNode.Properties.SingleOrDefault()?.GlobalTiming.Duration.Ticks; reason = nodeState.Explanation; errorMessage = cancelledTestNodeStateProperty.Exception?.Message; errorStackTrace = cancelledTestNodeStateProperty.Exception?.StackTrace; break; } + + return new TestNodeDetails(state, duration, reason, errorMessage, errorStackTrace, standardOutput, standardError); } + public record TestNodeDetails(byte? State, long? Duration, string? Reason, string? ErrorMessage, string? ErrorStackTrace, string? StandardOutput, string? StandardError); + public Task IsEnabledAsync() => Task.FromResult(true); public async Task OnTestSessionStartingAsync(SessionUid sessionUid, CancellationToken cancellationToken) diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/TestResultMessages.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/TestResultMessages.cs index 2d1411c01c..e7c82a4fac 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/TestResultMessages.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/TestResultMessages.cs @@ -3,8 +3,8 @@ namespace Microsoft.Testing.Platform.IPC.Models; -internal sealed record SuccessfulTestResultMessage(string? Uid, string? DisplayName, byte? State, string? Reason, string? SessionUid); +internal sealed record SuccessfulTestResultMessage(string? Uid, string? DisplayName, byte? State, long? Duration, string? Reason, string? StandardOutput, string? ErrorOutput, string? SessionUid); -internal sealed record FailedTestResultMessage(string? Uid, string? DisplayName, byte? State, string? Reason, string? ErrorMessage, string? ErrorStackTrace, string? SessionUid); +internal sealed record FailedTestResultMessage(string? Uid, string? DisplayName, byte? State, long? Duration, string? Reason, string? ErrorMessage, string? ErrorStackTrace, string? StandardOutput, string? ErrorOutput, string? SessionUid); internal sealed record TestResultMessages(string? ExecutionId, SuccessfulTestResultMessage[] SuccessfulTestMessages, FailedTestResultMessage[] FailedTestMessages) : IRequest; diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs index 72f00138dd..e143ef3b06 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs @@ -66,8 +66,11 @@ internal static class SuccessfulTestResultMessageFieldsId public const ushort Uid = 1; public const ushort DisplayName = 2; public const ushort State = 3; - public const ushort Reason = 4; - public const ushort SessionUid = 5; + public const ushort Duration = 4; + public const ushort Reason = 5; + public const ushort StandardOutput = 6; + public const ushort ErrorOutput = 7; + public const ushort SessionUid = 8; } internal static class FailedTestResultMessageFieldsId @@ -75,10 +78,13 @@ internal static class FailedTestResultMessageFieldsId public const ushort Uid = 1; public const ushort DisplayName = 2; public const ushort State = 3; - public const ushort Reason = 4; - public const ushort ErrorMessage = 5; - public const ushort ErrorStackTrace = 6; - public const ushort SessionUid = 7; + public const ushort Duration = 4; + public const ushort Reason = 5; + public const ushort ErrorMessage = 6; + public const ushort ErrorStackTrace = 7; + public const ushort StandardOutput = 8; + public const ushort ErrorOutput = 9; + public const ushort SessionUid = 10; } internal static class FileArtifactMessagesFieldsId diff --git a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestResultMessagesSerializer.cs b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestResultMessagesSerializer.cs index ef94f25bab..6707272cd7 100644 --- a/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestResultMessagesSerializer.cs +++ b/src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/TestResultMessagesSerializer.cs @@ -28,13 +28,25 @@ namespace Microsoft.Testing.Platform.IPC.Serializers; |---SuccessfulTestMessageList[0].DisplayName Value---| (n bytes) |---SuccessfulTestMessageList[0].State Id---| (2 bytes) - |---SuccessfulTestMessageList[0].State Size---| (4 bytes) + |---SuccessfulTestMessageList[0].State Size---| (1 byte) |---SuccessfulTestMessageList[0].State Value---| (n bytes) + |---SuccessfulTestMessageList[0].Duration Id---| (2 bytes) + |---SuccessfulTestMessageList[0].Duration Size---| (8 bytes) + |---SuccessfulTestMessageList[0].Duration Value---| (n bytes) + |---SuccessfulTestMessageList[0].Reason Id---| (2 bytes) |---SuccessfulTestMessageList[0].Reason Size---| (4 bytes) |---SuccessfulTestMessageList[0].Reason Value---| (n bytes) + |---SuccessfulTestMessageList[0].StandardOutput Id---| (2 bytes) + |---SuccessfulTestMessageList[0].StandardOutput Size---| (4 bytes) + |---SuccessfulTestMessageList[0].StandardOutput Value---| (n bytes) + + |---SuccessfulTestMessageList[0].StandardError Id---| (2 bytes) + |---SuccessfulTestMessageList[0].StandardError Size---| (4 bytes) + |---SuccessfulTestMessageList[0].StandardError Value---| (n bytes) + |---SuccessfulTestMessageList[0].SessionUid Id---| (2 bytes) |---SuccessfulTestMessageList[0].SessionUid Size---| (4 bytes) |---SuccessfulTestMessageList[0].SessionUid Value---| (n bytes) @@ -55,9 +67,13 @@ namespace Microsoft.Testing.Platform.IPC.Serializers; |---FailedTestMessageList[0].DisplayName Value---| (n bytes) |---FailedTestMessageList[0].State Id---| (2 bytes) - |---FailedTestMessageList[0].State Size---| (4 bytes) + |---FailedTestMessageList[0].State Size---| (1 byte) |---FailedTestMessageList[0].State Value---| (n bytes) + |---SuccessfulTestMessageList[0].Duration Id---| (2 bytes) + |---SuccessfulTestMessageList[0].Duration Size---| (8 bytes) + |---SuccessfulTestMessageList[0].Duration Value---| (n bytes) + |---FailedTestMessageList[0].Reason Id---| (2 bytes) |---FailedTestMessageList[0].Reason Size---| (4 bytes) |---FailedTestMessageList[0].Reason Value---| (n bytes) @@ -70,6 +86,14 @@ namespace Microsoft.Testing.Platform.IPC.Serializers; |---FailedTestMessageList[0].ErrorStackTrace Size---| (4 bytes) |---FailedTestMessageList[0].ErrorStackTrace Value---| (n bytes) + |---SuccessfulTestMessageList[0].StandardOutput Id---| (2 bytes) + |---SuccessfulTestMessageList[0].StandardOutput Size---| (4 bytes) + |---SuccessfulTestMessageList[0].StandardOutput Value---| (n bytes) + + |---SuccessfulTestMessageList[0].StandardError Id---| (2 bytes) + |---SuccessfulTestMessageList[0].StandardError Size---| (4 bytes) + |---SuccessfulTestMessageList[0].StandardError Value---| (n bytes) + |---FailedTestMessageList[0].SessionUid Id---| (2 bytes) |---FailedTestMessageList[0].SessionUid Size---| (4 bytes) |---FailedTestMessageList[0].SessionUid Value---| (n bytes) @@ -126,8 +150,9 @@ private static List ReadSuccessfulTestMessagesPaylo int length = ReadInt(stream); for (int i = 0; i < length; i++) { - string? uid = null, displayName = null, reason = null, sessionUid = null; + string? uid = null, displayName = null, reason = null, standardOutput = null, errorOutput = null, sessionUid = null; byte? state = null; + long? duration = null; int fieldCount = ReadShort(stream); @@ -150,10 +175,22 @@ private static List ReadSuccessfulTestMessagesPaylo state = ReadByte(stream); break; + case SuccessfulTestResultMessageFieldsId.Duration: + duration = ReadLong(stream); + break; + case SuccessfulTestResultMessageFieldsId.Reason: reason = ReadStringValue(stream, fieldSize); break; + case SuccessfulTestResultMessageFieldsId.StandardOutput: + standardOutput = ReadStringValue(stream, fieldSize); + break; + + case SuccessfulTestResultMessageFieldsId.ErrorOutput: + errorOutput = ReadStringValue(stream, fieldSize); + break; + case SuccessfulTestResultMessageFieldsId.SessionUid: sessionUid = ReadStringValue(stream, fieldSize); break; @@ -164,7 +201,7 @@ private static List ReadSuccessfulTestMessagesPaylo } } - successfulTestResultMessages.Add(new SuccessfulTestResultMessage(uid, displayName, state, reason, sessionUid)); + successfulTestResultMessages.Add(new SuccessfulTestResultMessage(uid, displayName, state, duration, reason, standardOutput, errorOutput, sessionUid)); } return successfulTestResultMessages; @@ -177,8 +214,10 @@ private static List ReadFailedTestMessagesPayload(Strea int length = ReadInt(stream); for (int i = 0; i < length; i++) { - string? uid = null, displayName = null, reason = null, sessionUid = null, errorMessage = null, errorStackTrace = null; + string? uid = null, displayName = null, reason = null, sessionUid = null, + errorMessage = null, errorStackTrace = null, standardOutput = null, errorOutput = null; byte? state = null; + long? duration = null; int fieldCount = ReadShort(stream); @@ -201,6 +240,10 @@ private static List ReadFailedTestMessagesPayload(Strea state = ReadByte(stream); break; + case FailedTestResultMessageFieldsId.Duration: + duration = ReadLong(stream); + break; + case FailedTestResultMessageFieldsId.Reason: reason = ReadStringValue(stream, fieldSize); break; @@ -213,6 +256,14 @@ private static List ReadFailedTestMessagesPayload(Strea errorStackTrace = ReadStringValue(stream, fieldSize); break; + case FailedTestResultMessageFieldsId.StandardOutput: + standardOutput = ReadStringValue(stream, fieldSize); + break; + + case FailedTestResultMessageFieldsId.ErrorOutput: + errorOutput = ReadStringValue(stream, fieldSize); + break; + case FailedTestResultMessageFieldsId.SessionUid: sessionUid = ReadStringValue(stream, fieldSize); break; @@ -223,7 +274,7 @@ private static List ReadFailedTestMessagesPayload(Strea } } - failedTestResultMessages.Add(new FailedTestResultMessage(uid, displayName, state, reason, errorMessage, errorStackTrace, sessionUid)); + failedTestResultMessages.Add(new FailedTestResultMessage(uid, displayName, state, duration, reason, errorMessage, errorStackTrace, standardOutput, errorOutput, sessionUid)); } return failedTestResultMessages; @@ -264,7 +315,10 @@ private static void WriteSuccessfulTestMessagesPayload(Stream stream, Successful WriteField(stream, SuccessfulTestResultMessageFieldsId.Uid, successfulTestResultMessage.Uid); WriteField(stream, SuccessfulTestResultMessageFieldsId.DisplayName, successfulTestResultMessage.DisplayName); WriteField(stream, SuccessfulTestResultMessageFieldsId.State, successfulTestResultMessage.State); + WriteField(stream, SuccessfulTestResultMessageFieldsId.Duration, successfulTestResultMessage.Duration); WriteField(stream, SuccessfulTestResultMessageFieldsId.Reason, successfulTestResultMessage.Reason); + WriteField(stream, SuccessfulTestResultMessageFieldsId.StandardOutput, successfulTestResultMessage.StandardOutput); + WriteField(stream, SuccessfulTestResultMessageFieldsId.ErrorOutput, successfulTestResultMessage.ErrorOutput); WriteField(stream, SuccessfulTestResultMessageFieldsId.SessionUid, successfulTestResultMessage.SessionUid); } @@ -295,9 +349,12 @@ private static void WriteFailedTestMessagesPayload(Stream stream, FailedTestResu WriteField(stream, FailedTestResultMessageFieldsId.Uid, failedTestResultMessage.Uid); WriteField(stream, FailedTestResultMessageFieldsId.DisplayName, failedTestResultMessage.DisplayName); WriteField(stream, FailedTestResultMessageFieldsId.State, failedTestResultMessage.State); + WriteField(stream, FailedTestResultMessageFieldsId.Duration, failedTestResultMessage.Duration); WriteField(stream, FailedTestResultMessageFieldsId.Reason, failedTestResultMessage.Reason); WriteField(stream, FailedTestResultMessageFieldsId.ErrorMessage, failedTestResultMessage.ErrorMessage); WriteField(stream, FailedTestResultMessageFieldsId.ErrorStackTrace, failedTestResultMessage.ErrorStackTrace); + WriteField(stream, FailedTestResultMessageFieldsId.StandardOutput, failedTestResultMessage.StandardOutput); + WriteField(stream, FailedTestResultMessageFieldsId.ErrorOutput, failedTestResultMessage.ErrorOutput); WriteField(stream, FailedTestResultMessageFieldsId.SessionUid, failedTestResultMessage.SessionUid); } @@ -315,15 +372,21 @@ private static ushort GetFieldCount(SuccessfulTestResultMessage successfulTestRe (ushort)((successfulTestResultMessage.Uid is null ? 0 : 1) + (successfulTestResultMessage.DisplayName is null ? 0 : 1) + (successfulTestResultMessage.State is null ? 0 : 1) + + (successfulTestResultMessage.Duration is null ? 0 : 1) + (successfulTestResultMessage.Reason is null ? 0 : 1) + + (successfulTestResultMessage.StandardOutput is null ? 0 : 1) + + (successfulTestResultMessage.ErrorOutput is null ? 0 : 1) + (successfulTestResultMessage.SessionUid is null ? 0 : 1)); private static ushort GetFieldCount(FailedTestResultMessage failedTestResultMessage) => (ushort)((failedTestResultMessage.Uid is null ? 0 : 1) + (failedTestResultMessage.DisplayName is null ? 0 : 1) + (failedTestResultMessage.State is null ? 0 : 1) + + (failedTestResultMessage.Duration is null ? 0 : 1) + (failedTestResultMessage.Reason is null ? 0 : 1) + (failedTestResultMessage.ErrorMessage is null ? 0 : 1) + (failedTestResultMessage.ErrorStackTrace is null ? 0 : 1) + + (failedTestResultMessage.StandardOutput is null ? 0 : 1) + + (failedTestResultMessage.ErrorOutput is null ? 0 : 1) + (failedTestResultMessage.SessionUid is null ? 0 : 1)); }