Skip to content

Commit

Permalink
Adding codec to serialize F# Unit
Browse files Browse the repository at this point in the history
  • Loading branch information
gfix authored and ReubenBond committed Jul 8, 2024
1 parent 3f93995 commit 1c0666d
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 69 deletions.
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

0 comments on commit 1c0666d

Please sign in to comment.