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

Json serializer type discovery #18

Merged
merged 15 commits into from
Jul 23, 2020
Merged
Show file tree
Hide file tree
Changes from 9 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
Expand Up @@ -2,16 +2,32 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Text.Json.Serialization;
using Xunit;

namespace System.Text.Json.SourceGeneration.Tests
{
public class JsonSerializerSouceGeneratorTests
public class JsonSerializerSourceGeneratorTests
kevinwkt marked this conversation as resolved.
Show resolved Hide resolved
{
[JsonSerializable]
public class SampleInternalTest
{
}

[JsonSerializable(typeof(KeyValuePair))]
public class SampleExternalTest
{
}

[Fact]
public static void TestGeneratedCode()
public void TestGeneratedCode()
{
Assert.Equal("Hello", HelloWorldGenerated.HelloWorld.SayHello());
var internalTypeTest = new HelloWorldGenerated.SampleInternalTestClassInfo();
var externalTypeTest = new HelloWorldGenerated.SampleExternalTestClassInfo();

Assert.Equal("SampleInternalTest", internalTypeTest.TestMethod());
Assert.Equal("SampleExternalTest", externalTypeTest.TestMethod());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetFrameworkCurrent)</TargetFrameworks>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,222 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using System.Text.Json.Serialization;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Xunit;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.IO;

namespace System.Text.Json.SourceGeneration.UnitTests
{
public static class GeneratorTests
public class GeneratorTests
{
[Fact]
public static void SourceGeneratorInitializationPass()
public void TypeDiscoveryPrimitivePOCO()
kevinwkt marked this conversation as resolved.
Show resolved Hide resolved
{
}
SyntaxTree tree = CSharpSyntaxTree.ParseText(@"
using System;
using System.Text.Json.Serialization;

[Fact]
public static void SourceGeneratorInitializationFail()
{
}
namespace HelloWorld
{
[JsonSerializable]
public class MyType {
public int PublicPropertyInt { get; set; }
public string PublicPropertyString { get; set; }
private int PrivatePropertyInt { get; set; }
private string PrivatePropertyString { get; set; }

[Fact]
public static void SourceGeneratorExecutionPass()
{
public double PublicDouble;
public char PublicChar;
private double PrivateDouble;
private char PrivateChar;

public void MyMethod() { }
public void MySecondMethod() { }
}
}");

// Bypass System.Runtime error.
Assembly systemRuntimeAssembly = Assembly.Load("System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
string systemRuntimeAssemblyPath = systemRuntimeAssembly.Location;

CSharpCompilationOptions options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
MetadataReference[] references = new MetadataReference[] {
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Attribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(JsonSerializableAttribute).Assembly.Location),
MetadataReference.CreateFromFile(systemRuntimeAssemblyPath),
};

Compilation compilation = CSharpCompilation.Create(
"TestAssembly",
syntaxTrees: new[] { tree },
references: references,
options: options
);

//// Emit the image of the referenced assembly.
byte[] image = null;
using (MemoryStream ms = new MemoryStream())
{
var emitResult = compilation.Emit(ms);
if (!emitResult.Success)
{
throw new InvalidOperationException();
}
image = ms.ToArray();
}

JsonSerializerSourceGenerator generator = new JsonSerializerSourceGenerator();

GeneratorDriver driver = new CSharpGeneratorDriver(
new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.Parse),
ImmutableArray.Create<ISourceGenerator>(generator),
ImmutableArray<AdditionalText>.Empty
);

driver.RunFullGeneration(compilation, out _, out _);

// Check base functionality of found types.
Assert.Equal(1, generator.foundTypes.Count);
Assert.Equal("HelloWorld.MyType", generator.foundTypes["MyType"].FullName);

// Check for received properties in created type.
string[] expectedPropertyNames = { "PublicPropertyInt", "PublicPropertyString", "PrivatePropertyInt", "PrivatePropertyString" };
string[] receivedPropertyNames = generator.foundTypes["MyType"].GetProperties().Select(property => property.Name).ToArray();
Assert.Equal(expectedPropertyNames, receivedPropertyNames);

// Check for fields in created type.
string[] expectedFieldNames = { "PublicDouble", "PublicChar", "PrivateDouble", "PrivateChar" };
string[] receivedFieldNames = generator.foundTypes["MyType"].GetFields().Select(field => field.Name).ToArray();
Assert.Equal(expectedFieldNames, receivedFieldNames);

// Check for methods in created type.
string[] expectedMethodNames = { "get_PublicPropertyInt", "set_PublicPropertyInt", "get_PublicPropertyString", "set_PublicPropertyString", "MyMethod", "MySecondMethod" };
string[] receivedMethodNames = generator.foundTypes["MyType"].GetMethods().Select(method => method.Name).ToArray();
Assert.Equal(expectedMethodNames, receivedMethodNames);
}

[Fact]
public static void SourceGeneratorExecutionFail()
public void TypeDiscoveryPrimitiveTemporaryPOCO()
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(@"
using System;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Collections.Generic;

namespace HelloWorld
{
[JsonSerializable]
public class MyType {
public int PublicPropertyInt { get; set; }
public string PublicPropertyString { get; set; }
private int PrivatePropertyInt { get; set; }
private string PrivatePropertyString { get; set; }

public double PublicDouble;
public char PublicChar;
private double PrivateDouble;
private char PrivateChar;

public void MyMethod() { }
public void MySecondMethod() { }
}

[JsonSerializable(typeof(JsonConverterAttribute))]
public class NotMyType { }

}");

// Bypass System.Runtime error.
Assembly systemRuntimeAssembly = Assembly.Load("System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
string systemRuntimeAssemblyPath = systemRuntimeAssembly.Location;

CSharpCompilationOptions options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
MetadataReference[] references = new MetadataReference[] {
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Attribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(JsonSerializableAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(JsonSerializerOptions).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Type).Assembly.Location),
MetadataReference.CreateFromFile(systemRuntimeAssemblyPath),
};

Compilation compilation = CSharpCompilation.Create(
"TestAssembly",
syntaxTrees: new[] { tree },
references: references,
options: options
);

//// Emit the image of the referenced assembly.
byte[] image = null;
using (MemoryStream ms = new MemoryStream())
{
var emitResult = compilation.Emit(ms);
if (!emitResult.Success)
{
throw new InvalidOperationException();
}
image = ms.ToArray();
}

JsonSerializerSourceGenerator generator = new JsonSerializerSourceGenerator();

GeneratorDriver driver = new CSharpGeneratorDriver(
new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.Parse),
ImmutableArray.Create<ISourceGenerator>(generator),
ImmutableArray<AdditionalText>.Empty
);

driver.RunFullGeneration(compilation, out _, out _);

// Check base functionality of found types.
Assert.Equal(2, generator.foundTypes.Count);

// Check for MyType.
Assert.Equal("HelloWorld.MyType", generator.foundTypes["MyType"].FullName);

// Check for received properties in created type.
string[] expectedPropertyNamesMyType = { "PublicPropertyInt", "PublicPropertyString", "PrivatePropertyInt", "PrivatePropertyString" };
string[] receivedPropertyNamesMyType = generator.foundTypes["MyType"].GetProperties().Select(property => property.Name).ToArray();
Assert.Equal(expectedPropertyNamesMyType, receivedPropertyNamesMyType);

// Check for fields in created type.
string[] expectedFieldNamesMyType = { "PublicDouble", "PublicChar", "PrivateDouble", "PrivateChar" };
string[] receivedFieldNamesMyType = generator.foundTypes["MyType"].GetFields().Select(field => field.Name).ToArray();
Assert.Equal(expectedFieldNamesMyType, receivedFieldNamesMyType);

// Check for methods in created type.
string[] expectedMethodNamesMyType = { "get_PublicPropertyInt", "set_PublicPropertyInt", "get_PublicPropertyString", "set_PublicPropertyString", "MyMethod", "MySecondMethod" };
string[] receivedMethodNamesMyType = generator.foundTypes["MyType"].GetMethods().Select(method => method.Name).ToArray();
Assert.Equal(expectedMethodNamesMyType, receivedMethodNamesMyType);

// Check for NotMyType.
Assert.Equal("System.Text.Json.Serialization.JsonConverterAttribute", generator.foundTypes["NotMyType"].FullName);

// Check for received properties in created type.
string[] expectedPropertyNamesNotMyType = { "ConverterType" };
string[] receivedPropertyNamesNotMyType = generator.foundTypes["NotMyType"].GetProperties().Select(property => property.Name).ToArray();
Assert.Equal(expectedPropertyNamesNotMyType, receivedPropertyNamesNotMyType);

// Check for fields in created type.
string[] expectedFieldNamesNotMyType = { };
string[] receivedFieldNamesNotMyType = generator.foundTypes["NotMyType"].GetFields().Select(field => field.Name).ToArray();
Assert.Equal(expectedFieldNamesNotMyType, receivedFieldNamesNotMyType);

// Check for methods in created type.
string[] expectedMethodNamesNotMyType = { "get_ConverterType", "CreateConverter" };
string[] receivedMethodNamesNotMyType = generator.foundTypes["NotMyType"].GetMethods().Select(method => method.Name).ToArray();
Assert.Equal(expectedMethodNamesNotMyType, receivedMethodNamesNotMyType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="$(MicrosoftCodeAnalysisVersion)" PrivateAssets="all" />

<ProjectReference Include="..\src\System.Text.Json.csproj" />
<ProjectReference Include="..\System.Text.Json.SourceGeneration\System.Text.Json.SourceGeneration.csproj" />
</ItemGroup>

Expand Down
Loading