Skip to content

Commit

Permalink
Add test demonstrating Union failure in MessagePackFormatter
Browse files Browse the repository at this point in the history
Repro for microsoft#460
  • Loading branch information
AArnott committed May 2, 2020
1 parent 9db2d5c commit ad3aeaf
Showing 1 changed file with 104 additions and 0 deletions.
104 changes: 104 additions & 0 deletions src/StreamJsonRpc.Tests/JsonRpcMessagePackLengthTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MessagePack;
using MessagePack.Formatters;
Expand All @@ -22,6 +23,13 @@ public JsonRpcMessagePackLengthTests(ITestOutputHelper logger)
{
}

private interface IMessagePackServer
{
Task<UnionBaseClass> ReturnUnionTypeAsync(CancellationToken cancellationToken);

Task<string?> AcceptUnionTypeAsync(UnionBaseClass value, CancellationToken cancellationToken);
}

[Fact]
public async Task CanPassAndCallPrivateMethodsObjects()
{
Expand Down Expand Up @@ -53,6 +61,84 @@ public async Task CanPassExceptionFromServer_ErrorData()
Assert.StrictEqual(COR_E_UNAUTHORIZEDACCESS, errorData.HResult);
}

/// <summary>
/// Verifies that return values can support union types by considering the return type as declared in the server method signature.
/// </summary>
[Fact]
public async Task UnionType_ReturnValue()
{
this.serverRpc.AllowModificationWhileListening = true;
this.serverRpc.AddLocalRpcTarget(new MessagePackServer());
UnionBaseClass result = await this.clientRpc.InvokeWithCancellationAsync<UnionBaseClass>(nameof(MessagePackServer.ReturnUnionTypeAsync), null, this.TimeoutToken);
Assert.IsType<UnionDerivedClass>(result);
}

/// <summary>
/// Verifies that positional parameters can support union types by providing extra type information for each argument.
/// </summary>
/// <devremarks>
/// It is expected that after the fix, this test will need to change to provide a Type[] or some other hint as to the base type that should be serialized for the argument.
/// </devremarks>
[Fact]
public async Task UnionType_PositionalParameter()
{
this.serverRpc.AllowModificationWhileListening = true;
this.serverRpc.AddLocalRpcTarget(new MessagePackServer());
string? result = await this.clientRpc.InvokeWithCancellationAsync<string?>(nameof(MessagePackServer.AcceptUnionTypeAsync), new object?[] { new UnionDerivedClass() }, this.TimeoutToken);
Assert.Equal(typeof(UnionDerivedClass).Name, result);
}

/// <summary>
/// Verifies that the type information associated with named parameters is used for proper serialization of union types.
/// </summary>
[Fact]
public async Task UnionType_NamedParameter()
{
this.serverRpc.AllowModificationWhileListening = true;
this.serverRpc.AddLocalRpcTarget(new MessagePackServer());
string? result = await this.clientRpc.InvokeWithParameterObjectAsync<string?>(nameof(MessagePackServer.AcceptUnionTypeAsync), new { value = (UnionBaseClass)new UnionDerivedClass() }, this.TimeoutToken);
Assert.Equal(typeof(UnionDerivedClass).Name, result);
}

/// <summary>
/// Verifies that return values can support union types by considering the return type as declared in the server method signature.
/// </summary>
[Fact]
public async Task UnionType_ReturnValue_Proxy()
{
this.serverRpc.AllowModificationWhileListening = true;
this.serverRpc.AddLocalRpcTarget(new MessagePackServer());
var clientProxy = this.clientRpc.Attach<IMessagePackServer>();
UnionBaseClass result = await clientProxy.ReturnUnionTypeAsync(this.TimeoutToken);
Assert.IsType<UnionDerivedClass>(result);
}

/// <summary>
/// Verifies that positional parameters can support union types by providing extra type information for each argument.
/// </summary>
[Fact]
public async Task UnionType_PositionalParameter_Proxy()
{
this.serverRpc.AllowModificationWhileListening = true;
this.serverRpc.AddLocalRpcTarget(new MessagePackServer());
var clientProxy = this.clientRpc.Attach<IMessagePackServer>();
string? result = await clientProxy.AcceptUnionTypeAsync(new UnionDerivedClass(), this.TimeoutToken);
Assert.Equal(typeof(UnionDerivedClass).Name, result);
}

/// <summary>
/// Verifies that the type information associated with named parameters is used for proper serialization of union types.
/// </summary>
[Fact]
public async Task UnionType_NamedParameter_Proxy()
{
this.serverRpc.AllowModificationWhileListening = true;
this.serverRpc.AddLocalRpcTarget(new MessagePackServer());
var clientProxy = this.clientRpc.Attach<IMessagePackServer>(new JsonRpcProxyOptions { ServerRequiresNamedArguments = true });
string? result = await clientProxy.AcceptUnionTypeAsync(new UnionDerivedClass(), this.TimeoutToken);
Assert.Equal(typeof(UnionDerivedClass).Name, result);
}

protected override void InitializeFormattersAndHandlers()
{
this.serverMessageFormatter = new MessagePackFormatter();
Expand All @@ -69,6 +155,17 @@ protected override void InitializeFormattersAndHandlers()
this.clientMessageHandler = new LengthHeaderMessageHandler(this.clientStream, this.clientStream, this.clientMessageFormatter);
}

[MessagePackObject]
[Union(0, typeof(UnionDerivedClass))]
public abstract class UnionBaseClass
{
}

[MessagePackObject]
public class UnionDerivedClass : UnionBaseClass
{
}

private class UnserializableTypeFormatter : IMessagePackFormatter<CustomSerializedType>
{
public CustomSerializedType Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
Expand All @@ -94,4 +191,11 @@ public void Serialize(ref MessagePackWriter writer, TypeThrowsWhenDeserialized v
writer.WriteArrayHeader(0);
}
}

private class MessagePackServer : IMessagePackServer
{
public Task<UnionBaseClass> ReturnUnionTypeAsync(CancellationToken cancellationToken) => Task.FromResult<UnionBaseClass>(new UnionDerivedClass());

public Task<string?> AcceptUnionTypeAsync(UnionBaseClass value, CancellationToken cancellationToken) => Task.FromResult<string?>(value?.GetType().Name);
}
}

0 comments on commit ad3aeaf

Please sign in to comment.