Visitar URL original
Enable P2P generic test by AArnott · Pull Request #1604 · MessagePack-CSharp/MessagePack-CSharp · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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;

public class GenericsFormatterTests
Expand Down Expand Up @@ -428,7 +429,7 @@ public class MyGenericObject<T1, T2>
await VerifyCS.Test.RunDefaultAsync(testSource);
}

[Fact(Skip = "Does not pass yet because the project reference isn't set up correctly.")]
[Fact]
public async Task Generics_Defined_In_ReferencedProject()
{
string defineSource = """
Expand Down Expand Up @@ -469,21 +470,14 @@ public class MyObjectNested
}
}
""";
var definingProject = new Microsoft.CodeAnalysis.Testing.ProjectState("DefiningProject", LanguageNames.CSharp, string.Empty, ".cs")
{
Sources = { defineSource },
ReferenceAssemblies = new VerifyCS.Test().ReferenceAssemblies,
};
definingProject.AdditionalReferences.AddRange(new VerifyCS.Test().TestState.AdditionalReferences);

await new VerifyCS.Test
{
TestState =
{
Sources = { usageSource },
AdditionalProjects =
{
{ "DefiningProject", definingProject },
["DefiningProject"] = { Sources = { defineSource } },
},
AdditionalProjectReferences = { "DefiningProject" },
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@
<ProjectReference Include="..\..\src\MessagePack\MessagePack.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Resources\Generics_Defined_In_ReferencedProject\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// <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 Formatters.TempProject
{
using MsgPack = global::MessagePack;

internal sealed class MyGenericObjectFormatter<T> : MsgPack::Formatters.IMessagePackFormatter<global::TempProject.MyGenericObject<T>>
{

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

MsgPack::IFormatterResolver formatterResolver = options.Resolver;
writer.WriteArrayHeader(1);
MsgPack::FormatterResolverExtensions.GetFormatterWithVerify<T>(formatterResolver).Serialize(ref writer, value.Content, options);
}

public global::TempProject.MyGenericObject<T> Deserialize(ref MsgPack::MessagePackReader reader, MsgPack::MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}

options.Security.DepthStep(ref reader);
MsgPack::IFormatterResolver formatterResolver = options.Resolver;
var length = reader.ReadArrayHeader();
var ____result = new global::TempProject.MyGenericObject<T>();

for (int i = 0; i < length; i++)
{
switch (i)
{
case 0:
____result.Content = MsgPack::FormatterResolverExtensions.GetFormatterWithVerify<T>(formatterResolver).Deserialize(ref reader, options);
break;
default:
reader.Skip();
break;
}
}

reader.Depth--;
return ____result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// <auto-generated />

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

namespace MessagePack
{
using MsgPack = global::MessagePack;
using Formatters = global::Formatters;

/// <summary>A MessagePack resolver that uses generated formatters for types in this assembly.</summary>
internal 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();

/// <summary>An instance of this resolver that returns standard AOT-compatible formatters as well as formatters specifically generated for types in this assembly.</summary>
public static readonly MsgPack::IFormatterResolver InstanceWithStandardAotResolver = MsgPack::Resolvers.CompositeResolver.Create(Instance, MsgPack::Resolvers.StandardAotResolver.Instance);

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;
}
}
}
}

internal static class GeneratedMessagePackResolverGetFormatterHelper
{
private static readonly global::System.Collections.Generic.Dictionary<global::System.Type, int> lookup;

static GeneratedMessagePackResolverGetFormatterHelper()
{
lookup = new global::System.Collections.Generic.Dictionary<global::System.Type, int>(4)
{
{ typeof(global::TempProject.MyGenericObject<global::TempProject.MyGenericObject<int>>), 0 },
{ typeof(global::TempProject.MyGenericObject<int>), 1 },
{ typeof(global::TempProject.MyObject), 2 },
{ typeof(global::TempProject.MyObjectNested), 3 },
};
}

internal static object GetFormatter(global::System.Type t)
{
int key;
if (!lookup.TryGetValue(t, out key))
{
return null;
}

switch (key)
{
case 0: return new Formatters::TempProject.MyGenericObjectFormatter<global::TempProject.MyGenericObject<int>>();
case 1: return new Formatters::TempProject.MyGenericObjectFormatter<int>();
case 2: return new Formatters::TempProject.MyObjectFormatter();
case 3: return new Formatters::TempProject.MyObjectNestedFormatter();
default: return null;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public static partial class CSharpSourceGeneratorVerifier
public class Test : CSharpSourceGeneratorTest<EmptySourceGeneratorProvider, XUnitVerifier>
{
private readonly string? testFile;
private readonly string? testMethod;
private readonly string testMethod;
private AnalyzerOptions options = AnalyzerOptions.Default;

public Test([CallerFilePath] string? testFile = null, [CallerMemberName] string? testMethod = null)
public Test([CallerFilePath] string? testFile = null, [CallerMemberName] string testMethod = null!)
{
this.CompilerDiagnostics = CompilerDiagnostics.Warnings;

Expand All @@ -46,8 +46,6 @@ public Test([CallerFilePath] string? testFile = null, [CallerMemberName] string?
#if WRITE_EXPECTED
TestBehaviors |= TestBehaviors.SkipGeneratedSourcesCheck;
#endif

this.AddGeneratedSources(testMethod);
}

public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.Latest;
Expand All @@ -69,7 +67,7 @@ public AnalyzerOptions Options
}
}

public static async Task RunDefaultAsync(string testSource, AnalyzerOptions? options = null, [CallerFilePath] string? testFile = null, [CallerMemberName] string? testMethod = null)
public static async Task RunDefaultAsync(string testSource, AnalyzerOptions? options = null, [CallerFilePath] string? testFile = null, [CallerMemberName] string testMethod = null!)
{
await new Test(testFile, testMethod)
{
Expand All @@ -81,35 +79,58 @@ public static async Task RunDefaultAsync(string testSource, AnalyzerOptions? opt
}.RunAsync();
}

public Test AddGeneratedSources([CallerMemberName] string? testMethod = null)
public Test AddGeneratedSources()
{
string expectedPrefix = $"{ThisAssembly.AssemblyName}.Resources.{testMethod}."
.Replace(' ', '_')
.Replace(',', '_')
.Replace('(', '_')
.Replace(')', '_');

foreach (var resourceName in typeof(Test).Assembly.GetManifestResourceNames())
static void AddGeneratedSources(ProjectState project, string testMethod, bool withPrefix)
{
if (!resourceName.StartsWith(expectedPrefix))
{
continue;
}

using var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
if (resourceStream is null)
string prefix = withPrefix ? $"{project.Name}." : string.Empty;
string expectedPrefix = $"{ThisAssembly.AssemblyName}.Resources.{testMethod}.{prefix}"
.Replace(' ', '_')
.Replace(',', '_')
.Replace('(', '_')
.Replace(')', '_');

foreach (var resourceName in typeof(Test).Assembly.GetManifestResourceNames())
{
throw new InvalidOperationException();
if (!resourceName.StartsWith(expectedPrefix))
{
continue;
}

using var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
if (resourceStream is null)
{
throw new InvalidOperationException();
}

using var reader = new StreamReader(resourceStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 4096, leaveOpen: true);
var name = resourceName.Substring(expectedPrefix.Length);
project.GeneratedSources.Add((typeof(MessagePackGenerator), name, reader.ReadToEnd()));
}
}

using var reader = new StreamReader(resourceStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 4096, leaveOpen: true);
var name = resourceName.Substring(expectedPrefix.Length);
this.TestState.GeneratedSources.Add((typeof(MessagePackGenerator), name, reader.ReadToEnd()));
AddGeneratedSources(this.TestState, this.testMethod, this.TestState.AdditionalProjects.Count > 0);
foreach (ProjectState addlProject in this.TestState.AdditionalProjects.Values)
{
AddGeneratedSources(addlProject, this.testMethod, true);
}

return this;
}

protected override Task RunImplAsync(CancellationToken cancellationToken)
{
this.AddGeneratedSources();

foreach (ProjectState addlProject in this.TestState.AdditionalProjects.Values)
{
addlProject.AdditionalReferences.AddRange(this.TestState.AdditionalReferences);
addlProject.DocumentationMode = DocumentationMode.Parse;
}

return base.RunImplAsync(cancellationToken);
}

protected override IEnumerable<Type> GetSourceGenerators()
{
yield return typeof(MessagePackGenerator);
Expand Down Expand Up @@ -139,17 +160,18 @@ protected override ParseOptions CreateParseOptions()

protected override async Task<(Compilation, ImmutableArray<Diagnostic>)> GetProjectCompilationAsync(Project project, IVerifier verifier, CancellationToken cancellationToken)
{
var resourceDirectory = Path.Combine(Path.GetDirectoryName(this.testFile)!, "Resources", this.testMethod!);
string fileNamePrefix = this.TestState.AdditionalProjects.Count > 0 ? $"{project.Name}." : string.Empty;
var resourceDirectory = Path.Combine(Path.GetDirectoryName(this.testFile)!, "Resources", this.testMethod);

var (compilation, diagnostics) = await base.GetProjectCompilationAsync(project, verifier, cancellationToken);
var expectedNames = new HashSet<string>();
foreach (var tree in compilation.SyntaxTrees.Skip(project.DocumentIds.Count))
{
WriteTreeToDiskIfNecessary(tree, resourceDirectory);
WriteTreeToDiskIfNecessary(tree, resourceDirectory, fileNamePrefix);
expectedNames.Add(Path.GetFileName(tree.FilePath));
}

var currentTestPrefix = $"{ThisAssembly.AssemblyName}.Resources.{this.testMethod}.";
var currentTestPrefix = $"{ThisAssembly.AssemblyName}.Resources.{this.testMethod}.{fileNamePrefix}";
foreach (var name in this.GetType().Assembly.GetManifestResourceNames())
{
if (!name.StartsWith(currentTestPrefix))
Expand All @@ -167,15 +189,15 @@ protected override ParseOptions CreateParseOptions()
}

[Conditional("WRITE_EXPECTED")]
private static void WriteTreeToDiskIfNecessary(SyntaxTree tree, string resourceDirectory)
private static void WriteTreeToDiskIfNecessary(SyntaxTree tree, string resourceDirectory, string fileNamePrefix)
{
if (tree.Encoding is null)
{
throw new ArgumentException("Syntax tree encoding was not specified");
}

var name = Path.GetFileName(tree.FilePath);
var filePath = Path.Combine(resourceDirectory, name);
string name = fileNamePrefix + Path.GetFileName(tree.FilePath);
string filePath = Path.Combine(resourceDirectory, name);
Directory.CreateDirectory(resourceDirectory);
File.WriteAllText(filePath, tree.GetText().ToString(), tree.Encoding);
}
Expand Down