Skip to content

Commit

Permalink
Add tests for private member serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
AArnott committed Apr 20, 2024
1 parent 3fc7cde commit 0dd89bd
Show file tree
Hide file tree
Showing 18 changed files with 694 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ MsgPack006 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack007 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack008 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack009 | Usage | Error | MsgPack00xMessagePackAnalyzer
MsgPack010 | Usage | Warning | MsgPack00xMessagePackAnalyzer
MsgPack010 | Usage | Warning | MsgPack00xMessagePackAnalyzer
MsgPack011 | Usage | Error | MsgPack00xMessagePackAnalyzer
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
public const string AOTLimitationsId = "MsgPack008";
public const string CollidingFormattersId = "MsgPack009";
public const string InaccessibleFormatterId = "MsgPack010";
public const string PartialTypeRequiredId = "MsgPack011";

internal const string Category = "Usage";

Expand Down Expand Up @@ -207,6 +208,16 @@ public class MsgPack00xMessagePackAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(InaccessibleFormatterId));

internal static readonly DiagnosticDescriptor PartialClassRequired = new(
id: PartialTypeRequiredId,
title: "Partial type required",
category: Category,
messageFormat: "Types with private, serializable members must be declared as partial",
description: "When a data type has serializable members that may only be accessible to the class itself (e.g. private or protected members), the type must be declared as partial to allow source generation of the formatter as a nested type.",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
helpLinkUri: AnalyzerUtilities.GetHelpLink(PartialTypeRequiredId));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
TypeMustBeMessagePackObject,
PublicMemberNeedsKey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ public void GeneratedResolverPicksUpCustomResolversAutomatically()
this.AssertRoundtrip(new MyCustomType2());
}

[Fact]
public void PrivateFieldIsSerialized()
{
this.AssertRoundtrip(new HasPrivateSerializedMembers { ValueAccessor = 3 });
}

private T AssertRoundtrip<T>(T value)
{
byte[] serialized = MessagePackSerializer.Serialize(value, SerializerOptions);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

[MessagePackObject]
internal partial record HasPrivateSerializedMembers
{
[Key(0)]
private int value;

[IgnoreMember]
internal int ValueAccessor
{
get => this.value;
set => this.value = value;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using MessagePack;
using Microsoft.CodeAnalysis;
using VerifyCS = CSharpSourceGeneratorVerifier<MessagePack.SourceGenerator.MessagePackGenerator>;

public class GenericsFormatterTests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using VerifyCS = CSharpSourceGeneratorVerifier<MessagePack.SourceGenerator.MessagePackGenerator>;

public class PrivateMemberAccessTests(ITestOutputHelper logger)
{
[Theory]
[InlineData("class")]
[InlineData("record")]
[InlineData("struct")]
public async Task FormatterForClassWithPrivateMembers(string type)
{
string testSource = $$"""
using System;
using MessagePack;

[MessagePackObject]
partial {{type}} MyObject
{
[Key(0)]
private int value;
}
""";
await VerifyCS.Test.RunDefaultAsync(logger, testSource, testMethod: $"{nameof(FormatterForClassWithPrivateMembers)}({type})");
}

[Theory]
[InlineData("class")]
[InlineData("record")]
[InlineData("struct")]
public async Task FormatterForClassWithPrivateMembers_NonPartialTypes(string type)
{
string testSource = $$"""
using System;
using MessagePack;

[MessagePackObject]
{|MsgPack011:{{type}} MyObject|}
{
[Key(0)]
private int value;
}
""";
await VerifyCS.Test.RunDefaultAsync(logger, testSource, testMethod: $"{nameof(FormatterForClassWithPrivateMembers_NonPartialTypes)}({type})");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// <auto-generated />

#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649

#pragma warning disable CS8669 // We may leak nullable annotations into generated code.

namespace MessagePack {

using MsgPack = global::MessagePack;

partial class GeneratedMessagePackResolver
{
private sealed class MyObjectFormatter : MsgPack::Formatters.IMessagePackFormatter<global::MyObject>
{

public void Serialize(ref MsgPack::MessagePackWriter writer, global::MyObject value, MsgPack::MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}

writer.WriteArrayHeader(0);
}

public global::MyObject Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}

reader.Skip();
return new global::MyObject();
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// <auto-generated />

#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649

using MsgPack = global::MessagePack;

[assembly: MsgPack::Internal.GeneratedAssemblyMessagePackResolverAttribute(typeof(MessagePack.GeneratedMessagePackResolver), 3, 0)]

namespace MessagePack {

/// <summary>A MessagePack resolver that uses generated formatters for types in this assembly.</summary>
partial class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
{
/// <summary>An instance of this resolver that only returns formatters specifically generated for types in this assembly.</summary>
public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();

private GeneratedMessagePackResolver()
{
}

public MsgPack::Formatters.IMessagePackFormatter<T> GetFormatter<T>()
{
return FormatterCache<T>.Formatter;
}

private static class FormatterCache<T>
{
internal static readonly MsgPack::Formatters.IMessagePackFormatter<T> Formatter;

static FormatterCache()
{
var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
if (f != null)
{
Formatter = (MsgPack::Formatters.IMessagePackFormatter<T>)f;
}
}
}

private static class GeneratedMessagePackResolverGetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary<global::System.Type, int> closedTypeLookup = new(1)
{
{ typeof(global::MyObject), 0 },
};

internal static object GetFormatter(global::System.Type t)
{
if (closedTypeLookup.TryGetValue(t, out int closedKey))
{
return closedKey switch
{
0 => new MyObjectFormatter(),
_ => null, // unreachable
};
}

return null;
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// <auto-generated />

#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649

#pragma warning disable CS8669 // We may leak nullable annotations into generated code.

namespace MessagePack {

using MsgPack = global::MessagePack;

partial class GeneratedMessagePackResolver
{
private sealed class MyObjectFormatter : MsgPack::Formatters.IMessagePackFormatter<global::MyObject>
{

public void Serialize(ref MsgPack::MessagePackWriter writer, global::MyObject value, MsgPack::MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}

writer.WriteArrayHeader(0);
}

public global::MyObject Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}

reader.Skip();
return new global::MyObject();
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// <auto-generated />

#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649

using MsgPack = global::MessagePack;

[assembly: MsgPack::Internal.GeneratedAssemblyMessagePackResolverAttribute(typeof(MessagePack.GeneratedMessagePackResolver), 3, 0)]

namespace MessagePack {

/// <summary>A MessagePack resolver that uses generated formatters for types in this assembly.</summary>
partial class GeneratedMessagePackResolver : MsgPack::IFormatterResolver
{
/// <summary>An instance of this resolver that only returns formatters specifically generated for types in this assembly.</summary>
public static readonly MsgPack::IFormatterResolver Instance = new GeneratedMessagePackResolver();

private GeneratedMessagePackResolver()
{
}

public MsgPack::Formatters.IMessagePackFormatter<T> GetFormatter<T>()
{
return FormatterCache<T>.Formatter;
}

private static class FormatterCache<T>
{
internal static readonly MsgPack::Formatters.IMessagePackFormatter<T> Formatter;

static FormatterCache()
{
var f = GeneratedMessagePackResolverGetFormatterHelper.GetFormatter(typeof(T));
if (f != null)
{
Formatter = (MsgPack::Formatters.IMessagePackFormatter<T>)f;
}
}
}

private static class GeneratedMessagePackResolverGetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary<global::System.Type, int> closedTypeLookup = new(1)
{
{ typeof(global::MyObject), 0 },
};

internal static object GetFormatter(global::System.Type t)
{
if (closedTypeLookup.TryGetValue(t, out int closedKey))
{
return closedKey switch
{
0 => new MyObjectFormatter(),
_ => null, // unreachable
};
}

return null;
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// <auto-generated />

#pragma warning disable 618, 612, 414, 168, CS1591, SA1129, SA1309, SA1312, SA1403, SA1649

#pragma warning disable CS8669 // We may leak nullable annotations into generated code.

namespace MessagePack {

using MsgPack = global::MessagePack;

partial class GeneratedMessagePackResolver
{
private sealed class MyObjectFormatter : MsgPack::Formatters.IMessagePackFormatter<global::MyObject>
{

public void Serialize(ref MsgPack::MessagePackWriter writer, global::MyObject value, MsgPack::MessagePackSerializerOptions options)
{
writer.WriteArrayHeader(0);
}

public global::MyObject Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
throw new global::System.InvalidOperationException("typecode is null, struct not supported");
}

reader.Skip();
return new global::MyObject();
}
}

}

}

0 comments on commit 0dd89bd

Please sign in to comment.