Skip to content

Commit

Permalink
fix serialization context (#156)
Browse files Browse the repository at this point in the history
  • Loading branch information
elringus authored May 28, 2024
1 parent 3817bed commit df39df3
Show file tree
Hide file tree
Showing 29 changed files with 300 additions and 283 deletions.
10 changes: 5 additions & 5 deletions src/cs/Bootsharp.Common.Test/Bootsharp.Common.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="xunit" Version="2.6.6"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="xunit" Version="2.8.1"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
16 changes: 8 additions & 8 deletions src/cs/Bootsharp.Common.Test/SerializerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void WhenInfoResolverNotAssignedThrowsError ()
TypeInfoResolver = null
};
Assert.Contains("Serializer info resolver is not assigned",
Assert.Throws<Error>(() => Serialize("")).Message);
Assert.Throws<Error>(() => Serialize("", null)).Message);
}

[Fact]
Expand All @@ -31,20 +31,20 @@ public void WhenTypeInfoNotAvailableThrowsError ()
TypeInfoResolver = new MockResolver()
};
Assert.Contains("Failed to resolve serializer info",
Assert.Throws<Error>(() => Serialize("")).Message);
Assert.Throws<Error>(() => Serialize("", null)).Message);
}

[Fact]
public void CanSerialize ()
{
Assert.Equal("""{"items":[{"id":"foo"},{"id":"bar"}]}""",
Serialize(new MockRecord(new MockItem[] { new("foo"), new("bar") })));
Serialize(new MockRecord(new MockItem[] { new("foo"), new("bar") }), typeof(MockRecord)));
}

[Fact]
public void SerializesNullAsNull ()
{
Assert.Equal("null", Serialize(null));
Assert.Equal("null", Serialize(null, null));
}

[Fact]
Expand Down Expand Up @@ -77,17 +77,17 @@ public void WhenDeserializationFailsErrorIsThrown ()
[Fact]
public void RespectsOptions ()
{
Assert.Equal("{\"enum\":0}", Serialize(new MockItemWithEnum(MockEnum.Foo)));
Assert.Equal("{\"enum\":null}", Serialize(new MockItemWithEnum(null)));
Assert.Equal("{\"enum\":0}", Serialize(new MockItemWithEnum(MockEnum.Foo), typeof(MockItemWithEnum)));
Assert.Equal("{\"enum\":null}", Serialize(new MockItemWithEnum(null), typeof(MockItemWithEnum)));
Assert.Equal(MockEnum.Foo, Deserialize<MockItemWithEnum>("{\"enum\":0}").Enum);
Assert.Null((Deserialize<MockItemWithEnum>("{\"enum\":null}")).Enum);
Options = new JsonSerializerOptions(JsonSerializerDefaults.Web) {
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter() },
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
Assert.Equal("{\"enum\":\"Foo\"}", Serialize(new MockItemWithEnum(MockEnum.Foo)));
Assert.Equal("{}", Serialize(new MockItemWithEnum(null)));
Assert.Equal("{\"enum\":\"Foo\"}", Serialize(new MockItemWithEnum(MockEnum.Foo), typeof(MockItemWithEnum)));
Assert.Equal("{}", Serialize(new MockItemWithEnum(null), typeof(MockItemWithEnum)));
Assert.Equal(MockEnum.Foo, (Deserialize<MockItemWithEnum>("{\"enum\":\"Foo\"}")).Enum);
Assert.Null((Deserialize<MockItemWithEnum>("{}")).Enum);
}
Expand Down
9 changes: 0 additions & 9 deletions src/cs/Bootsharp.Common.Test/TypesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,6 @@ public class TypesTest
private readonly CustomAttributeData export = GetMockExportAttribute();
private readonly CustomAttributeData import = GetMockImportAttribute();

[Fact]
public void Records ()
{
// TODO: Remove when coverlet bug is resolved: https://github.com/coverlet-coverage/coverlet/issues/1561
_ = new MockItem("") with { Id = "foo" };
_ = new MockItemWithEnum(default) with { Enum = MockEnum.Bar };
_ = new MockRecord(default) with { Items = new[] { new MockItem("") } };
}

[Fact]
public void TypesAreAssigned ()
{
Expand Down
6 changes: 3 additions & 3 deletions src/cs/Bootsharp.Common/Interop/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ public static class Serializer
};

/// <summary>
/// Serializes specified object to JSON string.
/// Serializes specified object to JSON string using specified serialization context type.
/// </summary>
public static string Serialize (object? @object)
public static string Serialize (object? @object, Type type)
{
if (@object is null) return "null";
return JsonSerializer.Serialize(@object, GetInfo(@object.GetType()));
return JsonSerializer.Serialize(@object, GetInfo(type));
}

/// <summary>
Expand Down
12 changes: 6 additions & 6 deletions src/cs/Bootsharp.Generate.Test/Bootsharp.Generate.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.8.0"/>
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.9.2"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2-beta1.23431.1"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="xunit" Version="2.6.6"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="xunit" Version="2.8.1"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
10 changes: 5 additions & 5 deletions src/cs/Bootsharp.Inject.Test/Bootsharp.Inject.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="xunit" Version="2.6.6"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="xunit" Version="2.8.1"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
2 changes: 1 addition & 1 deletion src/cs/Bootsharp.Inject/Bootsharp.Inject.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<ItemGroup>
<PackageReference Include="Bootsharp.Common" Version="$(Version)"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1"/>
</ItemGroup>

</Project>
16 changes: 8 additions & 8 deletions src/cs/Bootsharp.Publish.Test/Bootsharp.Publish.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0"/>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.8.3"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="MSBuild.ProjectCreation" Version="11.0.1"/>
<PackageReference Include="xunit" Version="2.6.6"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2"/>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.10.4"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="MSBuild.ProjectCreation" Version="12.0.1"/>
<PackageReference Include="xunit" Version="2.8.1"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
2 changes: 1 addition & 1 deletion src/cs/Bootsharp.Publish.Test/Emit/DependenciesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class Class
Added(All, "Bootsharp.Generated.Imports.JSImportedInstancedA");
Added(All, "Bootsharp.Generated.Imports.JSImportedInstancedB");
// Export interop instances are not generated in C#; they're authored by user.
Assert.DoesNotContain("Bootsharp.Generated.Exports.JSExportedInstanced", TestedContent);
DoesNotContain("Bootsharp.Generated.Exports.JSExportedInstanced");
}

[Fact]
Expand Down
4 changes: 2 additions & 2 deletions src/cs/Bootsharp.Publish.Test/Emit/InterfacesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public class JSImported(global::System.Int32 _id) : global::IImported
}
}
""");
Assert.DoesNotContain("JSExported", TestedContent); // Exported instances are authored by user and registered on initial interop.
DoesNotContain("JSExported"); // Exported instances are authored by user and registered on initial interop.
}

[Fact]
Expand Down Expand Up @@ -297,6 +297,6 @@ public class Class
}
"""));
Execute();
Assert.DoesNotContain("Foo", TestedContent, StringComparison.OrdinalIgnoreCase);
DoesNotContain("Foo");
}
}
20 changes: 10 additions & 10 deletions src/cs/Bootsharp.Publish.Test/Emit/InteropTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ internal static void RegisterProxies ()
public void GeneratesDisposeInstanceBindings ()
{
Execute();
Contains("JSExport] internal static void DisposeExportedInstance (global::System.Int32 id) => global::Bootsharp.Instances.Dispose(id);");
Contains("""JSImport("disposeInstance", "Bootsharp")] internal static partial void DisposeImportedInstance (global::System.Int32 id);""");
Contains("JSExport] internal static void DisposeExportedInstance (int id) => global::Bootsharp.Instances.Dispose(id);");
Contains("""JSImport("disposeInstance", "Bootsharp")] internal static partial void DisposeImportedInstance (int id);""");
}

[Fact]
Expand Down Expand Up @@ -158,7 +158,7 @@ public class Class
}
"""));
Execute();
Assert.DoesNotContain("Foo", TestedContent, StringComparison.OrdinalIgnoreCase);
DoesNotContain("Foo");
}

[Fact]
Expand Down Expand Up @@ -206,17 +206,17 @@ public class Class
}
"""));
Execute();
Contains("""Proxies.Set("Space.Class.FunA", (global::Space.Record a) => Deserialize<global::Space.Record>(Space_Class_FunA(Serialize(a))));""");
Contains("""Proxies.Set("Space.Class.FunB", async (global::Space.Record?[]? a) => Deserialize<global::Space.Record?[]?>(await Space_Class_FunB(Serialize(a))));""");
Contains("JSExport] internal static global::System.String Space_Class_InvA (global::System.String a) => Serialize(global::Space.Class.InvA(Deserialize<global::Space.Record>(a)));");
Contains("JSExport] internal static async global::System.Threading.Tasks.Task<global::System.String?> Space_Class_InvB (global::System.String? a) => Serialize(await global::Space.Class.InvB(Deserialize<global::Space.Record?[]?>(a)));");
Contains("""Proxies.Set("Space.Class.FunA", (global::Space.Record a) => Deserialize<global::Space.Record>(Space_Class_FunA(Serialize(a, typeof(global::Space.Record)))));""");
Contains("""Proxies.Set("Space.Class.FunB", async (global::Space.Record?[]? a) => Deserialize<global::Space.Record?[]?>(await Space_Class_FunB(Serialize(a, typeof(global::Space.Record[])))));""");
Contains("JSExport] internal static global::System.String Space_Class_InvA (global::System.String a) => Serialize(global::Space.Class.InvA(Deserialize<global::Space.Record>(a)), typeof(global::Space.Record));");
Contains("JSExport] internal static async global::System.Threading.Tasks.Task<global::System.String?> Space_Class_InvB (global::System.String? a) => Serialize(await global::Space.Class.InvB(Deserialize<global::Space.Record?[]?>(a)), typeof(global::Space.Record[]));");
Contains("""JSImport("Space.Class.funASerialized", "Bootsharp")] internal static partial global::System.String Space_Class_FunA (global::System.String a);""");
Contains("""JSImport("Space.Class.funBSerialized", "Bootsharp")] internal static partial global::System.Threading.Tasks.Task<global::System.String?> Space_Class_FunB (global::System.String? a);""");

// TODO: Remove when resolved: https://github.com/elringus/bootsharp/issues/138
Contains("""Proxies.Set("Space.Class.FunAsyncBytes", async () => Deserialize<global::System.Byte[]>(await Space_Class_FunAsyncBytes()));""");
Contains("JSExport] internal static async global::System.Threading.Tasks.Task<global::System.String> Space_Class_InvAsyncBytes () => Serialize(await global::Space.Class.InvAsyncBytes());");
Contains("""JSImport("Space.Class.funAsyncBytesSerialized", "Bootsharp")] internal static partial global::System.Threading.Tasks.Task<global::System.String> Space_Class_FunAsyncBytes ();""");
Contains("""Proxies.Set("Space.Class.FunAsyncBytes", async () => await Space_Class_FunAsyncBytes());""");
Contains("JSExport] [return: JSMarshalAs<JSType.Promise<JSType.Any>>] internal static async global::System.Threading.Tasks.Task<object> Space_Class_InvAsyncBytes () => await global::Space.Class.InvAsyncBytes();");
Contains("""JSImport("Space.Class.funAsyncBytesSerialized", "Bootsharp")] [return: JSMarshalAs<JSType.Promise<JSType.Any>>] internal static partial global::System.Threading.Tasks.Task<object> Space_Class_FunAsyncBytes ();""");
}

[Fact]
Expand Down
64 changes: 2 additions & 62 deletions src/cs/Bootsharp.Publish.Test/Emit/SerializerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void WhenNoSerializableTypesIsEmpty ()
WithClass("[JSInvokable] public static byte[] Bar (int[] a, double[] b, string[] c) => default;")
);
Execute();
Assert.DoesNotContain("JsonSerializable", TestedContent);
DoesNotContain("JsonSerializable");
}

[Fact]
Expand All @@ -43,7 +43,7 @@ public class Class
}
"""));
Execute();
Assert.DoesNotContain("JsonSerializable", TestedContent);
DoesNotContain("JsonSerializable");
}

[Fact] // .NET's generator indexes types by short names (w/o namespace) and fails on duplicates.
Expand All @@ -65,64 +65,4 @@ public void AddsOnlyTopLevelTypesAndCrawledDuplicates ()
Contains("[JsonSerializable(typeof(global::n.Baz)");
Contains("[JsonSerializable(typeof(global::y.Struct)");
}

[Fact]
public void AddsProxiesForListInterface ()
{
AddAssembly(WithClass("[JSInvokable] public static void Foo (IList<string> a) {}"));
Execute();
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.IList<global::System.String>)");
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.List<global::System.String>)");
Contains("[JsonSerializable(typeof(global::System.String[])");
}

[Fact]
public void AddsProxiesForReadOnlyListInterface ()
{
AddAssembly(WithClass("[JSInvokable] public static void Foo (IReadOnlyList<string> a) {}"));
Execute();
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.IReadOnlyList<global::System.String>)");
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.List<global::System.String>)");
Contains("[JsonSerializable(typeof(global::System.String[])");
}

[Fact]
public void AddsProxiesForDictInterface ()
{
AddAssembly(WithClass("[JSInvokable] public static void Foo (IDictionary<string, int> a) {}"));
Execute();
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.IDictionary<global::System.String, global::System.Int32>)");
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.Dictionary<global::System.String, global::System.Int32>)");
}

[Fact]
public void AddsProxiesForReadOnlyDictInterface ()
{
AddAssembly(WithClass("[JSInvokable] public static void Foo (IReadOnlyDictionary<string, int[]> a) {}"));
Execute();
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.IReadOnlyDictionary<global::System.String, global::System.Int32[]>)");
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.Dictionary<global::System.String, global::System.Int32[]>)");
}

[Fact]
public void DoesntAddProxiesForTaskWithoutResult ()
{
AddAssembly(WithClass("[JSInvokable] public static Task Foo (Task bar) => default;"));
Execute();
Assert.DoesNotContain("JsonSerializable", TestedContent);
}

[Fact]
public void AddsProxiesForTaskWithResult ()
{
AddAssembly(
With("public record Info;"),
WithClass("[JSInvokable] public static Task<Info> Foo () => default;"),
WithClass("[JSInvokable] public static Task<IReadOnlyList<bool>> Bar () => default;"));
Execute();
Contains("[JsonSerializable(typeof((global::Info, byte))");
Contains("[JsonSerializable(typeof((global::System.Collections.Generic.IReadOnlyList<global::System.Boolean>, byte))");
Contains("[JsonSerializable(typeof(global::System.Collections.Generic.List<global::System.Boolean>)");
Contains("[JsonSerializable(typeof(global::System.Boolean[])");
}
}
Loading

0 comments on commit df39df3

Please sign in to comment.