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

Adding codec to serialize F# Unit #9039

Merged
merged 1 commit into from
Jul 8, 2024
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
7 changes: 7 additions & 0 deletions Orleans.sln
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardToy.Frontend", "pl
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardToy.AppHost", "playground\DashboardToy\DashboardToy.AppHost\DashboardToy.AppHost.csproj", "{84B44F1D-B7FE-40E3-82F0-730A55AC8613}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Orleans.Serialization.FSharp.Tests", "test\Orleans.Serialization.FSharp.Tests\Orleans.Serialization.FSharp.Tests.fsproj", "{B2D53D3C-E44A-4C9B-AAEE-28FB8C1BDF62}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -603,6 +605,10 @@ Global
{84B44F1D-B7FE-40E3-82F0-730A55AC8613}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84B44F1D-B7FE-40E3-82F0-730A55AC8613}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84B44F1D-B7FE-40E3-82F0-730A55AC8613}.Release|Any CPU.Build.0 = Release|Any CPU
{B2D53D3C-E44A-4C9B-AAEE-28FB8C1BDF62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2D53D3C-E44A-4C9B-AAEE-28FB8C1BDF62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2D53D3C-E44A-4C9B-AAEE-28FB8C1BDF62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2D53D3C-E44A-4C9B-AAEE-28FB8C1BDF62}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -714,6 +720,7 @@ Global
{316CDCC7-323F-4264-9FC9-667662BB1F80} = {A41DE3D1-F8AA-4234-BE6F-3C9646A1507A}
{C4DD4F96-3EC6-47C6-97AA-9B14F0F2099B} = {316CDCC7-323F-4264-9FC9-667662BB1F80}
{84B44F1D-B7FE-40E3-82F0-730A55AC8613} = {316CDCC7-323F-4264-9FC9-667662BB1F80}
{B2D53D3C-E44A-4C9B-AAEE-28FB8C1BDF62} = {A6573187-FD0D-4DF7-91D1-03E07E470C0A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7BFB3429-B5BB-4DB1-95B4-67D77A864952}
Expand Down
25 changes: 25 additions & 0 deletions src/Orleans.Serialization.FSharp/FSharpCodecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,31 @@

namespace Orleans.Serialization
{
/// <summary>
/// Serializer for <see cref="Unit"/>
/// </summary>
[RegisterSerializer]
public sealed class FSharpUnitCodec : IFieldCodec<Unit>
{
public void WriteField<TBufferWriter>(ref Writer<TBufferWriter> writer, uint fieldIdDelta, Type expectedType, Unit value) where TBufferWriter : IBufferWriter<byte> =>
ReferenceCodec.WriteNullReference(ref writer, fieldIdDelta);

public Unit ReadValue<TInput>(ref Reader<TInput> reader, Field field)
{
field.EnsureWireType(WireType.Reference);
ReferenceCodec.MarkValueField(reader.Session);
var reference = reader.ReadVarUInt32();
if (reference != 0) throw new ReferenceNotFoundException(typeof(Unit), reference);
return null;
}
}

/// <summary>
/// Copier for <see cref="Unit"/>
/// </summary>
[RegisterCopier]
public sealed class FSharpUnitCopier : ShallowCopier<Unit>;

/// <summary>
/// Serializer for <see cref="FSharpOption{T}"/>.
/// </summary>
Expand Down
69 changes: 0 additions & 69 deletions test/DefaultCluster.Tests/SerializationTests/SerializationTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using TestExtensions;
using UnitTests.FSharpTypes;
using UnitTests.GrainInterfaces;
using Xunit;

Expand Down Expand Up @@ -95,73 +94,5 @@ public void Serialization_DefaultActivatorValueTypeWithUseActivator_Phase2()
Assert.IsAssignableFrom<DefaultActivatorValueTypeWithUseActivator>(copy);
Assert.Equal(4, ((DefaultActivatorValueTypeWithUseActivator)copy).Value);
}

[Fact, TestCategory("BVT"), TestCategory("Serialization")]
public void Serialization_Roundtrip_FSharp_SingleCaseDiscriminatedUnion()
{
var du = SingleCaseDU.ofInt(1);
var copy = HostedCluster.RoundTripSerializationForTesting(du);

Assert.IsAssignableFrom<SingleCaseDU>(copy);
Assert.Equal(du, copy);
}

[Fact, TestCategory("BVT"), TestCategory("Serialization")]
public void Serialization_Roundtrip_FSharp_DoubleCaseDiscriminatedUnion()
{
var case1 = DoubleCaseDU.NewCase1("case 1");
var case2 = DoubleCaseDU.NewCase2(2);

var copyCase1 = HostedCluster.RoundTripSerializationForTesting(case1);
var copyCase2 = HostedCluster.RoundTripSerializationForTesting(case2);

Assert.IsAssignableFrom<DoubleCaseDU>(copyCase1);
Assert.IsAssignableFrom<DoubleCaseDU>(copyCase2);
Assert.Equal(case1, copyCase1);
Assert.Equal(case2, copyCase2);
}

[Fact, TestCategory("BVT"), TestCategory("Serialization")]
public void Serialization_Roundtrip_FSharp_TripleCaseDiscriminatedUnion()
{
var case1 = TripleCaseDU.NewCase1("case 1");
var case2 = TripleCaseDU.NewCase2(2);
var case3 = TripleCaseDU.NewCase3('a');

var copyCase1 = HostedCluster.RoundTripSerializationForTesting(case1);
var copyCase2 = HostedCluster.RoundTripSerializationForTesting(case2);
var copyCase3 = HostedCluster.RoundTripSerializationForTesting(case3);

Assert.IsAssignableFrom<TripleCaseDU>(copyCase1);
Assert.IsAssignableFrom<TripleCaseDU>(copyCase2);
Assert.IsAssignableFrom<TripleCaseDU>(copyCase3);
Assert.Equal(case1, copyCase1);
Assert.Equal(case2, copyCase2);
Assert.Equal(case3, copyCase3);
}

[Fact(Skip = "DUs with 4 or more cases fail when trying to instanciate Case{1-4}-classes via RuntimeHelpers.GetUninitializedObject when deserializing"),
TestCategory("BVT"), TestCategory("Serialization")]
public void Serialization_Roundtrip_FSharp_QuadrupleCaseDiscriminatedUnion()
{
var case1 = QuadrupleCaseDU.NewCase1("case 1");
var case2 = QuadrupleCaseDU.NewCase2(2);
var case3 = QuadrupleCaseDU.NewCase3('a');
var case4 = QuadrupleCaseDU.NewCase4(1);

var copyCase1 = HostedCluster.RoundTripSerializationForTesting(case1);
var copyCase2 = HostedCluster.RoundTripSerializationForTesting(case2);
var copyCase3 = HostedCluster.RoundTripSerializationForTesting(case3);
var copyCase4 = HostedCluster.RoundTripSerializationForTesting(case4);

Assert.IsAssignableFrom<QuadrupleCaseDU>(copyCase1);
Assert.IsAssignableFrom<QuadrupleCaseDU>(copyCase2);
Assert.IsAssignableFrom<QuadrupleCaseDU>(copyCase3);
Assert.IsAssignableFrom<QuadrupleCaseDU>(copyCase4);
Assert.Equal(case1, copyCase1);
Assert.Equal(case2, copyCase2);
Assert.Equal(case3, copyCase3);
Assert.Equal(case4, copyCase4);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>latest</LangVersion>
<TargetFrameworks>$(TestTargetFrameworks)</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="FSharp.Core"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="$(SourceRoot)src/Orleans.Serialization.FSharp/Orleans.Serialization.FSharp.csproj"/>
<ProjectReference Include="$(SourceRoot)test/TestInfrastructure/TestExtensions/TestExtensions.csproj"/>
<ProjectReference Include="$(SourceRoot)test/Grains/TestFSharp/TestFSharp.fsproj"/>
<ProjectReference Include="$(SourceRoot)test/Grains/TestFSharpGrainInterfaces/TestFSharpGrainInterfaces.csproj"/>
</ItemGroup>

<ItemGroup>
<Compile Include="SerializationTests.fs" />
</ItemGroup>

</Project>
89 changes: 89 additions & 0 deletions test/Orleans.Serialization.FSharp.Tests/SerializationTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
namespace TestFSharpSerialization

open TestExtensions
open UnitTests.FSharpTypes
open Xunit

[<CollectionDefinition("DefaultCluster")>]
type DefaultClusterTestCollection() = interface ICollectionFixture<DefaultClusterFixture>;

type FSharpSerializationTests(fixture: DefaultClusterFixture) =
inherit HostedTestClusterEnsureDefaultStarted(fixture)

let cluster = fixture.HostedCluster

[<Fact; TestCategory("BVT"); TestCategory("Serialization")>]
let Serialization_Roundtrip_FSharp_Unit () =
let roundtripped = cluster.RoundTripSerializationForTesting ()
let copy = cluster.DeepCopy ()
Assert.Equal((), roundtripped)
Assert.Equal((), copy)

[<Fact; TestCategory("BVT"); TestCategory("Serialization")>]
let Serialization_Roundtrip_FSharp_SingleCaseDiscriminatedUnion () =
let du = SingleCaseDU.Case1 1
let roundtripped = cluster.RoundTripSerializationForTesting du
let copy = cluster.DeepCopy du
Assert.Equal(du, roundtripped)
Assert.Equal(du, copy)

[<Fact; TestCategory("BVT"); TestCategory("Serialization")>]
let Serialization_Roundtrip_FSharp_DoubleCaseDiscriminatedUnion () =
let case1 = DoubleCaseDU.Case1 "case 1"
let case2 = DoubleCaseDU.Case2 2

let roundtrippedCase1 = cluster.RoundTripSerializationForTesting case1
let roundtrippedCase2 = cluster.RoundTripSerializationForTesting case2
let copyCase1 = cluster.DeepCopy case1
let copyCase2 = cluster.DeepCopy case2

Assert.Equal(case1, roundtrippedCase1)
Assert.Equal(case2, roundtrippedCase2)
Assert.Equal(case1, copyCase1)
Assert.Equal(case2, copyCase2)

[<Fact; TestCategory("BVT"); TestCategory("Serialization")>]
let Serialization_Roundtrip_FSharp_TripleCaseDiscriminatedUnion () =
let case1 = TripleCaseDU.Case1 "case 1"
let case2 = TripleCaseDU.Case2 2
let case3 = TripleCaseDU.Case3 'a'

let roundtrippedCase1 = cluster.RoundTripSerializationForTesting case1
let roundtrippedCase2 = cluster.RoundTripSerializationForTesting case2
let roundtrippedCase3 = cluster.RoundTripSerializationForTesting case3
let copyCase1 = cluster.DeepCopy case1
let copyCase2 = cluster.DeepCopy case2
let copyCase3 = cluster.DeepCopy case3

Assert.Equal(case1, roundtrippedCase1)
Assert.Equal(case2, roundtrippedCase2)
Assert.Equal(case3, roundtrippedCase3)
Assert.Equal(case1, copyCase1)
Assert.Equal(case2, copyCase2)
Assert.Equal(case3, copyCase3)

[<Fact(Skip = "DUs with 4 or more cases fail when trying to instantiate Case{2-4}-classes via RuntimeHelpers.GetUninitializedObject when deserializing")>]
[<TestCategory("BVT"); TestCategory("Serialization")>]
let Serialization_Roundtrip_FSharp_QuadrupleCaseDiscriminatedUnion () =
let case1 = QuadrupleCaseDU.Case1 "case 1"
let case2 = QuadrupleCaseDU.Case2 2
let case3 = QuadrupleCaseDU.Case3 'a'
let case4 = QuadrupleCaseDU.Case4 1uy

let roundtrippedCase1 = cluster.RoundTripSerializationForTesting case1
let roundtrippedCase2 = cluster.RoundTripSerializationForTesting case2
let roundtrippedCase3 = cluster.RoundTripSerializationForTesting case3
let roundtrippedCase4 = cluster.RoundTripSerializationForTesting case4
let copyCase1 = cluster.DeepCopy case1
let copyCase2 = cluster.DeepCopy case2
let copyCase3 = cluster.DeepCopy case3
let copyCase4 = cluster.DeepCopy case4

Assert.Equal(case1, roundtrippedCase1);
Assert.Equal(case2, roundtrippedCase2);
Assert.Equal(case3, roundtrippedCase3);
Assert.Equal(case4, roundtrippedCase4);
Assert.Equal(case1, copyCase1);
Assert.Equal(case2, copyCase2);
Assert.Equal(case3, copyCase3);
Assert.Equal(case4, copyCase4);
13 changes: 13 additions & 0 deletions test/Orleans.Serialization.UnitTests/BuiltInCodecTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3156,6 +3156,19 @@ public class UriCopierTests(ITestOutputHelper output) : CopierTester<Uri, IDeepC
protected override bool IsImmutable => true;
}

public class FSharpUnitTests(ITestOutputHelper output) : FieldCodecTester<Unit, FSharpUnitCodec>(output)
{
protected override Unit CreateValue() => null;
protected override Unit[] TestValues => [null];
}

public class FSharpUnitCopierTests(ITestOutputHelper output) : CopierTester<Unit, FSharpUnitCopier>(output)
{
protected override bool IsImmutable => true;
protected override Unit CreateValue() => null;
protected override Unit[] TestValues => [null];
}

public class FSharpOptionTests(ITestOutputHelper output) : FieldCodecTester<FSharpOption<Guid>, FSharpOptionCodec<Guid>>(output)
{
protected override FSharpOption<Guid>[] TestValues => [null, FSharpOption<Guid>.None, FSharpOption<Guid>.Some(Guid.Empty), FSharpOption<Guid>.Some(Guid.NewGuid())];
Expand Down
Loading