File: Serialization\TestCaseConverterV2.cs
Web Access
Project: src\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.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 TestCase object to and from v2 json.
/// In v2, core properties are flat fields; only custom properties use the Properties array.
/// </summary>
internal class TestCaseConverterV2 : JsonConverter<TestCase>
{
    /// <inheritdoc/>
    public override TestCase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var testCase = new TestCase();

        using var doc = JsonDocument.ParseValue(ref reader);
        var data = doc.RootElement;

        if (data.TryGetProperty("FullyQualifiedName", out var fqn))
            testCase.FullyQualifiedName = fqn.GetString()!;
        if (data.TryGetProperty("ExecutorUri", out var uri) && uri.ValueKind != JsonValueKind.Null)
            testCase.ExecutorUri = new Uri(uri.GetString()!);
        if (data.TryGetProperty("Source", out var source))
            testCase.Source = source.GetString()!;
        if (data.TryGetProperty("Id", out var id) && id.ValueKind != JsonValueKind.Null)
            testCase.Id = GuidPolyfill.Parse(id.GetString()!, CultureInfo.InvariantCulture);
        if (data.TryGetProperty("DisplayName", out var display) && display.ValueKind != JsonValueKind.Null)
            testCase.DisplayName = display.GetString()!;
        if (data.TryGetProperty("CodeFilePath", out var codePath) && codePath.ValueKind != JsonValueKind.Null)
            testCase.CodeFilePath = codePath.GetString();
        if (data.TryGetProperty("LineNumber", out var lineNum))
            testCase.LineNumber = lineNum.GetInt32();

        // Process custom properties (e.g. Traits)
        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 = JsonSerializer.Deserialize<TestProperty>(keyElement, options);
                if (testProperty is null)
                    continue;

                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));
                testCase.SetPropertyValue(testProperty, propertyData);
            }
        }

        return testCase;
    }

    /// <inheritdoc/>
    public override void Write(Utf8JsonWriter writer, TestCase value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        writer.WriteString("Id", value.Id.ToString());
        writer.WriteString("FullyQualifiedName", value.FullyQualifiedName);
        writer.WriteString("DisplayName", value.DisplayName);
        writer.WriteString("ExecutorUri", value.ExecutorUri?.OriginalString);
        writer.WriteString("Source", value.Source);
        writer.WriteString("CodeFilePath", value.CodeFilePath);
        writer.WriteNumber("LineNumber", value.LineNumber);

        // Custom properties (e.g. Traits) — only non-core properties from the store
        writer.WritePropertyName("Properties");
        writer.WriteStartArray();
        foreach (var property in value.GetProperties())
        {
            writer.WriteStartObject();
            writer.WritePropertyName("Key");
            JsonSerializer.Serialize(writer, property.Key, options);
            writer.WritePropertyName("Value");
            if (property.Value is null)
            {
                writer.WriteNullValue();
            }
            else
            {
                JsonSerializer.Serialize(writer, property.Value, property.Value.GetType(), options);
            }
            writer.WriteEndObject();
        }
        writer.WriteEndArray();

        writer.WriteEndObject();
    }
}

#endif