| File: Serialization\TestResultConverterV2.cs | Web Access |
| Project: src\vstest\src\Microsoft.TestPlatform.CommunicationUtilities\Microsoft.TestPlatform.CommunicationUtilities.csproj (Microsoft.TestPlatform.CommunicationUtilities) |
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if NETCOREAPP using System; using System.Collections.ObjectModel; using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.VisualStudio.TestPlatform.ObjectModel; namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Serialization; /// <summary> /// Converter used by v2 protocol serializer to serialize TestResult object to and from v2 json. /// In v2, core properties are flat fields; only custom properties use the Properties array. /// </summary> internal class TestResultConverterV2 : JsonConverter<TestResult> { /// <inheritdoc/> public override TestResult? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var doc = JsonDocument.ParseValue(ref reader); var data = doc.RootElement; // TestCase must come first to construct the TestResult var testCaseElement = data.GetProperty("TestCase"); var testCase = StjSafe.Deserialize<TestCase>(testCaseElement, options)!; var testResult = new TestResult(testCase); // Attachments if (data.TryGetProperty("Attachments", out var attachments) && attachments.GetArrayLength() > 0) { foreach (var attachment in attachments.EnumerateArray()) { if (attachment.ValueKind != JsonValueKind.Null) { testResult.Attachments.Add(StjSafe.Deserialize<AttachmentSet>(attachment, options)!); } } } // Messages if (data.TryGetProperty("Messages", out var messages) && messages.GetArrayLength() > 0) { foreach (var message in messages.EnumerateArray()) { if (message.ValueKind != JsonValueKind.Null) { testResult.Messages.Add(StjSafe.Deserialize<TestResultMessage>(message, options)!); } } } // Flat properties if (data.TryGetProperty("Outcome", out var outcome)) testResult.Outcome = (TestOutcome)outcome.GetInt32(); if (data.TryGetProperty("ErrorMessage", out var errorMsg) && errorMsg.ValueKind != JsonValueKind.Null) testResult.ErrorMessage = errorMsg.GetString(); if (data.TryGetProperty("ErrorStackTrace", out var errorStack) && errorStack.ValueKind != JsonValueKind.Null) testResult.ErrorStackTrace = errorStack.GetString(); if (data.TryGetProperty("DisplayName", out var displayName) && displayName.ValueKind != JsonValueKind.Null) testResult.DisplayName = displayName.GetString(); if (data.TryGetProperty("ComputerName", out var computerName) && computerName.ValueKind != JsonValueKind.Null) testResult.ComputerName = computerName.GetString(); if (data.TryGetProperty("Duration", out var duration) && duration.ValueKind != JsonValueKind.Null) testResult.Duration = TimeSpan.Parse(duration.GetString()!, CultureInfo.InvariantCulture); if (data.TryGetProperty("StartTime", out var startTime) && startTime.ValueKind != JsonValueKind.Null) testResult.StartTime = DateTimeOffset.Parse(startTime.GetString()!, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); if (data.TryGetProperty("EndTime", out var endTime) && endTime.ValueKind != JsonValueKind.Null) testResult.EndTime = DateTimeOffset.Parse(endTime.GetString()!, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); // Custom properties if (data.TryGetProperty("Properties", out var properties) && properties.GetArrayLength() > 0) { foreach (var prop in properties.EnumerateArray()) { if (!prop.TryGetProperty("Key", out var keyElement)) continue; var testProperty = StjSafe.Deserialize<TestProperty>(keyElement, options)!; if (!prop.TryGetProperty("Value", out var valueElement)) continue; string? propertyData = null; if (valueElement.ValueKind != JsonValueKind.Null) { propertyData = valueElement.ValueKind == JsonValueKind.String ? valueElement.GetString() : valueElement.GetRawText().Trim('"'); } testProperty = TestProperty.Register(testProperty.Id, testProperty.Label, testProperty.GetValueType(), testProperty.Attributes, typeof(TestObject)); testResult.SetPropertyValue(testProperty, propertyData); } } return testResult; } /// <inheritdoc/> public override void Write(Utf8JsonWriter writer, TestResult value, JsonSerializerOptions options) { writer.WriteStartObject(); // TestCase writer.WritePropertyName("TestCase"); StjSafe.Serialize(writer, value.TestCase, options); // Attachments writer.WritePropertyName("Attachments"); StjSafe.Serialize(writer, value.Attachments, options); // Flat properties writer.WriteNumber("Outcome", (int)value.Outcome); writer.WriteString("ErrorMessage", value.ErrorMessage); writer.WriteString("ErrorStackTrace", value.ErrorStackTrace); writer.WriteString("DisplayName", value.DisplayName); // Messages writer.WritePropertyName("Messages"); StjSafe.Serialize(writer, value.Messages, options); writer.WriteString("ComputerName", value.ComputerName); writer.WriteString("Duration", value.Duration.ToString()); writer.WriteString("StartTime", value.StartTime); writer.WriteString("EndTime", value.EndTime); // Custom properties (non-core) writer.WritePropertyName("Properties"); writer.WriteStartArray(); foreach (var property in value.GetProperties()) { writer.WriteStartObject(); writer.WritePropertyName("Key"); StjSafe.Serialize(writer, property.Key, options); writer.WritePropertyName("Value"); if (property.Value is null) { writer.WriteNullValue(); } else { // Avoid JsonSerializer.Serialize(writer, value, value.GetType(), options) which // requires reflection metadata that NativeAOT trims. TestObjectBaseConverter.WritePropertyValue(writer, property.Value, options); } writer.WriteEndObject(); } writer.WriteEndArray(); writer.WriteEndObject(); } } #endif