|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using Microsoft.DotNet.Cli.Commands.Test.IPC.Models;
namespace Microsoft.DotNet.Cli.Commands.Test.IPC.Serializers;
/*
|---FieldCount---| 2 bytes
|---ExecutionId Id---| (2 bytes)
|---ExecutionId Size---| (4 bytes)
|---ExecutionId Value---| (n bytes)
|---InstanceId Id---| (2 bytes)
|---InstanceId Size---| (4 bytes)
|---InstanceId Value---| (n bytes)
|---SuccessfulTestMessageList Id---| (2 bytes)
|---SuccessfulTestMessageList Size---| (4 bytes)
|---SuccessfulTestMessageList Value---| (n bytes)
|---SuccessfulTestMessageList Length---| (4 bytes)
|---SuccessfulTestMessageList[0] FieldCount---| 2 bytes
|---SuccessfulTestMessageList[0].Uid Id---| (2 bytes)
|---SuccessfulTestMessageList[0].Uid Size---| (4 bytes)
|---SuccessfulTestMessageList[0].Uid Value---| (n bytes)
|---SuccessfulTestMessageList[0].DisplayName Id---| (2 bytes)
|---SuccessfulTestMessageList[0].DisplayName Size---| (4 bytes)
|---SuccessfulTestMessageList[0].DisplayName Value---| (n bytes)
|---SuccessfulTestMessageList[0].State Id---| (2 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)
|---FailedTestMessageList Id---| (2 bytes)
|---FailedTestMessageList Size---| (4 bytes)
|---FailedTestMessageList Value---| (n bytes)
|---FailedTestMessageList Length---| (4 bytes)
|---FailedTestMessageList[0] FieldCount---| 2 bytes
|---FailedTestMessageList[0].Uid Id---| (2 bytes)
|---FailedTestMessageList[0].Uid Size---| (4 bytes)
|---FailedTestMessageList[0].Uid Value---| (n bytes)
|---FailedTestMessageList[0].DisplayName Id---| (2 bytes)
|---FailedTestMessageList[0].DisplayName Size---| (4 bytes)
|---FailedTestMessageList[0].DisplayName Value---| (n bytes)
|---FailedTestMessageList[0].State Id---| (2 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)
|---FailedTestMessageList[0].ErrorMessage Id---| (2 bytes)
|---FailedTestMessageList[0].ErrorMessage Size---| (4 bytes)
|---FailedTestMessageList[0].ErrorMessage Value---| (n bytes)
|---FailedTestMessageList[0].ErrorStackTrace Id---| (2 bytes)
|---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)
*/
internal sealed class TestResultMessagesSerializer : BaseSerializer, INamedPipeSerializer
{
public int Id => TestResultMessagesFieldsId.MessagesSerializerId;
public object Deserialize(Stream stream)
{
string? executionId = null;
string? instanceId = null;
List<SuccessfulTestResultMessage>? successfulTestResultMessages = null;
List<FailedTestResultMessage>? failedTestResultMessages = null;
ushort fieldCount = ReadUShort(stream);
for (int i = 0; i < fieldCount; i++)
{
int fieldId = ReadUShort(stream);
int fieldSize = ReadInt(stream);
switch (fieldId)
{
case TestResultMessagesFieldsId.ExecutionId:
executionId = ReadStringValue(stream, fieldSize);
break;
case TestResultMessagesFieldsId.InstanceId:
instanceId = ReadStringValue(stream, fieldSize);
break;
case TestResultMessagesFieldsId.SuccessfulTestMessageList:
successfulTestResultMessages = ReadSuccessfulTestMessagesPayload(stream);
break;
case TestResultMessagesFieldsId.FailedTestMessageList:
failedTestResultMessages = ReadFailedTestMessagesPayload(stream);
break;
default:
// If we don't recognize the field id, skip the payload corresponding to that field
SetPosition(stream, stream.Position + fieldSize);
break;
}
}
return new TestResultMessages(
executionId,
instanceId,
successfulTestResultMessages is null ? [] : [.. successfulTestResultMessages],
failedTestResultMessages is null ? [] : [.. failedTestResultMessages]);
}
private static List<SuccessfulTestResultMessage> ReadSuccessfulTestMessagesPayload(Stream stream)
{
List<SuccessfulTestResultMessage> successfulTestResultMessages = [];
int length = ReadInt(stream);
for (int i = 0; i < length; i++)
{
string? uid = null, displayName = null, reason = null, standardOutput = null, errorOutput = null, sessionUid = null;
byte? state = null;
long? duration = null;
int fieldCount = ReadUShort(stream);
for (int j = 0; j < fieldCount; j++)
{
int fieldId = ReadUShort(stream);
int fieldSize = ReadInt(stream);
switch (fieldId)
{
case SuccessfulTestResultMessageFieldsId.Uid:
uid = ReadStringValue(stream, fieldSize);
break;
case SuccessfulTestResultMessageFieldsId.DisplayName:
displayName = ReadStringValue(stream, fieldSize);
break;
case SuccessfulTestResultMessageFieldsId.State:
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;
default:
SetPosition(stream, stream.Position + fieldSize);
break;
}
}
successfulTestResultMessages.Add(new SuccessfulTestResultMessage(uid, displayName, state, duration, reason, standardOutput, errorOutput, sessionUid));
}
return successfulTestResultMessages;
}
private static List<FailedTestResultMessage> ReadFailedTestMessagesPayload(Stream stream)
{
List<FailedTestResultMessage> failedTestResultMessages = [];
int length = ReadInt(stream);
for (int i = 0; i < length; i++)
{
string? uid = null, displayName = null, reason = null, sessionUid = null, standardOutput = null, errorOutput = null;
ExceptionMessage[] exceptionMessages = [];
byte? state = null;
long? duration = null;
int fieldCount = ReadUShort(stream);
for (int j = 0; j < fieldCount; j++)
{
int fieldId = ReadUShort(stream);
int fieldSize = ReadInt(stream);
switch (fieldId)
{
case FailedTestResultMessageFieldsId.Uid:
uid = ReadStringValue(stream, fieldSize);
break;
case FailedTestResultMessageFieldsId.DisplayName:
displayName = ReadStringValue(stream, fieldSize);
break;
case FailedTestResultMessageFieldsId.State:
state = ReadByte(stream);
break;
case FailedTestResultMessageFieldsId.Duration:
duration = ReadLong(stream);
break;
case FailedTestResultMessageFieldsId.Reason:
reason = ReadStringValue(stream, fieldSize);
break;
case FailedTestResultMessageFieldsId.ExceptionMessageList:
exceptionMessages = ReadExceptionMessagesPayload(stream);
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;
default:
SetPosition(stream, stream.Position + fieldSize);
break;
}
}
failedTestResultMessages.Add(new FailedTestResultMessage(uid, displayName, state, duration, reason, exceptionMessages, standardOutput, errorOutput, sessionUid));
}
return failedTestResultMessages;
}
private static ExceptionMessage[] ReadExceptionMessagesPayload(Stream stream)
{
var exceptionMessages = new List<ExceptionMessage>();
int length = ReadInt(stream);
for (int i = 0; i < length; i++)
{
int fieldCount = ReadUShort(stream);
string? errorMessage = null;
string? errorType = null;
string? stackTrace = null;
for (int j = 0; j < fieldCount; j++)
{
int fieldId = ReadUShort(stream);
int fieldSize = ReadInt(stream);
switch (fieldId)
{
case ExceptionMessageFieldsId.ErrorMessage:
errorMessage = ReadStringValue(stream, fieldSize);
break;
case ExceptionMessageFieldsId.ErrorType:
errorType = ReadStringValue(stream, fieldSize);
break;
case ExceptionMessageFieldsId.StackTrace:
stackTrace = ReadStringValue(stream, fieldSize);
break;
}
}
exceptionMessages.Add(new ExceptionMessage(errorMessage, errorType, stackTrace));
}
return [.. exceptionMessages];
}
public void Serialize(object objectToSerialize, Stream stream)
{
Debug.Assert(stream.CanSeek, "We expect a seekable stream.");
var testResultMessages = (TestResultMessages)objectToSerialize;
WriteUShort(stream, GetFieldCount(testResultMessages));
WriteField(stream, TestResultMessagesFieldsId.ExecutionId, testResultMessages.ExecutionId);
WriteField(stream, TestResultMessagesFieldsId.InstanceId, testResultMessages.InstanceId);
WriteSuccessfulTestMessagesPayload(stream, testResultMessages.SuccessfulTestMessages);
WriteFailedTestMessagesPayload(stream, testResultMessages.FailedTestMessages);
}
private static void WriteSuccessfulTestMessagesPayload(Stream stream, SuccessfulTestResultMessage[]? successfulTestResultMessages)
{
if (successfulTestResultMessages is null || successfulTestResultMessages.Length == 0)
{
return;
}
WriteUShort(stream, TestResultMessagesFieldsId.SuccessfulTestMessageList);
// We will reserve an int (4 bytes)
// so that we fill the size later, once we write the payload
WriteInt(stream, 0);
long before = stream.Position;
WriteInt(stream, successfulTestResultMessages.Length);
foreach (SuccessfulTestResultMessage successfulTestResultMessage in successfulTestResultMessages)
{
WriteUShort(stream, GetFieldCount(successfulTestResultMessage));
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);
}
// NOTE: We are able to seek only if we are using a MemoryStream
// thus, the seek operation is fast as we are only changing the value of a property
WriteAtPosition(stream, (int)(stream.Position - before), before - sizeof(int));
}
private static void WriteFailedTestMessagesPayload(Stream stream, FailedTestResultMessage[]? failedTestResultMessages)
{
if (failedTestResultMessages is null || failedTestResultMessages.Length == 0)
{
return;
}
WriteUShort(stream, TestResultMessagesFieldsId.FailedTestMessageList);
// We will reserve an int (4 bytes)
// so that we fill the size later, once we write the payload
WriteInt(stream, 0);
long before = stream.Position;
WriteInt(stream, failedTestResultMessages.Length);
foreach (FailedTestResultMessage failedTestResultMessage in failedTestResultMessages)
{
WriteUShort(stream, GetFieldCount(failedTestResultMessage));
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);
WriteExceptionMessagesPayload(stream, failedTestResultMessage.Exceptions);
WriteField(stream, FailedTestResultMessageFieldsId.StandardOutput, failedTestResultMessage.StandardOutput);
WriteField(stream, FailedTestResultMessageFieldsId.ErrorOutput, failedTestResultMessage.ErrorOutput);
WriteField(stream, FailedTestResultMessageFieldsId.SessionUid, failedTestResultMessage.SessionUid);
}
// NOTE: We are able to seek only if we are using a MemoryStream
// thus, the seek operation is fast as we are only changing the value of a property
WriteAtPosition(stream, (int)(stream.Position - before), before - sizeof(int));
}
private static void WriteExceptionMessagesPayload(Stream stream, ExceptionMessage[]? exceptionMessages)
{
if (exceptionMessages is null || exceptionMessages.Length == 0)
{
return;
}
WriteUShort(stream, FailedTestResultMessageFieldsId.ExceptionMessageList);
// We will reserve an int (4 bytes)
// so that we fill the size later, once we write the payload
WriteInt(stream, 0);
long before = stream.Position;
WriteInt(stream, exceptionMessages.Length);
foreach (ExceptionMessage exceptionMessage in exceptionMessages)
{
WriteUShort(stream, GetFieldCount(exceptionMessage));
WriteField(stream, ExceptionMessageFieldsId.ErrorMessage, exceptionMessage.ErrorMessage);
WriteField(stream, ExceptionMessageFieldsId.ErrorType, exceptionMessage.ErrorType);
WriteField(stream, ExceptionMessageFieldsId.StackTrace, exceptionMessage.StackTrace);
}
// NOTE: We are able to seek only if we are using a MemoryStream
// thus, the seek operation is fast as we are only changing the value of a property
WriteAtPosition(stream, (int)(stream.Position - before), before - sizeof(int));
}
private static ushort GetFieldCount(TestResultMessages testResultMessages) =>
(ushort)((testResultMessages.ExecutionId is null ? 0 : 1) +
(testResultMessages.InstanceId is null ? 0 : 1) +
(IsNullOrEmpty(testResultMessages.SuccessfulTestMessages) ? 0 : 1) +
(IsNullOrEmpty(testResultMessages.FailedTestMessages) ? 0 : 1));
private static ushort GetFieldCount(SuccessfulTestResultMessage successfulTestResultMessage) =>
(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) +
(IsNullOrEmpty(failedTestResultMessage.Exceptions) ? 0 : 1) +
(failedTestResultMessage.StandardOutput is null ? 0 : 1) +
(failedTestResultMessage.ErrorOutput is null ? 0 : 1) +
(failedTestResultMessage.SessionUid is null ? 0 : 1));
private static ushort GetFieldCount(ExceptionMessage exceptionMessage) =>
(ushort)((exceptionMessage.ErrorMessage is null ? 0 : 1) +
(exceptionMessage.ErrorType is null ? 0 : 1) +
(exceptionMessage.StackTrace is null ? 0 : 1));
}
|