Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix deserializers for DiscoveryRequestArgs and RunRequestArgs #2768

Merged
merged 7 commits into from
Apr 29, 2024
16 changes: 14 additions & 2 deletions src/Platform/Microsoft.Testing.Platform/ServerMode/Json/Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ public Json(Dictionary<Type, JsonSerializer>? serializers = null, Dictionary<Typ
string runId = json.Bind<string>(jsonElement, JsonRpcStrings.RunId);
_ = Guid.TryParse(runId, out Guid result);

json.TryBind(jsonElement, out ICollection<TestNode>? testNodes, JsonRpcStrings.Tests);
json.TryArrayBind(jsonElement, out TestNode[]? testNodes, JsonRpcStrings.Tests);
json.TryBind(jsonElement, out string? graphFilter, JsonRpcStrings.Filter);

return new DiscoverRequestArgs(
Expand All @@ -564,7 +564,7 @@ public Json(Dictionary<Type, JsonSerializer>? serializers = null, Dictionary<Typ
string runId = json.Bind<string>(jsonElement, JsonRpcStrings.RunId);
_ = Guid.TryParse(runId, out Guid result);

json.TryBind(jsonElement, out ICollection<TestNode>? testNodes, JsonRpcStrings.Tests);
json.TryArrayBind(jsonElement, out TestNode[]? testNodes, JsonRpcStrings.Tests);
json.TryBind(jsonElement, out string? graphFilter, JsonRpcStrings.Filter);

return new RunRequestArgs(
Expand Down Expand Up @@ -726,6 +726,18 @@ internal bool TryBind<T>(JsonElement element, out T? value, string? property = n
return true;
}

internal bool TryArrayBind<T>(JsonElement element, out T[]? value, string? property = null)
{
if (property is not null && !element.TryGetProperty(property, out element))
{
value = default;
return false;
}

value = element.EnumerateArray().Select(x => Deserialize<T>(x)).ToArray();
return true;
}

private T Deserialize<T>(JsonElement element)
{
bool deserializerFound = _deserializers.TryGetValue(typeof(T), out JsonDeserializer? deserializer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Testing.TestInfrastructure;

using TestNode = Microsoft.Testing.Platform.Extensions.Messages.TestNode;
using TestNodeUid = Microsoft.Testing.Platform.Extensions.Messages.TestNodeUid;

namespace Microsoft.Testing.Platform.UnitTests;

Expand Down Expand Up @@ -70,19 +71,64 @@ public async Task SerializeDeserialize_Succeed(Type type)
}

static bool HasCustomDeserializeAssert(Type type) => type == typeof(TestNode);
static void CustomAssert(Type type, object instanceDeserialized, object originalObject)
}

[Arguments(typeof(DiscoverRequestArgs))]
[Arguments(typeof(RunRequestArgs))]
public void DeserializeSpecificTypes(Type type)
{
var json = CreateSerializedInstance(type);
Type? deserializer = SerializerUtilities.DeserializerTypes.SingleOrDefault(x => x == type);

if (deserializer is not null)
{
if (type == typeof(TestNode))
var actual = Deserialize(deserializer, json);
object expected = CreateInstance(type);

if (type == typeof(DiscoverRequestArgs))
{
var deserialized = (TestNode)instanceDeserialized;
var original = (TestNode)originalObject;
Assert.AreEqual(original.Uid, deserialized.Uid);
Assert.AreEqual(original.DisplayName, deserialized.DisplayName);
Assert.AreEqual(original.Properties.Single<TestFileLocationProperty>(), deserialized.Properties.Single<TestFileLocationProperty>());
AssertRequestArgs(type, (DiscoverRequestArgs)actual, (DiscoverRequestArgs)expected);
}
else if (type == typeof(RunRequestArgs))
{
AssertRequestArgs(type, (RunRequestArgs)actual, (RunRequestArgs)expected);
}
else
{
Assert.AreEqual(actual, expected);
}
}
}

private void AssertRequestArgs<TRequestArgs>(Type type, TRequestArgs actualRequest, TRequestArgs expectedRequest)
where TRequestArgs : RequestArgsBase
{
Assert.AreEqual(expectedRequest.RunId, actualRequest.RunId);
Assert.AreEqual(expectedRequest.TestNodes?.Count, actualRequest.TestNodes?.Count);

var actualTestNodes = actualRequest.TestNodes?.ToArray();
var expectedTestNodes = expectedRequest.TestNodes?.ToArray();

for (int i = 0; i < actualRequest.TestNodes?.Count; i++)
{
CustomAssert(typeof(TestNode), actualTestNodes?[i]!, expectedTestNodes?[i]!);
}

Assert.AreEqual(expectedRequest.GraphFilter, actualRequest.GraphFilter);
}

private static void CustomAssert(Type type, object instanceDeserialized, object originalObject)
{
if (type == typeof(TestNode))
{
var deserialized = (TestNode)instanceDeserialized;
var original = (TestNode)originalObject;
Assert.AreEqual(original.Uid, deserialized.Uid);
Assert.AreEqual(original.DisplayName, deserialized.DisplayName);
Assert.AreEqual(original.Properties.Single<TestFileLocationProperty>(), deserialized.Properties.Single<TestFileLocationProperty>());
}
}

internal static TestArgumentsEntry<Type> FormatSerializerTypes(TestArgumentsContext testArgumentsContext)
=> new((Type)testArgumentsContext.Arguments, ((Type)testArgumentsContext.Arguments).Name);

Expand Down Expand Up @@ -218,6 +264,26 @@ private static void AssertSerialize(Type type, string instanceSerialized)
throw new NotImplementedException($"Assertion not implemented '{type}', value to assert:\n{instanceSerialized}");
}

private static string CreateSerializedInstance(Type type)
{
return type == typeof(DiscoverRequestArgs) || type == typeof(RunRequestArgs)
? """
{
"runId":"00000000-0000-0000-0000-000000000000",
"tests":[
{
"uid":"UnitTest1.TestMethod1",
"display-name":"test1",
"location.file":"filePath",
"location.line-start":1,
"location.line-end":2
}
]
}
"""
: throw new NotImplementedException($"Serialized instance doesn't exist for '{type}'");
}

private static object CreateInstance(Type type)
{
if (type == typeof(AttachDebuggerInfoArgs))
Expand Down Expand Up @@ -323,6 +389,38 @@ private static object CreateInstance(Type type)
return new RequestMessage(1, "testing/discoverTests", null);
}

if (type == typeof(DiscoverRequestArgs))
{
return new DiscoverRequestArgs(
Guid.Empty,
new TestNode[]
{
new()
{
Uid = new TestNodeUid("UnitTest1.TestMethod1"),
DisplayName = "test1",
Properties = new PropertyBag(new TestFileLocationProperty("filePath", new LinePositionSpan(new(1, 0), new(2, 0)))),
},
},
null);
}

if (type == typeof(RunRequestArgs))
{
return new RunRequestArgs(
Guid.Empty,
new TestNode[]
{
new()
{
Uid = new TestNodeUid("UnitTest1.TestMethod1"),
DisplayName = "test1",
Properties = new PropertyBag(new TestFileLocationProperty("filePath", new LinePositionSpan(new(1, 0), new(2, 0)))),
},
},
null);
}

// Last resort, try to create an instance of the type
return type == typeof(object)
? new object()
Expand Down Expand Up @@ -396,6 +494,24 @@ private object Deserialize(Type type, string instanceSerialized)
#endif
}

if (type == typeof(DiscoverRequestArgs))
{
#if NETCOREAPP
return _formatter.Deserialize<DiscoverRequestArgs>(instanceSerialized.AsMemory());
#else
return _formatter.Deserialize<DiscoverRequestArgs>(instanceSerialized);
#endif
}

if (type == typeof(RunRequestArgs))
{
#if NETCOREAPP
return _formatter.Deserialize<RunRequestArgs>(instanceSerialized.AsMemory());
#else
return _formatter.Deserialize<RunRequestArgs>(instanceSerialized);
#endif
}

if (type == typeof(ServerCapabilities))
{
#if NETCOREAPP
Expand Down