From 069c6b8945493cad8b60bddd909484dce266b9a2 Mon Sep 17 00:00:00 2001 From: James Courtney <2636896+jamescourtney@users.noreply.github.com> Date: Mon, 3 Oct 2022 00:38:27 -0700 Subject: [PATCH 01/31] Create stryker.yml --- .github/workflows/stryker.yml | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/stryker.yml diff --git a/.github/workflows/stryker.yml b/.github/workflows/stryker.yml new file mode 100644 index 00000000..a5731871 --- /dev/null +++ b/.github/workflows/stryker.yml @@ -0,0 +1,54 @@ +name: Mutation Testing + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + build: + runs-on: windows-2019 + env: + AppVeyorBuild: true + steps: + - uses: actions/checkout@v2 + + - name: Setup .NET 6 + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 6.0.x + + - name: Setup .NET 5 + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 5.0.x + + - name: Setup .NET 3 + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.x + + - name: Setup .NET 2 + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 2.1.x + + - name: Restore dependencies + working-directory: src + run: dotnet restore + + - name: Build Compiler + working-directory: src/FlatSharp.Compiler + run: dotnet build -c Release --no-restore + + - name: Build + working-directory: src + run: dotnet build -c Release --no-restore + + - name: Install Stryker + run: dotnet tool install -g dotnet-stryker + + - name: Run Stryker + run: dotnet stryker From cb8a5a985bbfeb04957ae067672529763dac5abd Mon Sep 17 00:00:00 2001 From: James Courtney <2636896+jamescourtney@users.noreply.github.com> Date: Mon, 3 Oct 2022 01:16:17 -0700 Subject: [PATCH 02/31] Update stryker.yml --- .github/workflows/stryker.yml | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/.github/workflows/stryker.yml b/.github/workflows/stryker.yml index a5731871..42a46ba5 100644 --- a/.github/workflows/stryker.yml +++ b/.github/workflows/stryker.yml @@ -12,6 +12,7 @@ jobs: runs-on: windows-2019 env: AppVeyorBuild: true + steps: - uses: actions/checkout@v2 @@ -39,16 +40,25 @@ jobs: working-directory: src run: dotnet restore - - name: Build Compiler - working-directory: src/FlatSharp.Compiler - run: dotnet build -c Release --no-restore - - - name: Build - working-directory: src - run: dotnet build -c Release --no-restore - - name: Install Stryker run: dotnet tool install -g dotnet-stryker - - name: Run Stryker - run: dotnet stryker + - name: Run Stryker FlatSharp/Runtime + working-directory: src/Tests/FlatSharpTests + run: dotnet-stryker -p FlatSharp.Runtime.csproj + + - name: Run Stryker FlatSharp/FlatSharp + working-directory: src/Tests/FlatSharpTests + run: dotnet-stryker -p FlatSharp.csproj + + - name: Run Stryker FlatSharpCompiler/Compiler + working-directory: src/Tests/FlatSharpCompilerTests + run: dotnet-stryker -p FlatSharp.Compiler.csproj + + - name: Run Stryker FlatSharpCompiler/Runtime + working-directory: src/Tests/FlatSharpCompilerTests + run: dotnet-stryker -p FlatSharp.Runtime.csproj + + - name: Run Stryker FlatSharpCompiler/FlatSharp + working-directory: src/Tests/FlatSharpCompilerTests + run: dotnet-stryker -p FlatSharp.csproj From 9348a89440bf6ee783c7102184124003aaa59dfd Mon Sep 17 00:00:00 2001 From: James Courtney Date: Mon, 3 Oct 2022 01:58:53 -0700 Subject: [PATCH 03/31] Add configs --- src/FlatSharp.Compiler/stryker-config.json | 11 +++++++++++ src/FlatSharp.Runtime/stryker-config.json | 11 +++++++++++ src/FlatSharp/stryker-config.json | 10 ++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/FlatSharp.Compiler/stryker-config.json create mode 100644 src/FlatSharp.Runtime/stryker-config.json create mode 100644 src/FlatSharp/stryker-config.json diff --git a/src/FlatSharp.Compiler/stryker-config.json b/src/FlatSharp.Compiler/stryker-config.json new file mode 100644 index 00000000..0610fa9f --- /dev/null +++ b/src/FlatSharp.Compiler/stryker-config.json @@ -0,0 +1,11 @@ +{ + "stryker-config": + { + "project": "FlatSharp.Compiler.csproj", + "test-projects": [ + "../Tests/FlatSharpTests/FlatSharpTests.csproj", + "../Tests/FlatSharpCompilerTests/FlatSharpCompilerTests.csproj" + ], + "target-framework": "net6.0" + } +} \ No newline at end of file diff --git a/src/FlatSharp.Runtime/stryker-config.json b/src/FlatSharp.Runtime/stryker-config.json new file mode 100644 index 00000000..89ec9684 --- /dev/null +++ b/src/FlatSharp.Runtime/stryker-config.json @@ -0,0 +1,11 @@ +{ + "stryker-config": + { + "project": "FlatSharp.Runtime.csproj", + "test-projects": [ + "../Tests/FlatSharpTests/FlatSharpTests.csproj", + "../Tests/FlatSharpCompilerTests/FlatSharpCompilerTests.csproj" + ], + "target-framework": "net6.0" + } +} \ No newline at end of file diff --git a/src/FlatSharp/stryker-config.json b/src/FlatSharp/stryker-config.json new file mode 100644 index 00000000..b00b196b --- /dev/null +++ b/src/FlatSharp/stryker-config.json @@ -0,0 +1,10 @@ +{ + "stryker-config": + { + "project": "FlatSharp.csproj", + "test-projects": [ + "../Tests/FlatSharpTests/FlatSharpTests.csproj" + ], + "target-framework": "net6.0" + } +} \ No newline at end of file From 87077378ad871492361629e294e4d0863fbd885c Mon Sep 17 00:00:00 2001 From: James Courtney <2636896+jamescourtney@users.noreply.github.com> Date: Mon, 3 Oct 2022 02:01:05 -0700 Subject: [PATCH 04/31] Update stryker.yml --- .github/workflows/stryker.yml | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/.github/workflows/stryker.yml b/.github/workflows/stryker.yml index 42a46ba5..d2fab32d 100644 --- a/.github/workflows/stryker.yml +++ b/.github/workflows/stryker.yml @@ -42,23 +42,15 @@ jobs: - name: Install Stryker run: dotnet tool install -g dotnet-stryker + + - name: Run Stryker FlatSharp + working-directory: src/FlatSharp + run: dotnet-stryker - - name: Run Stryker FlatSharp/Runtime - working-directory: src/Tests/FlatSharpTests - run: dotnet-stryker -p FlatSharp.Runtime.csproj - - - name: Run Stryker FlatSharp/FlatSharp - working-directory: src/Tests/FlatSharpTests - run: dotnet-stryker -p FlatSharp.csproj - - - name: Run Stryker FlatSharpCompiler/Compiler - working-directory: src/Tests/FlatSharpCompilerTests - run: dotnet-stryker -p FlatSharp.Compiler.csproj - - - name: Run Stryker FlatSharpCompiler/Runtime - working-directory: src/Tests/FlatSharpCompilerTests - run: dotnet-stryker -p FlatSharp.Runtime.csproj + - name: Run Stryker FlatSharp.Runtime + working-directory: src/FlatSharp.Runtime + run: dotnet-stryker - - name: Run Stryker FlatSharpCompiler/FlatSharp - working-directory: src/Tests/FlatSharpCompilerTests - run: dotnet-stryker -p FlatSharp.csproj + - name: Run Stryker FlatSharp.Compiler + working-directory: src/FlatSharp.Compiler + run: dotnet-stryker From 6dd144181013c546fee25b59ac1429aef30a2b6f Mon Sep 17 00:00:00 2001 From: James Courtney Date: Mon, 3 Oct 2022 02:09:53 -0700 Subject: [PATCH 05/31] Update stryker config --- src/FlatSharp.Compiler/stryker-config.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/FlatSharp.Compiler/stryker-config.json b/src/FlatSharp.Compiler/stryker-config.json index 0610fa9f..d7ba5551 100644 --- a/src/FlatSharp.Compiler/stryker-config.json +++ b/src/FlatSharp.Compiler/stryker-config.json @@ -3,7 +3,6 @@ { "project": "FlatSharp.Compiler.csproj", "test-projects": [ - "../Tests/FlatSharpTests/FlatSharpTests.csproj", "../Tests/FlatSharpCompilerTests/FlatSharpCompilerTests.csproj" ], "target-framework": "net6.0" From 8a959555c2bf56467789a91a32c141a8b9bcb6cf Mon Sep 17 00:00:00 2001 From: James Courtney Date: Wed, 25 Jan 2023 02:32:41 -0800 Subject: [PATCH 06/31] ehh --- .gitignore | 1 + .../Properties/AssemblyInfo.cs | 2 + src/FlatSharp.sln | 9 +- src/FlatSharp/stryker-config.json | 10 -- .../ClassLib/NonScalarVectorTests.cs | 90 -------------- .../Documentation/DocumentationTestCases.cs | 2 +- .../FlatSharpEndToEndTests.csproj | 12 +- .../IO/InputBufferTests.cs | 18 +-- .../PartialMethods/PartialMethodsTests.cs | 23 +--- .../TableMembers/TableMembersTests.cs | 29 +---- .../FlatSharpEndToEndTests_Generated.csproj | 42 +++++++ .../InputBufferTestsPartials.cs | 39 ++++++ .../NonScalarVectorTestsPartials.cs | 111 ++++++++++++++++++ .../PartialMethodsTestsPartials.cs | 40 +++++++ .../TableMembersTestsPartials.cs | 46 ++++++++ 15 files changed, 294 insertions(+), 180 deletions(-) delete mode 100644 src/FlatSharp/stryker-config.json create mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTests_Generated.csproj create mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/InputBufferTestsPartials.cs create mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/NonScalarVectorTestsPartials.cs create mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/PartialMethodsTestsPartials.cs create mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/TableMembersTestsPartials.cs diff --git a/.gitignore b/.gitignore index f03fe2b0..caad5ae2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ /src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.xml /src/Tests/FlatSharpEndToEndTests/FlatSharp.generated.cs /src/Benchmarks/ExperimentalBenchmark/FlatSharp.generated.cs +/src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTestsGenerated.xml diff --git a/src/FlatSharp.Runtime/Properties/AssemblyInfo.cs b/src/FlatSharp.Runtime/Properties/AssemblyInfo.cs index ee458b9b..0a7ba5ca 100644 --- a/src/FlatSharp.Runtime/Properties/AssemblyInfo.cs +++ b/src/FlatSharp.Runtime/Properties/AssemblyInfo.cs @@ -21,6 +21,7 @@ [assembly: InternalsVisibleTo("FlatSharp, PublicKey=0024000004800000940000000602000000240000525341310004000001000100898185ce69dca04430ab296e094cd7eb6c66f5a3cfb0631ef64586fa183f0cb5ca64c47539a3a3c6351a9cf8d976a8d94350af430d5adc10536b3904cc1d6ecaaf3d0cb708aa318c559625f05d3b2d89da1c2bb323bb40e36dcf9245f21c3a4b6793c56ffface5e6e18290afb13c7eac1ea9c7a0c22f289c622bfa7b247d81a2")] [assembly: InternalsVisibleTo("FlatSharpTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100898185ce69dca04430ab296e094cd7eb6c66f5a3cfb0631ef64586fa183f0cb5ca64c47539a3a3c6351a9cf8d976a8d94350af430d5adc10536b3904cc1d6ecaaf3d0cb708aa318c559625f05d3b2d89da1c2bb323bb40e36dcf9245f21c3a4b6793c56ffface5e6e18290afb13c7eac1ea9c7a0c22f289c622bfa7b247d81a2")] [assembly: InternalsVisibleTo("FlatSharpEndToEndTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100898185ce69dca04430ab296e094cd7eb6c66f5a3cfb0631ef64586fa183f0cb5ca64c47539a3a3c6351a9cf8d976a8d94350af430d5adc10536b3904cc1d6ecaaf3d0cb708aa318c559625f05d3b2d89da1c2bb323bb40e36dcf9245f21c3a4b6793c56ffface5e6e18290afb13c7eac1ea9c7a0c22f289c622bfa7b247d81a2")] +[assembly: InternalsVisibleTo("FlatSharpEndToEndTestsGenerated, PublicKey=0024000004800000940000000602000000240000525341310004000001000100898185ce69dca04430ab296e094cd7eb6c66f5a3cfb0631ef64586fa183f0cb5ca64c47539a3a3c6351a9cf8d976a8d94350af430d5adc10536b3904cc1d6ecaaf3d0cb708aa318c559625f05d3b2d89da1c2bb323bb40e36dcf9245f21c3a4b6793c56ffface5e6e18290afb13c7eac1ea9c7a0c22f289c622bfa7b247d81a2")] [assembly: InternalsVisibleTo("PoolingTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100898185ce69dca04430ab296e094cd7eb6c66f5a3cfb0631ef64586fa183f0cb5ca64c47539a3a3c6351a9cf8d976a8d94350af430d5adc10536b3904cc1d6ecaaf3d0cb708aa318c559625f05d3b2d89da1c2bb323bb40e36dcf9245f21c3a4b6793c56ffface5e6e18290afb13c7eac1ea9c7a0c22f289c622bfa7b247d81a2")] [assembly: InternalsVisibleTo("FlatSharpCompilerTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100898185ce69dca04430ab296e094cd7eb6c66f5a3cfb0631ef64586fa183f0cb5ca64c47539a3a3c6351a9cf8d976a8d94350af430d5adc10536b3904cc1d6ecaaf3d0cb708aa318c559625f05d3b2d89da1c2bb323bb40e36dcf9245f21c3a4b6793c56ffface5e6e18290afb13c7eac1ea9c7a0c22f289c622bfa7b247d81a2")] [assembly: InternalsVisibleTo("FlatSharp.Compiler, PublicKey=0024000004800000940000000602000000240000525341310004000001000100898185ce69dca04430ab296e094cd7eb6c66f5a3cfb0631ef64586fa183f0cb5ca64c47539a3a3c6351a9cf8d976a8d94350af430d5adc10536b3904cc1d6ecaaf3d0cb708aa318c559625f05d3b2d89da1c2bb323bb40e36dcf9245f21c3a4b6793c56ffface5e6e18290afb13c7eac1ea9c7a0c22f289c622bfa7b247d81a2")] @@ -33,6 +34,7 @@ [assembly: InternalsVisibleTo("FlatSharpTests")] [assembly: InternalsVisibleTo("FlatSharpCompilerTests")] [assembly: InternalsVisibleTo("FlatSharpEndToEndTests")] +[assembly: InternalsVisibleTo("FlatSharpEndToEndTestsGenerated")] [assembly: InternalsVisibleTo("PoolingTests")] [assembly: InternalsVisibleTo("FlatSharp.Compiler")] [assembly: InternalsVisibleTo("ExperimentalBenchmark")] diff --git a/src/FlatSharp.sln b/src/FlatSharp.sln index b44ac9cf..ecce885e 100644 --- a/src/FlatSharp.sln +++ b/src/FlatSharp.sln @@ -35,7 +35,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microbench.6.3.3", "Benchma EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharpPoolableEndToEndTests", "Tests\FlatSharpPoolableEndToEndTests\FlatSharpPoolableEndToEndTests.csproj", "{7E55A248-4BBD-48AD-B27D-A7E0E705DC89}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlatSharp.UnityPolyfills", "FlatSharp.UnityPolyfills\FlatSharp.UnityPolyfills.csproj", "{E439E4AF-B948-4E6C-8791-1830619EB35B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharp.UnityPolyfills", "FlatSharp.UnityPolyfills\FlatSharp.UnityPolyfills.csproj", "{E439E4AF-B948-4E6C-8791-1830619EB35B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharpEndToEndTests_Generated", "Tests\FlatSharpEndToEndTests_Generated\FlatSharpEndToEndTests_Generated.csproj", "{2BD45021-16C3-46AF-8D69-E2CDF137639D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -99,6 +101,10 @@ Global {E439E4AF-B948-4E6C-8791-1830619EB35B}.Debug|Any CPU.Build.0 = Debug|Any CPU {E439E4AF-B948-4E6C-8791-1830619EB35B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E439E4AF-B948-4E6C-8791-1830619EB35B}.Release|Any CPU.Build.0 = Release|Any CPU + {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -114,6 +120,7 @@ Global {5A173CC8-53B4-445B-9AA2-68A61A4BE449} = {927A2D53-B127-4297-B17A-4E4EEE7ACD0D} {407BD242-0902-4850-9F53-17DF48F3DD6F} = {927A2D53-B127-4297-B17A-4E4EEE7ACD0D} {7E55A248-4BBD-48AD-B27D-A7E0E705DC89} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} + {2BD45021-16C3-46AF-8D69-E2CDF137639D} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {726A4C0E-5760-4B35-8C73-9AC21B2E4C8B} diff --git a/src/FlatSharp/stryker-config.json b/src/FlatSharp/stryker-config.json deleted file mode 100644 index b00b196b..00000000 --- a/src/FlatSharp/stryker-config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "stryker-config": - { - "project": "FlatSharp.csproj", - "test-projects": [ - "../Tests/FlatSharpTests/FlatSharpTests.csproj" - ], - "target-framework": "net6.0" - } -} \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests/ClassLib/NonScalarVectorTests.cs b/src/Tests/FlatSharpEndToEndTests/ClassLib/NonScalarVectorTests.cs index b9ac8e03..1193af73 100644 --- a/src/Tests/FlatSharpEndToEndTests/ClassLib/NonScalarVectorTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/ClassLib/NonScalarVectorTests.cs @@ -114,94 +114,4 @@ private void TestType(FlatBufferDeserializationOption } } } -} - -public interface ITable where T : IEquatable -{ - IList Vector { get; set; } -} - - -public interface IReadOnlyTable where T : IEquatable -{ - IReadOnlyList Vector { get; set; } -} - - -public partial class Root : ITable, ITable, ITable -{ - IList ITable.Vector - { - get => this.StringVector; - set => this.StringVector = value; - } - - IList ITable.Vector - { - get => this.TableVector; - set => this.TableVector = value; - } - - IList ITable.Vector - { - get => this.StructVector; - set => this.StructVector = value; - } -} - -public partial class RootReadOnly : IReadOnlyTable, IReadOnlyTable, IReadOnlyTable -{ - IReadOnlyList IReadOnlyTable.Vector - { - get => this.StringVector; - set => this.StringVector = value; - } - - IReadOnlyList IReadOnlyTable.Vector - { - get => this.TableVector; - set => this.TableVector = value; - } - - IReadOnlyList IReadOnlyTable.Vector - { - get => this.StructVector; - set => this.StructVector = value; - } -} - -public partial class InnerTable : IEquatable -{ - public bool Equals(InnerTable other) - { - return other?.Value == this.Value; - } - - public override bool Equals(object obj) - { - return this.Equals(obj as InnerTable); - } - - public override int GetHashCode() - { - return this.Value.GetHashCode(); - } -} - -public partial class InnerStruct : IEquatable -{ - public bool Equals(InnerStruct other) - { - return other?.Value == this.Value; - } - - public override bool Equals(object obj) - { - return this.Equals(obj as InnerStruct); - } - - public override int GetHashCode() - { - return this.Value.GetHashCode(); - } } \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests/Documentation/DocumentationTestCases.cs b/src/Tests/FlatSharpEndToEndTests/Documentation/DocumentationTestCases.cs index 7b8a8612..f5285c09 100644 --- a/src/Tests/FlatSharpEndToEndTests/Documentation/DocumentationTestCases.cs +++ b/src/Tests/FlatSharpEndToEndTests/Documentation/DocumentationTestCases.cs @@ -23,7 +23,7 @@ public class DocumentationTestCases public DocumentationTestCases() { this.xmlDoc = new System.Xml.XmlDocument(); - this.xmlDoc.Load("FlatSharpEndToEndTests.xml"); + this.xmlDoc.Load("FlatSharpEndToEndTestsGenerated.xml"); } [Fact] diff --git a/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj b/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj index 319db01f..07ab6062 100644 --- a/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj +++ b/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj @@ -12,10 +12,7 @@ FlatSharpTests true annotations - $(OutputPath)$(AssemblyName).xml CS1591 - $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)\..\..\FlatSharp.Compiler\bin\$(Configuration)\net7.0\FlatSharp.Compiler.dll')) - true False True latest @@ -40,16 +37,9 @@ - - + - - - - - - diff --git a/src/Tests/FlatSharpEndToEndTests/IO/InputBufferTests.cs b/src/Tests/FlatSharpEndToEndTests/IO/InputBufferTests.cs index a84fa153..a2ddad70 100644 --- a/src/Tests/FlatSharpEndToEndTests/IO/InputBufferTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/IO/InputBufferTests.cs @@ -264,20 +264,4 @@ private void TestDeserialize(Func bufferBuilder Assert.Equal(i + 5, parsed.Memory.Span[i - 1]); } } -} - -internal interface IMemoryTable -{ - ReadOnlyMemory Memory { get; set; } -} - -public partial class MemoryTable : IMemoryTable -{ - ReadOnlyMemory IMemoryTable.Memory { get => this.Memory.Value; set => this.Memory = MemoryMarshal.AsMemory(value); } -} - -public partial class ReadOnlyMemoryTable : IMemoryTable -{ - ReadOnlyMemory IMemoryTable.Memory { get => this.Memory.Value; set => this.Memory = value; } -} - +} \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests/PartialMethods/PartialMethodsTests.cs b/src/Tests/FlatSharpEndToEndTests/PartialMethods/PartialMethodsTests.cs index 800716f9..c75db4cb 100644 --- a/src/Tests/FlatSharpEndToEndTests/PartialMethods/PartialMethodsTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/PartialMethods/PartialMethodsTests.cs @@ -38,25 +38,4 @@ public void OnInitialized(FlatBufferDeserializationOption option) Assert.Equal(option, parsed.OnInitializedContext.Value.DeserializationOption); Assert.Equal(option, parsed.S.OnInitializedContext.Value.DeserializationOption); } -} - -public partial class Table -{ - public FlatBufferDeserializationContext? OnInitializedContext { get; private set; } - - partial void OnInitialized(FlatBufferDeserializationContext? context) - { - this.OnInitializedContext = context; - } -} - -public partial class Struct -{ - public FlatBufferDeserializationContext? OnInitializedContext { get; private set; } - - partial void OnInitialized(FlatBufferDeserializationContext? context) - { - this.OnInitializedContext = context; - } -} - +} \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests/TableMembers/TableMembersTests.cs b/src/Tests/FlatSharpEndToEndTests/TableMembers/TableMembersTests.cs index dd9e352b..bf6d4505 100644 --- a/src/Tests/FlatSharpEndToEndTests/TableMembers/TableMembersTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/TableMembers/TableMembersTests.cs @@ -168,31 +168,4 @@ private void RunTest(T expectedDefault, FlatBufferDeserializationOpti Assert.Equal(expectedDefault, parsed.ItemList[i]); } } -} - -public interface ITypedTable where T : struct -{ - T ItemStandard { get; set; } - - T? ItemOptional { get; set; } - - T ItemWithDefault { get; set; } - - T ItemDeprecated { get; set; } - - IList? ItemList { get; set; } - - IReadOnlyList? ItemReadonlyList { get; set; } -} - -public partial class BoolTable : ITypedTable { } -public partial class ByteTable : ITypedTable { } -public partial class SByteTable : ITypedTable { } -public partial class ShortTable : ITypedTable { } -public partial class UShortTable : ITypedTable { } -public partial class IntTable : ITypedTable { } -public partial class UIntTable : ITypedTable { } -public partial class LongTable : ITypedTable { } -public partial class ULongTable : ITypedTable { } -public partial class FloatTable : ITypedTable { } -public partial class DoubleTable : ITypedTable { } \ No newline at end of file +} \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTests_Generated.csproj b/src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTests_Generated.csproj new file mode 100644 index 00000000..d005548f --- /dev/null +++ b/src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTests_Generated.csproj @@ -0,0 +1,42 @@ + + + + + false + true + net7.0;net6.0 + net472;net6.0;net7.0 + net7.0 + false + FlatSharpEndToEndTestsGenerated + FlatSharpTests + true + annotations + $(OutputPath)$(AssemblyName).xml + CS1591 + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)\..\..\FlatSharp.Compiler\bin\$(Configuration)\net7.0\FlatSharp.Compiler.dll')) + true + False + True + latest + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/InputBufferTestsPartials.cs b/src/Tests/FlatSharpEndToEndTests_Generated/InputBufferTestsPartials.cs new file mode 100644 index 00000000..f3cb4000 --- /dev/null +++ b/src/Tests/FlatSharpEndToEndTests_Generated/InputBufferTestsPartials.cs @@ -0,0 +1,39 @@ +/* + * Copyright 2018 James Courtney + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using FlatSharp.Internal; + +namespace FlatSharpEndToEndTests.IO.InputBufferTests; + +public interface IMemoryTable +{ + ReadOnlyMemory Memory { get; set; } +} + +public partial class MemoryTable : IMemoryTable +{ + ReadOnlyMemory IMemoryTable.Memory { get => this.Memory.Value; set => this.Memory = MemoryMarshal.AsMemory(value); } +} + +public partial class ReadOnlyMemoryTable : IMemoryTable +{ + ReadOnlyMemory IMemoryTable.Memory { get => this.Memory.Value; set => this.Memory = value; } +} + diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/NonScalarVectorTestsPartials.cs b/src/Tests/FlatSharpEndToEndTests_Generated/NonScalarVectorTestsPartials.cs new file mode 100644 index 00000000..5f982b62 --- /dev/null +++ b/src/Tests/FlatSharpEndToEndTests_Generated/NonScalarVectorTestsPartials.cs @@ -0,0 +1,111 @@ +/* + * Copyright 2018 James Courtney + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace FlatSharpEndToEndTests.ClassLib.NonScalarVectorTests; + +public interface ITable where T : IEquatable +{ + IList Vector { get; set; } +} + + +public interface IReadOnlyTable where T : IEquatable +{ + IReadOnlyList Vector { get; set; } +} + + +public partial class Root : ITable, ITable, ITable +{ + IList ITable.Vector + { + get => this.StringVector; + set => this.StringVector = value; + } + + IList ITable.Vector + { + get => this.TableVector; + set => this.TableVector = value; + } + + IList ITable.Vector + { + get => this.StructVector; + set => this.StructVector = value; + } +} + +public partial class RootReadOnly : IReadOnlyTable, IReadOnlyTable, IReadOnlyTable +{ + IReadOnlyList IReadOnlyTable.Vector + { + get => this.StringVector; + set => this.StringVector = value; + } + + IReadOnlyList IReadOnlyTable.Vector + { + get => this.TableVector; + set => this.TableVector = value; + } + + IReadOnlyList IReadOnlyTable.Vector + { + get => this.StructVector; + set => this.StructVector = value; + } +} + +public partial class InnerTable : IEquatable +{ + public bool Equals(InnerTable other) + { + return other?.Value == this.Value; + } + + public override bool Equals(object obj) + { + return this.Equals(obj as InnerTable); + } + + public override int GetHashCode() + { + return this.Value.GetHashCode(); + } +} + +public partial class InnerStruct : IEquatable +{ + public bool Equals(InnerStruct other) + { + return other?.Value == this.Value; + } + + public override bool Equals(object obj) + { + return this.Equals(obj as InnerStruct); + } + + public override int GetHashCode() + { + return this.Value.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/PartialMethodsTestsPartials.cs b/src/Tests/FlatSharpEndToEndTests_Generated/PartialMethodsTestsPartials.cs new file mode 100644 index 00000000..736ad7ef --- /dev/null +++ b/src/Tests/FlatSharpEndToEndTests_Generated/PartialMethodsTestsPartials.cs @@ -0,0 +1,40 @@ +/* + * Copyright 2018 James Courtney + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FlatSharp; + +namespace FlatSharpEndToEndTests.PartialMethods; + +public partial class Table +{ + public FlatBufferDeserializationContext? OnInitializedContext { get; private set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) + { + this.OnInitializedContext = context; + } +} + +public partial class Struct +{ + public FlatBufferDeserializationContext? OnInitializedContext { get; private set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) + { + this.OnInitializedContext = context; + } +} + diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/TableMembersTestsPartials.cs b/src/Tests/FlatSharpEndToEndTests_Generated/TableMembersTestsPartials.cs new file mode 100644 index 00000000..ae884cbc --- /dev/null +++ b/src/Tests/FlatSharpEndToEndTests_Generated/TableMembersTestsPartials.cs @@ -0,0 +1,46 @@ +/* + * Copyright 2023 James Courtney + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Collections.Generic; + +namespace FlatSharpEndToEndTests.TableMembers; + +public interface ITypedTable where T : struct +{ + T ItemStandard { get; set; } + + T? ItemOptional { get; set; } + + T ItemWithDefault { get; set; } + + T ItemDeprecated { get; set; } + + IList? ItemList { get; set; } + + IReadOnlyList? ItemReadonlyList { get; set; } +} + +public partial class BoolTable : ITypedTable { } +public partial class ByteTable : ITypedTable { } +public partial class SByteTable : ITypedTable { } +public partial class ShortTable : ITypedTable { } +public partial class UShortTable : ITypedTable { } +public partial class IntTable : ITypedTable { } +public partial class UIntTable : ITypedTable { } +public partial class LongTable : ITypedTable { } +public partial class ULongTable : ITypedTable { } +public partial class FloatTable : ITypedTable { } +public partial class DoubleTable : ITypedTable { } \ No newline at end of file From ea7cb78f32f33d37f75f2719ec59444f75b0ea40 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 04:06:26 -0800 Subject: [PATCH 07/31] dedicated projects --- src/FlatSharp.Compiler/CompilerOptions.cs | 4 + src/FlatSharp.Compiler/FlatSharpCompiler.cs | 7 +- .../SchemaModel/RootModel.cs | 23 +-- .../ClassLib/NonScalarVectorTests.cs | 90 +++++++++++ .../Documentation/DocumentationTestCases.cs | 2 +- .../FlatSharpEndToEndTests.csproj | 12 +- src/Tests/FlatSharpEndToEndTests/Helpers.cs | 38 +++-- .../IO/InputBufferTests.cs | 18 ++- .../PartialMethods/PartialMethodsTests.cs | 23 ++- .../TableMembers/TableMembersTests.cs | 29 +++- .../FlatSharpEndToEndTests_Generated.csproj | 42 ----- .../InputBufferTestsPartials.cs | 39 ----- .../NonScalarVectorTestsPartials.cs | 111 ------------- .../PartialMethodsTestsPartials.cs | 40 ----- .../TableMembersTestsPartials.cs | 46 ------ src/Tests/Stryker/CodeGen/CodeGen.csproj | 17 ++ src/Tests/Stryker/CodeGen/FlatSharp.cs | 0 src/Tests/Stryker/CodeGen/Schema.fbs | 108 +++++++++++++ src/Tests/Stryker/Tests/GlobalUsings.cs | 32 ++++ src/Tests/Stryker/Tests/StructFieldTests.cs | 148 ++++++++++++++++++ src/Tests/Stryker/Tests/StrykerTests.csproj | 32 ++++ 21 files changed, 555 insertions(+), 306 deletions(-) delete mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTests_Generated.csproj delete mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/InputBufferTestsPartials.cs delete mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/NonScalarVectorTestsPartials.cs delete mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/PartialMethodsTestsPartials.cs delete mode 100644 src/Tests/FlatSharpEndToEndTests_Generated/TableMembersTestsPartials.cs create mode 100644 src/Tests/Stryker/CodeGen/CodeGen.csproj create mode 100644 src/Tests/Stryker/CodeGen/FlatSharp.cs create mode 100644 src/Tests/Stryker/CodeGen/Schema.fbs create mode 100644 src/Tests/Stryker/Tests/GlobalUsings.cs create mode 100644 src/Tests/Stryker/Tests/StructFieldTests.cs create mode 100644 src/Tests/Stryker/Tests/StrykerTests.csproj diff --git a/src/FlatSharp.Compiler/CompilerOptions.cs b/src/FlatSharp.Compiler/CompilerOptions.cs index 819feb47..e7b7cbaf 100644 --- a/src/FlatSharp.Compiler/CompilerOptions.cs +++ b/src/FlatSharp.Compiler/CompilerOptions.cs @@ -49,4 +49,8 @@ public record CompilerOptions [Option("instrument", Hidden = true, Default = false)] public bool Instrument { get; set; } + + // Suppress auto generated markers for mutation testing. + [Option("no-auto-generated", Hidden = true, Default = false)] + public bool SuppressAutoGenerated { get; set; } } diff --git a/src/FlatSharp.Compiler/FlatSharpCompiler.cs b/src/FlatSharp.Compiler/FlatSharpCompiler.cs index 8a1d6564..b0fbad65 100644 --- a/src/FlatSharp.Compiler/FlatSharpCompiler.cs +++ b/src/FlatSharp.Compiler/FlatSharpCompiler.cs @@ -83,7 +83,12 @@ private static int RunCompiler(CompilerOptions options) } // Read existing file to see if we even need to do any work. - const string outputFileName = "FlatSharp.generated.cs"; + string outputFileName = options.SuppressAutoGenerated switch + { + true => "FlatSharp.cs", + false => "FlatSharp.generated.cs", + }; + string outputFullPath = Path.Combine(options.OutputDirectory, outputFileName); try diff --git a/src/FlatSharp.Compiler/SchemaModel/RootModel.cs b/src/FlatSharp.Compiler/SchemaModel/RootModel.cs index 77c6e8c4..a77245c2 100644 --- a/src/FlatSharp.Compiler/SchemaModel/RootModel.cs +++ b/src/FlatSharp.Compiler/SchemaModel/RootModel.cs @@ -66,16 +66,19 @@ public void AddElement(BaseSchemaModel model) internal void WriteCode(CodeWriter writer, CompileContext context) { - writer.AppendLine($@" -//------------------------------------------------------------------------------ -// -// This code was generated by the FlatSharp FBS to C# compiler (source hash: {context.InputHash}) -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ -"); + if (!context.Options.SuppressAutoGenerated) + { + writer.AppendLine($$""" + //------------------------------------------------------------------------------ + // + // This code was generated by the FlatSharp FBS to C# compiler (source hash: {{context.InputHash}}) + // + // Changes to this file may cause incorrect behavior and will be lost if + // the code is regenerated. + // + //------------------------------------------------------------------------------ + """); + } writer.AppendLine("using System;"); writer.AppendLine("using System.Collections.Generic;"); diff --git a/src/Tests/FlatSharpEndToEndTests/ClassLib/NonScalarVectorTests.cs b/src/Tests/FlatSharpEndToEndTests/ClassLib/NonScalarVectorTests.cs index 1193af73..b9ac8e03 100644 --- a/src/Tests/FlatSharpEndToEndTests/ClassLib/NonScalarVectorTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/ClassLib/NonScalarVectorTests.cs @@ -114,4 +114,94 @@ private void TestType(FlatBufferDeserializationOption } } } +} + +public interface ITable where T : IEquatable +{ + IList Vector { get; set; } +} + + +public interface IReadOnlyTable where T : IEquatable +{ + IReadOnlyList Vector { get; set; } +} + + +public partial class Root : ITable, ITable, ITable +{ + IList ITable.Vector + { + get => this.StringVector; + set => this.StringVector = value; + } + + IList ITable.Vector + { + get => this.TableVector; + set => this.TableVector = value; + } + + IList ITable.Vector + { + get => this.StructVector; + set => this.StructVector = value; + } +} + +public partial class RootReadOnly : IReadOnlyTable, IReadOnlyTable, IReadOnlyTable +{ + IReadOnlyList IReadOnlyTable.Vector + { + get => this.StringVector; + set => this.StringVector = value; + } + + IReadOnlyList IReadOnlyTable.Vector + { + get => this.TableVector; + set => this.TableVector = value; + } + + IReadOnlyList IReadOnlyTable.Vector + { + get => this.StructVector; + set => this.StructVector = value; + } +} + +public partial class InnerTable : IEquatable +{ + public bool Equals(InnerTable other) + { + return other?.Value == this.Value; + } + + public override bool Equals(object obj) + { + return this.Equals(obj as InnerTable); + } + + public override int GetHashCode() + { + return this.Value.GetHashCode(); + } +} + +public partial class InnerStruct : IEquatable +{ + public bool Equals(InnerStruct other) + { + return other?.Value == this.Value; + } + + public override bool Equals(object obj) + { + return this.Equals(obj as InnerStruct); + } + + public override int GetHashCode() + { + return this.Value.GetHashCode(); + } } \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests/Documentation/DocumentationTestCases.cs b/src/Tests/FlatSharpEndToEndTests/Documentation/DocumentationTestCases.cs index f5285c09..7b8a8612 100644 --- a/src/Tests/FlatSharpEndToEndTests/Documentation/DocumentationTestCases.cs +++ b/src/Tests/FlatSharpEndToEndTests/Documentation/DocumentationTestCases.cs @@ -23,7 +23,7 @@ public class DocumentationTestCases public DocumentationTestCases() { this.xmlDoc = new System.Xml.XmlDocument(); - this.xmlDoc.Load("FlatSharpEndToEndTestsGenerated.xml"); + this.xmlDoc.Load("FlatSharpEndToEndTests.xml"); } [Fact] diff --git a/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj b/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj index 07ab6062..319db01f 100644 --- a/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj +++ b/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj @@ -12,7 +12,10 @@ FlatSharpTests true annotations + $(OutputPath)$(AssemblyName).xml CS1591 + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)\..\..\FlatSharp.Compiler\bin\$(Configuration)\net7.0\FlatSharp.Compiler.dll')) + true False True latest @@ -37,9 +40,16 @@ + + - + + + + + + diff --git a/src/Tests/FlatSharpEndToEndTests/Helpers.cs b/src/Tests/FlatSharpEndToEndTests/Helpers.cs index e4aef176..99b9d118 100644 --- a/src/Tests/FlatSharpEndToEndTests/Helpers.cs +++ b/src/Tests/FlatSharpEndToEndTests/Helpers.cs @@ -14,21 +14,12 @@ * limitations under the License. */ +using System.Threading; + namespace FlatSharpEndToEndTests; public static class Helpers { - public static IEnumerable AllOptions - { - get - { - yield return FlatBufferDeserializationOption.Lazy; - yield return FlatBufferDeserializationOption.Progressive; - yield return FlatBufferDeserializationOption.Greedy; - yield return FlatBufferDeserializationOption.GreedyMutable; - } - } - public static byte[] AllocateAndSerialize(this T item) where T : class, IFlatBufferSerializable { return item.AllocateAndSerialize(item.Serializer); @@ -63,5 +54,28 @@ public static T SerializeAndParse( byte[] buffer = item.AllocateAndSerialize(); return serializer.Parse(buffer); } -} + public static T SerializeAndParse( + this T item, + FlatBufferDeserializationOption option, + out byte[] buffer) where T : class, IFlatBufferSerializable + { + buffer = item.AllocateAndSerialize(); + return item.Serializer.Parse(buffer, option); + } + + public static void AssertSequenceEqual( + Span expected, + Span actual) + { + Assert.Equal(expected.Length, actual.Length); + + for (int i = 0; i < expected.Length; ++i) + { + if (expected[i] != actual[i]) + { + throw new Exception($"Buffers differed at position {i}. Expected = {expected[i]}. Actual = {actual[i]}"); + } + } + } +} \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests/IO/InputBufferTests.cs b/src/Tests/FlatSharpEndToEndTests/IO/InputBufferTests.cs index a2ddad70..a84fa153 100644 --- a/src/Tests/FlatSharpEndToEndTests/IO/InputBufferTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/IO/InputBufferTests.cs @@ -264,4 +264,20 @@ private void TestDeserialize(Func bufferBuilder Assert.Equal(i + 5, parsed.Memory.Span[i - 1]); } } -} \ No newline at end of file +} + +internal interface IMemoryTable +{ + ReadOnlyMemory Memory { get; set; } +} + +public partial class MemoryTable : IMemoryTable +{ + ReadOnlyMemory IMemoryTable.Memory { get => this.Memory.Value; set => this.Memory = MemoryMarshal.AsMemory(value); } +} + +public partial class ReadOnlyMemoryTable : IMemoryTable +{ + ReadOnlyMemory IMemoryTable.Memory { get => this.Memory.Value; set => this.Memory = value; } +} + diff --git a/src/Tests/FlatSharpEndToEndTests/PartialMethods/PartialMethodsTests.cs b/src/Tests/FlatSharpEndToEndTests/PartialMethods/PartialMethodsTests.cs index c75db4cb..800716f9 100644 --- a/src/Tests/FlatSharpEndToEndTests/PartialMethods/PartialMethodsTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/PartialMethods/PartialMethodsTests.cs @@ -38,4 +38,25 @@ public void OnInitialized(FlatBufferDeserializationOption option) Assert.Equal(option, parsed.OnInitializedContext.Value.DeserializationOption); Assert.Equal(option, parsed.S.OnInitializedContext.Value.DeserializationOption); } -} \ No newline at end of file +} + +public partial class Table +{ + public FlatBufferDeserializationContext? OnInitializedContext { get; private set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) + { + this.OnInitializedContext = context; + } +} + +public partial class Struct +{ + public FlatBufferDeserializationContext? OnInitializedContext { get; private set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) + { + this.OnInitializedContext = context; + } +} + diff --git a/src/Tests/FlatSharpEndToEndTests/TableMembers/TableMembersTests.cs b/src/Tests/FlatSharpEndToEndTests/TableMembers/TableMembersTests.cs index bf6d4505..dd9e352b 100644 --- a/src/Tests/FlatSharpEndToEndTests/TableMembers/TableMembersTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/TableMembers/TableMembersTests.cs @@ -168,4 +168,31 @@ private void RunTest(T expectedDefault, FlatBufferDeserializationOpti Assert.Equal(expectedDefault, parsed.ItemList[i]); } } -} \ No newline at end of file +} + +public interface ITypedTable where T : struct +{ + T ItemStandard { get; set; } + + T? ItemOptional { get; set; } + + T ItemWithDefault { get; set; } + + T ItemDeprecated { get; set; } + + IList? ItemList { get; set; } + + IReadOnlyList? ItemReadonlyList { get; set; } +} + +public partial class BoolTable : ITypedTable { } +public partial class ByteTable : ITypedTable { } +public partial class SByteTable : ITypedTable { } +public partial class ShortTable : ITypedTable { } +public partial class UShortTable : ITypedTable { } +public partial class IntTable : ITypedTable { } +public partial class UIntTable : ITypedTable { } +public partial class LongTable : ITypedTable { } +public partial class ULongTable : ITypedTable { } +public partial class FloatTable : ITypedTable { } +public partial class DoubleTable : ITypedTable { } \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTests_Generated.csproj b/src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTests_Generated.csproj deleted file mode 100644 index d005548f..00000000 --- a/src/Tests/FlatSharpEndToEndTests_Generated/FlatSharpEndToEndTests_Generated.csproj +++ /dev/null @@ -1,42 +0,0 @@ - - - - - false - true - net7.0;net6.0 - net472;net6.0;net7.0 - net7.0 - false - FlatSharpEndToEndTestsGenerated - FlatSharpTests - true - annotations - $(OutputPath)$(AssemblyName).xml - CS1591 - $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)\..\..\FlatSharp.Compiler\bin\$(Configuration)\net7.0\FlatSharp.Compiler.dll')) - true - False - True - latest - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/InputBufferTestsPartials.cs b/src/Tests/FlatSharpEndToEndTests_Generated/InputBufferTestsPartials.cs deleted file mode 100644 index f3cb4000..00000000 --- a/src/Tests/FlatSharpEndToEndTests_Generated/InputBufferTestsPartials.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2018 James Courtney - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; -using FlatSharp.Internal; - -namespace FlatSharpEndToEndTests.IO.InputBufferTests; - -public interface IMemoryTable -{ - ReadOnlyMemory Memory { get; set; } -} - -public partial class MemoryTable : IMemoryTable -{ - ReadOnlyMemory IMemoryTable.Memory { get => this.Memory.Value; set => this.Memory = MemoryMarshal.AsMemory(value); } -} - -public partial class ReadOnlyMemoryTable : IMemoryTable -{ - ReadOnlyMemory IMemoryTable.Memory { get => this.Memory.Value; set => this.Memory = value; } -} - diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/NonScalarVectorTestsPartials.cs b/src/Tests/FlatSharpEndToEndTests_Generated/NonScalarVectorTestsPartials.cs deleted file mode 100644 index 5f982b62..00000000 --- a/src/Tests/FlatSharpEndToEndTests_Generated/NonScalarVectorTestsPartials.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2018 James Courtney - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FlatSharpEndToEndTests.ClassLib.NonScalarVectorTests; - -public interface ITable where T : IEquatable -{ - IList Vector { get; set; } -} - - -public interface IReadOnlyTable where T : IEquatable -{ - IReadOnlyList Vector { get; set; } -} - - -public partial class Root : ITable, ITable, ITable -{ - IList ITable.Vector - { - get => this.StringVector; - set => this.StringVector = value; - } - - IList ITable.Vector - { - get => this.TableVector; - set => this.TableVector = value; - } - - IList ITable.Vector - { - get => this.StructVector; - set => this.StructVector = value; - } -} - -public partial class RootReadOnly : IReadOnlyTable, IReadOnlyTable, IReadOnlyTable -{ - IReadOnlyList IReadOnlyTable.Vector - { - get => this.StringVector; - set => this.StringVector = value; - } - - IReadOnlyList IReadOnlyTable.Vector - { - get => this.TableVector; - set => this.TableVector = value; - } - - IReadOnlyList IReadOnlyTable.Vector - { - get => this.StructVector; - set => this.StructVector = value; - } -} - -public partial class InnerTable : IEquatable -{ - public bool Equals(InnerTable other) - { - return other?.Value == this.Value; - } - - public override bool Equals(object obj) - { - return this.Equals(obj as InnerTable); - } - - public override int GetHashCode() - { - return this.Value.GetHashCode(); - } -} - -public partial class InnerStruct : IEquatable -{ - public bool Equals(InnerStruct other) - { - return other?.Value == this.Value; - } - - public override bool Equals(object obj) - { - return this.Equals(obj as InnerStruct); - } - - public override int GetHashCode() - { - return this.Value.GetHashCode(); - } -} \ No newline at end of file diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/PartialMethodsTestsPartials.cs b/src/Tests/FlatSharpEndToEndTests_Generated/PartialMethodsTestsPartials.cs deleted file mode 100644 index 736ad7ef..00000000 --- a/src/Tests/FlatSharpEndToEndTests_Generated/PartialMethodsTestsPartials.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2018 James Courtney - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using FlatSharp; - -namespace FlatSharpEndToEndTests.PartialMethods; - -public partial class Table -{ - public FlatBufferDeserializationContext? OnInitializedContext { get; private set; } - - partial void OnInitialized(FlatBufferDeserializationContext? context) - { - this.OnInitializedContext = context; - } -} - -public partial class Struct -{ - public FlatBufferDeserializationContext? OnInitializedContext { get; private set; } - - partial void OnInitialized(FlatBufferDeserializationContext? context) - { - this.OnInitializedContext = context; - } -} - diff --git a/src/Tests/FlatSharpEndToEndTests_Generated/TableMembersTestsPartials.cs b/src/Tests/FlatSharpEndToEndTests_Generated/TableMembersTestsPartials.cs deleted file mode 100644 index ae884cbc..00000000 --- a/src/Tests/FlatSharpEndToEndTests_Generated/TableMembersTestsPartials.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2023 James Courtney - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Collections.Generic; - -namespace FlatSharpEndToEndTests.TableMembers; - -public interface ITypedTable where T : struct -{ - T ItemStandard { get; set; } - - T? ItemOptional { get; set; } - - T ItemWithDefault { get; set; } - - T ItemDeprecated { get; set; } - - IList? ItemList { get; set; } - - IReadOnlyList? ItemReadonlyList { get; set; } -} - -public partial class BoolTable : ITypedTable { } -public partial class ByteTable : ITypedTable { } -public partial class SByteTable : ITypedTable { } -public partial class ShortTable : ITypedTable { } -public partial class UShortTable : ITypedTable { } -public partial class IntTable : ITypedTable { } -public partial class UIntTable : ITypedTable { } -public partial class LongTable : ITypedTable { } -public partial class ULongTable : ITypedTable { } -public partial class FloatTable : ITypedTable { } -public partial class DoubleTable : ITypedTable { } \ No newline at end of file diff --git a/src/Tests/Stryker/CodeGen/CodeGen.csproj b/src/Tests/Stryker/CodeGen/CodeGen.csproj new file mode 100644 index 00000000..9db6bcf6 --- /dev/null +++ b/src/Tests/Stryker/CodeGen/CodeGen.csproj @@ -0,0 +1,17 @@ + + + + net7.0 + StrykerTestsCodeGen + FlatSharpStrykerTests + true + annotations + CS1591 + + + + + + + + diff --git a/src/Tests/Stryker/CodeGen/FlatSharp.cs b/src/Tests/Stryker/CodeGen/FlatSharp.cs new file mode 100644 index 00000000..e69de29b diff --git a/src/Tests/Stryker/CodeGen/Schema.fbs b/src/Tests/Stryker/CodeGen/Schema.fbs new file mode 100644 index 00000000..21870467 --- /dev/null +++ b/src/Tests/Stryker/CodeGen/Schema.fbs @@ -0,0 +1,108 @@ + +attribute "fs_serializer"; +attribute "fs_unsafeStructVector"; +attribute "fs_valueStruct"; +attribute "fs_writeThrough"; +attribute "fs_vector"; +attribute "fs_sharedString"; + +namespace FlatSharpStrykerTests; + +enum Fruit : uint +{ + Apple, + Banana, + Strawberry, + Pear +} + +/// Poorly Aligned. Total Size = 10, Alignment = 4 +struct ValueStruct (fs_valueStruct) +{ + /// Position 0, Alignment 4 + a : int; + + /// Position 4, Alignment 1 + b : byte; + + /// Position 6, Alignment 2 + c : [ short : 2 ] (fs_unsafeStructVector); +} + +/// Poorly aligned. Total Size == 26, Alignemnt = 8 +struct RefStruct +{ + /// Position 0, Alignment 8 + a : long; + + /// Position 8, Alignment 4 + b : Fruit; + + /// Position 12, Alignment 1 + c : [ byte : 2 ]; + + /// Position 16, Alignment 4 + d : ValueStruct; +} + +table Key +{ + /// Field 0 + name : string (key, required); + + /// Field 1 + value : Fruit = Banana; +} + +union FunUnion { RefStruct, ValueStruct, str : string, Key } + +table Root (fs_serializer) +{ + /// Field 0 + fields : Fields; + + /// Field 1 + vectors : Vectors; +} + +table Fields +{ + /// Field 0 + ref_struct : RefStruct; + + /// Field 1 + value_struct : ValueStruct; + + /// Field 2 + memory : ubyte; + + /// Field 3 + str : string; + + /// Field 4 + union : FunUnion; + + /// Field 5 + scalar_with_default : int = 3; +} + +table Vectors +{ + /// Field 0 + ref_struct : [ RefStruct ]; + + /// Field 1 + value_struct : [ ValueStruct ]; + + /// Field 2 + memory : [ ubyte ]; + + /// Field 3 + str : [ string ]; + + /// Field 4 + union : [ FunUnion ]; + + /// Field 5 + indexed : [ Key ] (fs_vector:"IIndexedVector"); +} \ No newline at end of file diff --git a/src/Tests/Stryker/Tests/GlobalUsings.cs b/src/Tests/Stryker/Tests/GlobalUsings.cs new file mode 100644 index 00000000..723f7c0a --- /dev/null +++ b/src/Tests/Stryker/Tests/GlobalUsings.cs @@ -0,0 +1,32 @@ + +/* + * Copyright 2021 James Courtney + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +global using System; +global using System.Buffers; +global using System.Buffers.Binary; +global using System.Collections; +global using System.Collections.Generic; +global using System.ComponentModel; +global using System.Diagnostics; +global using System.Diagnostics.CodeAnalysis; +global using System.Linq; +global using System.Reflection; +global using System.Runtime.CompilerServices; +global using FlatSharp; +global using FlatSharp.Attributes; +global using Xunit; +global using FlatSharpEndToEndTests; \ No newline at end of file diff --git a/src/Tests/Stryker/Tests/StructFieldTests.cs b/src/Tests/Stryker/Tests/StructFieldTests.cs new file mode 100644 index 00000000..a59cde9e --- /dev/null +++ b/src/Tests/Stryker/Tests/StructFieldTests.cs @@ -0,0 +1,148 @@ +namespace FlatSharpStrykerTests; + +public class StructFieldTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ValueStructTableField(FlatBufferDeserializationOption option) + { + ValueStruct vs = new() + { + A = 4, + B = 3, + }; + + vs.C(0) = 1; + vs.C(1) = 2; + + Assert.Throws(() => vs.C(2) = 4); + Assert.Throws(() => vs.C(-1) = 4); + + Root root = new Root + { + Fields = new() + { + ValueStruct = vs, + } + }; + + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Assert.NotNull(parsed.Fields); + Assert.Null(parsed.Vectors); + Assert.NotNull(parsed.Fields.ValueStruct); + + ValueStruct vsParsed = parsed.Fields.ValueStruct.Value; + + Assert.Equal(vs.A, vsParsed.A); + Assert.Equal(vs.B, vsParsed.B); + Assert.Equal(vs.C(0), vsParsed.C(0)); + Assert.Equal(vs.C(1), vsParsed.C(1)); + + byte[] expectedBytes = new byte[] + { + 4, 0, 0, 0, // offset to table start + 248, 255, 255, 255, // soffset to vtable. + 12, 0, 0, 0, // uoffset to field 0 (fields table) + 6, 0, // vtable length + 8, 0, // table length + 4, 0, // offset of field 0 + 0, 0, // padding + + 242, 255, 255, 255, // soffset to vtable + 4, 0, 0, 0, // valuestruct.a + 3, 0, // valuestruct.b, padding + 1, 0, // valuestruct.c[0] + 2, 0, // valuestruct.c[1] + + 8, 0, // vtable length + 14, 0, // table length + 0, 0, // field 0 (not present) + 4, 0, // field 1 + }; + + Helpers.AssertSequenceEqual(expectedBytes, buffer); + } + + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ReferenceStructTableField(FlatBufferDeserializationOption option) + { + ValueStruct vs = new() + { + A = 4, + B = 3, + }; + + vs.C(0) = 1; + vs.C(1) = 2; + + RefStruct rs = new() + { + A = 12, + B = Fruit.Pear, + D = vs + }; + + rs.C[0] = 2; + rs.C[1] = 3; + + Assert.Throws(() => rs.C[-1] = 4); + Assert.Throws(() => rs.C[3] = 4); + + Root root = new Root + { + Fields = new() + { + RefStruct = rs, + } + }; + + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Assert.NotNull(parsed.Fields); + Assert.Null(parsed.Vectors); + Assert.NotNull(parsed.Fields.RefStruct); + + RefStruct rsParsed = parsed.Fields.RefStruct; + Assert.Equal(rs.A, rsParsed.A); + Assert.Equal(rs.B, rsParsed.B); + Assert.Equal(rs.C[0], rsParsed.C[0]); + Assert.Equal(rs.C[1], rsParsed.C[1]); + + ValueStruct vsParsed = rsParsed.D; + Assert.Equal(vs.A, vsParsed.A); + Assert.Equal(vs.B, vsParsed.B); + Assert.Equal(vs.C(0), vsParsed.C(0)); + Assert.Equal(vs.C(1), vsParsed.C(1)); + + byte[] expectedBytes = new byte[] + { + 4, 0, 0, 0, // offset to table start + 248, 255, 255, 255, // soffset to vtable. + 12, 0, 0, 0, // uoffset to field 0 (fields table) + 6, 0, // vtable length + 8, 0, // table length + 4, 0, // offset of field 0 + 0, 0, // padding + + 226, 255, 255, 255, // soffset to vtable + + 12, 0, 0, 0, // refStruct.A (ulong) + 0, 0, 0, 0, + 3, 0, 0, 0, // refStruct.Fruit (pear) + 2, 3, 0, 0, // refStruct.C[0,1], padding + 4, 0, 0, 0, // valuestruct.a + 3, 0, // valuestruct.b, padding + 1, 0, // valuestruct.c[0] + 2, 0, // valuestruct.c[1] + + 6, 0, // vtable length + 30, 0, // table length + 4, 0, // field 0 + }; + + Helpers.AssertSequenceEqual(expectedBytes, buffer); + } +} diff --git a/src/Tests/Stryker/Tests/StrykerTests.csproj b/src/Tests/Stryker/Tests/StrykerTests.csproj new file mode 100644 index 00000000..55a3f637 --- /dev/null +++ b/src/Tests/Stryker/Tests/StrykerTests.csproj @@ -0,0 +1,32 @@ + + + net7.0 + false + FlatSharpStrykerTests + FlatSharpStrykerTests + annotations + CS1591 + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + From f446bf8ba96ff9bdca92575365d42efbf2a07aee Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 04:11:26 -0800 Subject: [PATCH 08/31] Update stryker.yml --- .github/workflows/stryker.yml | 53 +++++++++++++++-------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/.github/workflows/stryker.yml b/.github/workflows/stryker.yml index d2fab32d..67211ff6 100644 --- a/.github/workflows/stryker.yml +++ b/.github/workflows/stryker.yml @@ -12,45 +12,36 @@ jobs: runs-on: windows-2019 env: AppVeyorBuild: true - steps: - uses: actions/checkout@v2 - - name: Setup .NET 6 - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 6.0.x - - - name: Setup .NET 5 - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 5.0.x - - - name: Setup .NET 3 - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 3.1.x - - - name: Setup .NET 2 + - name: Setup .NET 7 uses: actions/setup-dotnet@v1 with: - dotnet-version: 2.1.x + dotnet-version: 7.0.x - - name: Restore dependencies - working-directory: src - run: dotnet restore - - name: Install Stryker run: dotnet tool install -g dotnet-stryker + + - name: Build FlatSharp.Compiler + working-directory: src/FlatSharp.Compiler + run: dotnet build -c Release + + - name: Run FlatSharp.Compiler (E2E Tests) + # You may pin to the exact commit or the version. + # uses: Amadevus/pwsh-script@97a8b211a5922816aa8a69ced41fa32f23477186 + uses: Amadevus/pwsh-script@v2.0.3 + with: + # PowerShell script to execute in Actions-hydrated context + script: | + $fbs = (gci -r src/tests/Stryker/*.fbs) -join ";" + dotnet src/FlatSharp.Compiler/bin/Release/net7.0/FlatSharp.Compiler.dll --nullable-warnings false --normalize-field-names true --input "$fbs" -o src/tests/Stryker/CodeGen --no-auto-generated - - name: Run Stryker FlatSharp - working-directory: src/FlatSharp - run: dotnet-stryker + - name: Build + working-directory: src/Tests/Stryker/Tests + run: dotnet build -c Release - - name: Run Stryker FlatSharp.Runtime - working-directory: src/FlatSharp.Runtime - run: dotnet-stryker + - name: Mutate + working-directory: src/Tests/Stryker/Tests + run: dotnet-stryker -p CodeGen.csproj - - name: Run Stryker FlatSharp.Compiler - working-directory: src/FlatSharp.Compiler - run: dotnet-stryker From 2f684b42fed2d8b02a00ef3235163b927fbbb260 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 04:17:32 -0800 Subject: [PATCH 09/31] fix csproj --- src/FlatSharp.sln | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/FlatSharp.sln b/src/FlatSharp.sln index ecce885e..d0f5af60 100644 --- a/src/FlatSharp.sln +++ b/src/FlatSharp.sln @@ -37,8 +37,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharpPoolableEndToEndTe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharp.UnityPolyfills", "FlatSharp.UnityPolyfills\FlatSharp.UnityPolyfills.csproj", "{E439E4AF-B948-4E6C-8791-1830619EB35B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharpEndToEndTests_Generated", "Tests\FlatSharpEndToEndTests_Generated\FlatSharpEndToEndTests_Generated.csproj", "{2BD45021-16C3-46AF-8D69-E2CDF137639D}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -101,10 +99,6 @@ Global {E439E4AF-B948-4E6C-8791-1830619EB35B}.Debug|Any CPU.Build.0 = Debug|Any CPU {E439E4AF-B948-4E6C-8791-1830619EB35B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E439E4AF-B948-4E6C-8791-1830619EB35B}.Release|Any CPU.Build.0 = Release|Any CPU - {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -120,7 +114,6 @@ Global {5A173CC8-53B4-445B-9AA2-68A61A4BE449} = {927A2D53-B127-4297-B17A-4E4EEE7ACD0D} {407BD242-0902-4850-9F53-17DF48F3DD6F} = {927A2D53-B127-4297-B17A-4E4EEE7ACD0D} {7E55A248-4BBD-48AD-B27D-A7E0E705DC89} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} - {2BD45021-16C3-46AF-8D69-E2CDF137639D} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {726A4C0E-5760-4B35-8C73-9AC21B2E4C8B} From 25158f85ba7c068d6273f2a4a02e13bfd0df7209 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 13:21:50 -0800 Subject: [PATCH 10/31] Remove dead project --- src/FlatSharp.sln | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/FlatSharp.sln b/src/FlatSharp.sln index ecce885e..d0f5af60 100644 --- a/src/FlatSharp.sln +++ b/src/FlatSharp.sln @@ -37,8 +37,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharpPoolableEndToEndTe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharp.UnityPolyfills", "FlatSharp.UnityPolyfills\FlatSharp.UnityPolyfills.csproj", "{E439E4AF-B948-4E6C-8791-1830619EB35B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharpEndToEndTests_Generated", "Tests\FlatSharpEndToEndTests_Generated\FlatSharpEndToEndTests_Generated.csproj", "{2BD45021-16C3-46AF-8D69-E2CDF137639D}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -101,10 +99,6 @@ Global {E439E4AF-B948-4E6C-8791-1830619EB35B}.Debug|Any CPU.Build.0 = Debug|Any CPU {E439E4AF-B948-4E6C-8791-1830619EB35B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E439E4AF-B948-4E6C-8791-1830619EB35B}.Release|Any CPU.Build.0 = Release|Any CPU - {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2BD45021-16C3-46AF-8D69-E2CDF137639D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -120,7 +114,6 @@ Global {5A173CC8-53B4-445B-9AA2-68A61A4BE449} = {927A2D53-B127-4297-B17A-4E4EEE7ACD0D} {407BD242-0902-4850-9F53-17DF48F3DD6F} = {927A2D53-B127-4297-B17A-4E4EEE7ACD0D} {7E55A248-4BBD-48AD-B27D-A7E0E705DC89} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} - {2BD45021-16C3-46AF-8D69-E2CDF137639D} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {726A4C0E-5760-4B35-8C73-9AC21B2E4C8B} From 56f6be1b8743100ecdb3f576115fcde7c26b8ded Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 13:44:41 -0800 Subject: [PATCH 11/31] Add config file --- src/Tests/Stryker/Tests/stryker-config.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/Tests/Stryker/Tests/stryker-config.json diff --git a/src/Tests/Stryker/Tests/stryker-config.json b/src/Tests/Stryker/Tests/stryker-config.json new file mode 100644 index 00000000..4c5232df --- /dev/null +++ b/src/Tests/Stryker/Tests/stryker-config.json @@ -0,0 +1,12 @@ +{ + "stryker-config": { + "project": "CodeGen.csproj", + + "ignore-methods": + [ + "*Assert*" // Ignore assertions. + ], + + "reporters": [ "html", "cleartext" ] + } +} From 663ebddb955393047c08117ead946b9c5402dea4 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 13:44:50 -0800 Subject: [PATCH 12/31] Update stryker.yml --- .github/workflows/stryker.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stryker.yml b/.github/workflows/stryker.yml index 67211ff6..a1da0413 100644 --- a/.github/workflows/stryker.yml +++ b/.github/workflows/stryker.yml @@ -43,5 +43,10 @@ jobs: - name: Mutate working-directory: src/Tests/Stryker/Tests - run: dotnet-stryker -p CodeGen.csproj + run: dotnet-stryker + - name: Upload Results + uses: actions/upload-artifact@v2 + with: + name: Stryker Results + path: ./**/StrykerOutput/**/*.* From cbb53fc679526eb2c1f69327ca83c168e81c5350 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 18:30:08 -0800 Subject: [PATCH 13/31] Tests --- .../IFlatBufferProgressiveObject.cs | 33 ++++ src/FlatSharp/FlatBufferSerializerOptions.cs | 3 - .../DeserializeClassDefinition.cs | 60 +++++++- .../RoslynSerializerGenerator.cs | 19 ++- src/FlatSharp/TypeModel/StructMemberModel.cs | 14 +- src/Tests/FlatSharpEndToEndTests/Helpers.cs | 79 ++++++++++ src/Tests/Stryker/CodeGen/Schema.fbs | 23 +-- src/Tests/Stryker/Tests/StructFieldTests.cs | 141 ++++++++++++++---- src/Tests/Stryker/Tests/stryker-config.json | 2 +- 9 files changed, 320 insertions(+), 54 deletions(-) create mode 100644 src/FlatSharp.Runtime/IFlatBufferProgressiveObject.cs diff --git a/src/FlatSharp.Runtime/IFlatBufferProgressiveObject.cs b/src/FlatSharp.Runtime/IFlatBufferProgressiveObject.cs new file mode 100644 index 00000000..8831269b --- /dev/null +++ b/src/FlatSharp.Runtime/IFlatBufferProgressiveObject.cs @@ -0,0 +1,33 @@ +/* + * Copyright 2021 James Courtney + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace FlatSharp.Internal; + +/// +/// An interface applied to progressive items derialized by FlatSharp. Used for testing. +/// +public interface IFlatBufferProgressiveObject +{ + /// + /// Indicates if the given field is present in the buffer. + /// + bool IsFieldPresent(int index); + + /// + /// Indicates if the given field is loaded in the object. + /// + bool IsFieldLoaded(int index); +} \ No newline at end of file diff --git a/src/FlatSharp/FlatBufferSerializerOptions.cs b/src/FlatSharp/FlatBufferSerializerOptions.cs index bccb577d..02da36a7 100644 --- a/src/FlatSharp/FlatBufferSerializerOptions.cs +++ b/src/FlatSharp/FlatBufferSerializerOptions.cs @@ -81,9 +81,6 @@ public FlatBufferSerializerOptions( /// /// Indicates if the object is immutable OR changes to the object are guaranteed to be written back to the buffer. /// - /// - /// VectorCacheMutable is not eligible here, since only some changes are written back. - /// public bool CanSerializeWithMemoryCopy => this.DeserializationOption == FlatBufferDeserializationOption.Lazy || this.DeserializationOption == FlatBufferDeserializationOption.Progressive; diff --git a/src/FlatSharp/Serialization/DeserializeClassDefinition.cs b/src/FlatSharp/Serialization/DeserializeClassDefinition.cs index 4c501d06..3bb26e6a 100644 --- a/src/FlatSharp/Serialization/DeserializeClassDefinition.cs +++ b/src/FlatSharp/Serialization/DeserializeClassDefinition.cs @@ -284,6 +284,7 @@ public override string ToString() } string interfaceGlobalName = typeof(IFlatBufferDeserializedObject).GetGlobalCompilableTypeName(); + bool isProgressive = this.options.DeserializationOption == FlatBufferDeserializationOption.Progressive; return $@" @@ -292,7 +293,7 @@ private sealed class {this.ClassName} , {interfaceGlobalName} , {typeof(IPoolableObject).GetGlobalCompilableTypeName()} , {typeof(IPoolableObjectDebug).GetGlobalCompilableTypeName()} - + {(isProgressive ? $", {typeof(IFlatBufferProgressiveObject).GetGlobalCompilableTypeName()}" : string.Empty)} where TInputBuffer : IInputBuffer {{ private static readonly {typeof(FlatBufferDeserializationContext).GetGlobalCompilableTypeName()} {CtorContextVariableName} @@ -324,12 +325,65 @@ private sealed class {this.ClassName} set => this.{IsRootVariableName} = value; }} + {( + this.options.DeserializationOption == FlatBufferDeserializationOption.Progressive + ? this.ImplementProgressiveReflectionMethods() + : string.Empty + )} + {string.Join("\r\n", this.propertyOverrides)} {string.Join("\r\n", this.readMethods)} }} "; } + protected virtual string ImplementProgressiveReflectionMethods() + { + FlatSharpInternal.Assert(this.options.DeserializationOption == FlatBufferDeserializationOption.Progressive, "expecting progressive"); + + string interfaceName = typeof(IFlatBufferProgressiveObject).GetGlobalCompilableTypeName(); + + string isLoaded = this.options.DeserializationOption switch + { + FlatBufferDeserializationOption.Lazy => "return false;", + FlatBufferDeserializationOption.Greedy or FlatBufferDeserializationOption.GreedyMutable => "return true;", + FlatBufferDeserializationOption.Progressive => + string.Join("\r\n", + this.itemModels + .Select( + imm => $"if (index == {imm.Index}) {{ return ({GetHasValueFieldName(imm)} & {GetHasValueFieldMask(imm)}) != 0; }}") + .Concat(new[] { "return false;" })), + _ => string.Empty, // won't compile + }; + + if (this.typeModel.SchemaType == FlatBufferSchemaType.Table) + { + return $$""" + [{{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}}] + bool {{interfaceName}}.IsFieldPresent(int index) => {{vtableAccessor}}.OffsetOf(this.{{InputBufferVariableName}}, index) != 0; + + [{{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}}] + bool {{interfaceName}}.IsFieldLoaded(int index) + { + {{isLoaded}} + } + """; + } + else + { + return $$""" + [{{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}}] + bool {{interfaceName}}.IsFieldPresent(int index) => true; + + [{{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}}] + bool {{interfaceName}}.IsFieldLoaded(int index) + { + {{isLoaded}} + } + """; + } + } + protected virtual string GetSetterBody(ItemMemberModel itemModel) { List setterLines = new List(); @@ -538,9 +592,7 @@ public override void ReturnToPool(bool unsafeForce = false) protected static string GetFieldName(ItemMemberModel itemModel) => $"__index{itemModel.Index}Value"; - protected static string GetHasValueFieldName(int index) => $"__mask{index}"; - - protected static string GetHasValueFieldName(ItemMemberModel itemModel) => GetHasValueFieldName(itemModel.Index / 8); + protected static string GetHasValueFieldName(ItemMemberModel itemModel) => $"__mask{itemModel.Index / 8}"; protected static string GetHasValueFieldMask(ItemMemberModel itemModel) => $"(byte){1 << (itemModel.Index % 8)}"; diff --git a/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs b/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs index 63efda25..16f88903 100644 --- a/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs +++ b/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs @@ -485,6 +485,7 @@ namespace {resolvedName.@namespace} internal class {resolvedName.name} : {nameof(IGeneratedSerializer)}<{rootType.GetGlobalCompilableTypeName()}> {{ // Method generated to help AOT compilers make good decisions about generics. + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public void __AotHelper() {{ this.Write(default!, new byte[10], default!, default!); @@ -657,12 +658,13 @@ private static SyntaxNode ApplySyntaxTransformations(SyntaxNode rootNode) rootNode.DescendantNodes().OfType(), (a, b) => { - if (a.Body != null) + // Ignore empty methods. + if (b.Body == null || b.Body.Statements.Count == 0) { - return b.WithBody(SyntaxFactory.Block(SyntaxFactory.CheckedStatement(SyntaxKind.CheckedStatement, a.Body))); + return a; } - return a; + return b.WithBody(SyntaxFactory.Block(SyntaxFactory.CheckedStatement(SyntaxKind.CheckedStatement, b.Body))); }); // Add checked{} to constructors. @@ -670,7 +672,12 @@ private static SyntaxNode ApplySyntaxTransformations(SyntaxNode rootNode) rootNode.DescendantNodes().OfType(), (a, b) => { - return b.WithBody(SyntaxFactory.Block(SyntaxFactory.CheckedStatement(SyntaxKind.CheckedStatement, a.Body))); + // Ignore empty methods. + if (b.Body == null || b.Body.Statements.Count == 0) + { + return a; + } + return b.WithBody(SyntaxFactory.Block(SyntaxFactory.CheckedStatement(SyntaxKind.CheckedStatement, b.Body))); }); // Add checked{} to property accessors. @@ -678,11 +685,11 @@ private static SyntaxNode ApplySyntaxTransformations(SyntaxNode rootNode) rootNode.DescendantNodes().OfType(), (a, b) => { - if (b.Body == null) + // Ignore empty methods. + if (b.Body == null || b.Body.Statements.Count == 0) { return a; } - return b.WithBody(SyntaxFactory.Block(SyntaxFactory.CheckedStatement(SyntaxKind.CheckedStatement, b.Body))); }); diff --git a/src/FlatSharp/TypeModel/StructMemberModel.cs b/src/FlatSharp/TypeModel/StructMemberModel.cs index ec8ac90d..a10f65e8 100644 --- a/src/FlatSharp/TypeModel/StructMemberModel.cs +++ b/src/FlatSharp/TypeModel/StructMemberModel.cs @@ -70,7 +70,7 @@ public override string CreateReadItemBody( { context = context with { - OffsetVariableName = $"{context.OffsetVariableName} + {this.Offset}", + OffsetVariableName = $"{context.OffsetVariableName}{GetOffsetAdjustment(this.Offset)}", }; return $"return {context.GetParseInvocation(this.ItemTypeModel.ClrType)};"; @@ -82,9 +82,19 @@ public override string CreateWriteThroughBody( { context = context with { - OffsetVariableName = $"{context.OffsetVariableName} + {this.Offset}" + OffsetVariableName = $"{context.OffsetVariableName}{GetOffsetAdjustment(this.Offset)}" }; return context.GetSerializeInvocation(this.ItemTypeModel.ClrType) + ";"; } + + private static string GetOffsetAdjustment(int offset) + { + if (offset == 0) + { + return string.Empty; + } + + return $"+ {offset}"; + } } diff --git a/src/Tests/FlatSharpEndToEndTests/Helpers.cs b/src/Tests/FlatSharpEndToEndTests/Helpers.cs index 99b9d118..b00de275 100644 --- a/src/Tests/FlatSharpEndToEndTests/Helpers.cs +++ b/src/Tests/FlatSharpEndToEndTests/Helpers.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using System.Linq.Expressions; using System.Threading; namespace FlatSharpEndToEndTests; @@ -78,4 +79,82 @@ public static void AssertSequenceEqual( } } } + + public static void AssertMutationWorks( + FlatBufferDeserializationOption option, + TSource parent, + bool isWriteThrough, + Expression> propertyLambda, + TProperty newValue) + { + MemberExpression member = propertyLambda.Body as MemberExpression; + PropertyInfo propInfo = member.Member as PropertyInfo; + Action action = () => propInfo.SetMethod.Invoke(parent, new object[] { newValue }); + + switch (option) + { + case FlatBufferDeserializationOption.Lazy when isWriteThrough: + case FlatBufferDeserializationOption.Progressive when isWriteThrough: + case FlatBufferDeserializationOption.GreedyMutable when isWriteThrough is false: + action(); + + // For value types, validate that they are the same. + if (typeof(TProperty).IsValueType) + { + TProperty readValue = (TProperty)propInfo.GetMethod.Invoke(parent, null); + Assert.Equal(newValue, readValue); + } + else if (option != FlatBufferDeserializationOption.Lazy) + { + TProperty readValue = (TProperty)propInfo.GetMethod.Invoke(parent, null); + Assert.True(object.ReferenceEquals(newValue, readValue)); + } + + return; + + default: + Assert.Throws(new Action(() => + { + var ex = Assert.Throws(action).InnerException; + throw ex; + })); + + return; + } + } + + + public static void AssertMutationWorks( + FlatBufferDeserializationOption option, + TSource parent, + TProperty newValue, + bool isWriteThrough, + Func getValue, + Action setValue) + { + switch (option) + { + case FlatBufferDeserializationOption.Lazy when isWriteThrough: + case FlatBufferDeserializationOption.Progressive when isWriteThrough: + case FlatBufferDeserializationOption.GreedyMutable when isWriteThrough is false: + setValue(parent, newValue); + TProperty readValue = getValue(parent); + + // For value types, validate that they are the same. + if (typeof(TProperty).IsValueType) + { + Assert.Equal(newValue, readValue); + } + else if (option != FlatBufferDeserializationOption.Lazy) + { + Assert.True(object.ReferenceEquals(newValue, readValue)); + } + + return; + + default: + Assert.Throws(() => setValue(parent, newValue)); + return; + } + } } \ No newline at end of file diff --git a/src/Tests/Stryker/CodeGen/Schema.fbs b/src/Tests/Stryker/CodeGen/Schema.fbs index 21870467..cbb9871f 100644 --- a/src/Tests/Stryker/CodeGen/Schema.fbs +++ b/src/Tests/Stryker/CodeGen/Schema.fbs @@ -19,30 +19,33 @@ enum Fruit : uint /// Poorly Aligned. Total Size = 10, Alignment = 4 struct ValueStruct (fs_valueStruct) { - /// Position 0, Alignment 4 + /// Position 0, Length 4, Alignment 4 a : int; - /// Position 4, Alignment 1 + /// Position 4, Length 1, Alignment 1 b : byte; - /// Position 6, Alignment 2 + /// Position 6, Length 4, Alignment 2 c : [ short : 2 ] (fs_unsafeStructVector); } /// Poorly aligned. Total Size == 26, Alignemnt = 8 struct RefStruct { - /// Position 0, Alignment 8 - a : long; + /// Position 0, Length 8, Alignment 8, WriteThrough = true + a : long (fs_writeThrough); - /// Position 8, Alignment 4 + /// Position 8, Length 4, Alignment 4, WriteThrough = false b : Fruit; - /// Position 12, Alignment 1 - c : [ byte : 2 ]; + /// Position 12, Length 2, Alignment 1, WriteThrough = true + c : [ byte : 2 ] (fs_writeThrough); - /// Position 16, Alignment 4 - d : ValueStruct; + /// Position 14, length 2, Alignment 1, WriteThrough = false + d : [ byte : 2 ]; + + /// Position 16, Length 10, Alignment 4, WriteThrough = false + e : ValueStruct; } table Key diff --git a/src/Tests/Stryker/Tests/StructFieldTests.cs b/src/Tests/Stryker/Tests/StructFieldTests.cs index a59cde9e..72bd5f9a 100644 --- a/src/Tests/Stryker/Tests/StructFieldTests.cs +++ b/src/Tests/Stryker/Tests/StructFieldTests.cs @@ -1,4 +1,7 @@ -namespace FlatSharpStrykerTests; +using FlatSharp.Internal; +using System.Linq.Expressions; + +namespace FlatSharpStrykerTests; public class StructFieldTests { @@ -69,7 +72,108 @@ public void ValueStructTableField(FlatBufferDeserializationOption option) [ClassData(typeof(DeserializationOptionClassData))] public void ReferenceStructTableField(FlatBufferDeserializationOption option) { - ValueStruct vs = new() + Root root = CreateRootReferenceStruct(out RefStruct rs, out ValueStruct vs, out byte[] expectedBytes); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Assert.Throws(() => rs.C[-1] = 4); + Assert.Throws(() => rs.D[3] = 4); + + Assert.NotNull(parsed.Fields); + Assert.Null(parsed.Vectors); + Assert.NotNull(parsed.Fields.RefStruct); + + RefStruct rsParsed = parsed.Fields.RefStruct; + Assert.Equal(rs.A, rsParsed.A); + Assert.Equal(rs.B, rsParsed.B); + Assert.Equal(rs.C[0], rsParsed.C[0]); + Assert.Equal(rs.C[1], rsParsed.C[1]); + Assert.Equal(rs.D[0], rsParsed.D[0]); + Assert.Equal(rs.D[1], rsParsed.D[1]); + + ValueStruct vsParsed = rsParsed.E; + Assert.Equal(vs.A, vsParsed.A); + Assert.Equal(vs.B, vsParsed.B); + Assert.Equal(vs.C(0), vsParsed.C(0)); + Assert.Equal(vs.C(1), vsParsed.C(1)); + + Helpers.AssertSequenceEqual(expectedBytes, buffer); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ReferenceStructWriteThrough(FlatBufferDeserializationOption option) + { + Root root = CreateRootReferenceStruct(out RefStruct rs, out ValueStruct vs, out byte[] expectedBytes); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + RefStruct rsp = parsed.Fields.RefStruct; + + Helpers.AssertMutationWorks(option, rsp, true, rsp => rsp.A, 10); + Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.B, Fruit.Strawberry); + Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.E, new ValueStruct()); + + Helpers.AssertMutationWorks(option, rsp, 10, true, x => x.C[0], (x, y) => x.C[0] = y); + Helpers.AssertMutationWorks(option, rsp, 11, true, x => x.C[1], (x, y) => x.C[1] = y); + Helpers.AssertMutationWorks(option, rsp, 13, false, x => x.D[0], (x, y) => x.D[0] = y); + Helpers.AssertMutationWorks(option, rsp, 14, false, x => x.D[1], (x, y) => x.D[1] = y); + + var parsed2 = Root.Serializer.Parse(buffer, option); + + Assert.Equal(rsp.A, parsed2.Fields.RefStruct.A); + Assert.Equal(rsp.C[0], parsed2.Fields.RefStruct.C[0]); + Assert.Equal(rsp.C[1], parsed2.Fields.RefStruct.C[1]); + } + + [Fact] + public void ProgressiveFieldLoads() + { + static T LoadAndVerify(int index, bool expected, P parent, Func getter) + { + Assert.False(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); + Assert.Equal(expected, ((IFlatBufferProgressiveObject)parent).IsFieldPresent(index)); + + T item = getter(parent); + + for (int i = 0; i < 10; ++i) + { + Assert.True(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); + T next = getter(parent); + + if (typeof(T).IsValueType) + { + Assert.Equal(item, next); + } + else + { + Assert.True(object.ReferenceEquals(item, next)); + } + } + + return item; + } + + Root root = CreateRootReferenceStruct(out RefStruct rs, out ValueStruct vs, out byte[] expectedBytes); + Root parsed = root.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer); + + IFlatBufferProgressiveObject progressive = (IFlatBufferProgressiveObject)parsed; + + Fields fields = LoadAndVerify(0, true, parsed, p => p.Fields); + Assert.NotNull(fields); + + { + RefStruct @ref = LoadAndVerify(0, true, fields, f => f.RefStruct); + Assert.NotNull(@ref); + + Assert.Equal(root.Fields.RefStruct.A, LoadAndVerify(0, true, @ref, r => r.A)); + Assert.Equal(root.Fields.RefStruct.B, LoadAndVerify(1, true, @ref, r => r.B)); + + LoadAndVerify(6, true, @ref, r => r.E); + } + } + + private static Root CreateRootReferenceStruct(out RefStruct rs, out ValueStruct vs, out byte[] expectedBuffer) + { + vs = new() { A = 4, B = 3, @@ -78,18 +182,17 @@ public void ReferenceStructTableField(FlatBufferDeserializationOption option) vs.C(0) = 1; vs.C(1) = 2; - RefStruct rs = new() + rs = new() { A = 12, B = Fruit.Pear, - D = vs + E = vs }; rs.C[0] = 2; rs.C[1] = 3; - - Assert.Throws(() => rs.C[-1] = 4); - Assert.Throws(() => rs.C[3] = 4); + rs.D[0] = 4; + rs.D[1] = 5; Root root = new Root { @@ -99,25 +202,7 @@ public void ReferenceStructTableField(FlatBufferDeserializationOption option) } }; - Root parsed = root.SerializeAndParse(option, out byte[] buffer); - - Assert.NotNull(parsed.Fields); - Assert.Null(parsed.Vectors); - Assert.NotNull(parsed.Fields.RefStruct); - - RefStruct rsParsed = parsed.Fields.RefStruct; - Assert.Equal(rs.A, rsParsed.A); - Assert.Equal(rs.B, rsParsed.B); - Assert.Equal(rs.C[0], rsParsed.C[0]); - Assert.Equal(rs.C[1], rsParsed.C[1]); - - ValueStruct vsParsed = rsParsed.D; - Assert.Equal(vs.A, vsParsed.A); - Assert.Equal(vs.B, vsParsed.B); - Assert.Equal(vs.C(0), vsParsed.C(0)); - Assert.Equal(vs.C(1), vsParsed.C(1)); - - byte[] expectedBytes = new byte[] + expectedBuffer = new byte[] { 4, 0, 0, 0, // offset to table start 248, 255, 255, 255, // soffset to vtable. @@ -132,7 +217,7 @@ public void ReferenceStructTableField(FlatBufferDeserializationOption option) 12, 0, 0, 0, // refStruct.A (ulong) 0, 0, 0, 0, 3, 0, 0, 0, // refStruct.Fruit (pear) - 2, 3, 0, 0, // refStruct.C[0,1], padding + 2, 3, 4, 5, // refStruct.C[0,1], refStruct.D[0,1] 4, 0, 0, 0, // valuestruct.a 3, 0, // valuestruct.b, padding 1, 0, // valuestruct.c[0] @@ -143,6 +228,6 @@ public void ReferenceStructTableField(FlatBufferDeserializationOption option) 4, 0, // field 0 }; - Helpers.AssertSequenceEqual(expectedBytes, buffer); + return root; } } diff --git a/src/Tests/Stryker/Tests/stryker-config.json b/src/Tests/Stryker/Tests/stryker-config.json index 4c5232df..6b027876 100644 --- a/src/Tests/Stryker/Tests/stryker-config.json +++ b/src/Tests/Stryker/Tests/stryker-config.json @@ -4,7 +4,7 @@ "ignore-methods": [ - "*Assert*" // Ignore assertions. + "FlatSharp.Internal.FlatSharpInternal.AssertSizeOf" // Ignore assertions. ], "reporters": [ "html", "cleartext" ] From e1516d3732e7a3157e7499e2578be3b3796a033f Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 20:09:59 -0800 Subject: [PATCH 14/31] Update stryker.yml --- .github/workflows/stryker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stryker.yml b/.github/workflows/stryker.yml index a1da0413..c60a8295 100644 --- a/.github/workflows/stryker.yml +++ b/.github/workflows/stryker.yml @@ -35,7 +35,7 @@ jobs: # PowerShell script to execute in Actions-hydrated context script: | $fbs = (gci -r src/tests/Stryker/*.fbs) -join ";" - dotnet src/FlatSharp.Compiler/bin/Release/net7.0/FlatSharp.Compiler.dll --nullable-warnings false --normalize-field-names true --input "$fbs" -o src/tests/Stryker/CodeGen --no-auto-generated + dotnet src/FlatSharp.Compiler/bin/Release/net7.0/FlatSharp.Compiler.dll --nullable-warnings false --normalize-field-names true --input "$fbs" -o src/tests/Stryker/CodeGen --mutation-testing-mode - name: Build working-directory: src/Tests/Stryker/Tests From 83f75ff6ad766b0ae30a457b88d107c7aaf95764 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 20:10:53 -0800 Subject: [PATCH 15/31] Rename --- src/FlatSharp.Compiler/CompilerOptions.cs | 4 +-- src/FlatSharp.Compiler/FlatSharpCompiler.cs | 5 +++- .../SchemaModel/RootModel.cs | 2 +- .../StructVectorPropertyFieldModel.cs | 2 +- src/Tests/Stryker/CodeGen/RefStructPartial.cs | 26 +++++++++++++++++ src/Tests/Stryker/Tests/StructFieldTests.cs | 29 ++++++++++++++----- 6 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 src/Tests/Stryker/CodeGen/RefStructPartial.cs diff --git a/src/FlatSharp.Compiler/CompilerOptions.cs b/src/FlatSharp.Compiler/CompilerOptions.cs index e7b7cbaf..62609437 100644 --- a/src/FlatSharp.Compiler/CompilerOptions.cs +++ b/src/FlatSharp.Compiler/CompilerOptions.cs @@ -51,6 +51,6 @@ public record CompilerOptions public bool Instrument { get; set; } // Suppress auto generated markers for mutation testing. - [Option("no-auto-generated", Hidden = true, Default = false)] - public bool SuppressAutoGenerated { get; set; } + [Option("mutation-testing-mode", Hidden = true, Default = false)] + public bool MutationTestingMode { get; set; } } diff --git a/src/FlatSharp.Compiler/FlatSharpCompiler.cs b/src/FlatSharp.Compiler/FlatSharpCompiler.cs index b0fbad65..b4e542fa 100644 --- a/src/FlatSharp.Compiler/FlatSharpCompiler.cs +++ b/src/FlatSharp.Compiler/FlatSharpCompiler.cs @@ -32,6 +32,8 @@ public class FlatSharpCompiler { public const string FailureMessage = "// !! FLATSHARP CODE GENERATION FAILED. THIS FILE MAY CONTAIN INCOMPLETE OR INACCURATE DATA !!"; + internal static CompilerOptions? CommandLineOptions { get; private set; } + private static string AssemblyVersion => typeof(ISchemaMutator).Assembly.GetCustomAttribute()?.Version ?? "unknown"; [ExcludeFromCodeCoverage] @@ -44,6 +46,7 @@ static int Main(string[] args) CommandLine.Parser.Default.ParseArguments(args) .WithParsed(x => { + CommandLineOptions = x; exitCode = RunCompiler(x); }); } @@ -83,7 +86,7 @@ private static int RunCompiler(CompilerOptions options) } // Read existing file to see if we even need to do any work. - string outputFileName = options.SuppressAutoGenerated switch + string outputFileName = options.MutationTestingMode switch { true => "FlatSharp.cs", false => "FlatSharp.generated.cs", diff --git a/src/FlatSharp.Compiler/SchemaModel/RootModel.cs b/src/FlatSharp.Compiler/SchemaModel/RootModel.cs index a77245c2..a4723153 100644 --- a/src/FlatSharp.Compiler/SchemaModel/RootModel.cs +++ b/src/FlatSharp.Compiler/SchemaModel/RootModel.cs @@ -66,7 +66,7 @@ public void AddElement(BaseSchemaModel model) internal void WriteCode(CodeWriter writer, CompileContext context) { - if (!context.Options.SuppressAutoGenerated) + if (!context.Options.MutationTestingMode) { writer.AppendLine($$""" //------------------------------------------------------------------------------ diff --git a/src/FlatSharp.Compiler/SchemaModel/StructVectorPropertyFieldModel.cs b/src/FlatSharp.Compiler/SchemaModel/StructVectorPropertyFieldModel.cs index 7a1e330e..996bbaac 100644 --- a/src/FlatSharp.Compiler/SchemaModel/StructVectorPropertyFieldModel.cs +++ b/src/FlatSharp.Compiler/SchemaModel/StructVectorPropertyFieldModel.cs @@ -55,7 +55,7 @@ public StructVectorPropertyFieldModel( $"{field.Name}[{i}]", modifiedAttributes) { - ProtectedGetter = true, + ProtectedGetter = FlatSharpCompiler.CommandLineOptions?.MutationTestingMode != true, }; propertyModels.Add(model); diff --git a/src/Tests/Stryker/CodeGen/RefStructPartial.cs b/src/Tests/Stryker/CodeGen/RefStructPartial.cs new file mode 100644 index 00000000..aa8aa46a --- /dev/null +++ b/src/Tests/Stryker/CodeGen/RefStructPartial.cs @@ -0,0 +1,26 @@ +using FlatSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FlatSharpStrykerTests; + +public interface IReferenceItem +{ + static abstract bool IsStaticInitialized { get; } + + bool IsInitialized { get; } +} + +public partial class RefStruct : IReferenceItem +{ + public static bool IsStaticInitialized { get; set; } + + public bool IsInitialized { get; set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) => this.IsInitialized = true; + + static partial void OnStaticInitialize() => IsStaticInitialized = true; +} diff --git a/src/Tests/Stryker/Tests/StructFieldTests.cs b/src/Tests/Stryker/Tests/StructFieldTests.cs index 72bd5f9a..239d988f 100644 --- a/src/Tests/Stryker/Tests/StructFieldTests.cs +++ b/src/Tests/Stryker/Tests/StructFieldTests.cs @@ -110,13 +110,12 @@ public void ReferenceStructWriteThrough(FlatBufferDeserializationOption option) Helpers.AssertMutationWorks(option, rsp, true, rsp => rsp.A, 10); Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.B, Fruit.Strawberry); + Helpers.AssertMutationWorks(option, rsp, true, rsp => rsp.__flatsharp__C_0, (sbyte)3); + Helpers.AssertMutationWorks(option, rsp, true, rsp => rsp.__flatsharp__C_1, (sbyte)6); + Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.__flatsharp__D_0, (sbyte)3); + Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.__flatsharp__D_1, (sbyte)6); Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.E, new ValueStruct()); - Helpers.AssertMutationWorks(option, rsp, 10, true, x => x.C[0], (x, y) => x.C[0] = y); - Helpers.AssertMutationWorks(option, rsp, 11, true, x => x.C[1], (x, y) => x.C[1] = y); - Helpers.AssertMutationWorks(option, rsp, 13, false, x => x.D[0], (x, y) => x.D[0] = y); - Helpers.AssertMutationWorks(option, rsp, 14, false, x => x.D[1], (x, y) => x.D[1] = y); - var parsed2 = Root.Serializer.Parse(buffer, option); Assert.Equal(rsp.A, parsed2.Fields.RefStruct.A); @@ -124,6 +123,19 @@ public void ReferenceStructWriteThrough(FlatBufferDeserializationOption option) Assert.Equal(rsp.C[1], parsed2.Fields.RefStruct.C[1]); } + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ReferenceStructOnDeserialized(FlatBufferDeserializationOption option) + { + Root root = CreateRootReferenceStruct(out RefStruct rs, out ValueStruct vs, out byte[] expectedBytes); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + RefStruct rsp = parsed.Fields.RefStruct; + + Assert.True(rsp.IsInitialized); + Assert.True(RefStruct.IsStaticInitialized); + } + [Fact] public void ProgressiveFieldLoads() { @@ -162,11 +174,12 @@ static T LoadAndVerify(int index, bool expected, P parent, Func gett { RefStruct @ref = LoadAndVerify(0, true, fields, f => f.RefStruct); - Assert.NotNull(@ref); - Assert.Equal(root.Fields.RefStruct.A, LoadAndVerify(0, true, @ref, r => r.A)); Assert.Equal(root.Fields.RefStruct.B, LoadAndVerify(1, true, @ref, r => r.B)); - + Assert.Equal(root.Fields.RefStruct.C[0], LoadAndVerify(2, true, @ref, r => r.__flatsharp__C_0)); + Assert.Equal(root.Fields.RefStruct.C[1], LoadAndVerify(3, true, @ref, r => r.__flatsharp__C_1)); + Assert.Equal(root.Fields.RefStruct.D[0], LoadAndVerify(4, true, @ref, r => r.__flatsharp__D_0)); + Assert.Equal(root.Fields.RefStruct.D[1], LoadAndVerify(5, true, @ref, r => r.__flatsharp__D_1)); LoadAndVerify(6, true, @ref, r => r.E); } } From 10584a70bed8678e80a3056d9c4145c79b42d0be Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 21:58:43 -0800 Subject: [PATCH 16/31] Update stryker.yml --- .github/workflows/stryker.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stryker.yml b/.github/workflows/stryker.yml index c60a8295..ff68262e 100644 --- a/.github/workflows/stryker.yml +++ b/.github/workflows/stryker.yml @@ -27,7 +27,7 @@ jobs: working-directory: src/FlatSharp.Compiler run: dotnet build -c Release - - name: Run FlatSharp.Compiler (E2E Tests) + - name: Run FlatSharp.Compiler (Mutation Tests) # You may pin to the exact commit or the version. # uses: Amadevus/pwsh-script@97a8b211a5922816aa8a69ced41fa32f23477186 uses: Amadevus/pwsh-script@v2.0.3 @@ -41,6 +41,10 @@ jobs: working-directory: src/Tests/Stryker/Tests run: dotnet build -c Release + - name: Test + working-directory: src/Tests/Stryker/Tests + run: dotnet test -c Release + - name: Mutate working-directory: src/Tests/Stryker/Tests run: dotnet-stryker From 26e1b579eb87b205d4971e51c69b924355249444 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Thu, 26 Jan 2023 22:15:04 -0800 Subject: [PATCH 17/31] More coverage --- src/Tests/FlatSharpEndToEndTests/Helpers.cs | 49 ++- src/Tests/Stryker/CodeGen/FieldsPartial.cs | 19 + src/Tests/Stryker/CodeGen/KeyPartial.cs | 19 + src/Tests/Stryker/CodeGen/RootPartial.cs | 19 + src/Tests/Stryker/CodeGen/Schema.fbs | 4 +- src/Tests/Stryker/CodeGen/VectorPartial.cs | 19 + .../Stryker/Tests/PrimitivesFieldsTests.cs | 117 +++++++ src/Tests/Stryker/Tests/StructFieldTests.cs | 60 ++-- src/Tests/Stryker/Tests/UnionFieldTests.cs | 329 ++++++++++++++++++ 9 files changed, 568 insertions(+), 67 deletions(-) create mode 100644 src/Tests/Stryker/CodeGen/FieldsPartial.cs create mode 100644 src/Tests/Stryker/CodeGen/KeyPartial.cs create mode 100644 src/Tests/Stryker/CodeGen/RootPartial.cs create mode 100644 src/Tests/Stryker/CodeGen/VectorPartial.cs create mode 100644 src/Tests/Stryker/Tests/PrimitivesFieldsTests.cs create mode 100644 src/Tests/Stryker/Tests/UnionFieldTests.cs diff --git a/src/Tests/FlatSharpEndToEndTests/Helpers.cs b/src/Tests/FlatSharpEndToEndTests/Helpers.cs index b00de275..5459dd93 100644 --- a/src/Tests/FlatSharpEndToEndTests/Helpers.cs +++ b/src/Tests/FlatSharpEndToEndTests/Helpers.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using FlatSharp.Internal; using System.Linq.Expressions; using System.Threading; @@ -69,6 +70,8 @@ public static void AssertSequenceEqual( Span expected, Span actual) { + var combined = expected.ToArray().Zip(actual.ToArray()).ToArray(); + Assert.Equal(expected.Length, actual.Length); for (int i = 0; i < expected.Length; ++i) @@ -123,38 +126,28 @@ public static void AssertMutationWorks( } } - - public static void AssertMutationWorks( - FlatBufferDeserializationOption option, - TSource parent, - TProperty newValue, - bool isWriteThrough, - Func getValue, - Action setValue) + public static T TestProgressiveFieldLoad(int index, bool expected, P parent, Func getter) { - switch (option) - { - case FlatBufferDeserializationOption.Lazy when isWriteThrough: - case FlatBufferDeserializationOption.Progressive when isWriteThrough: - case FlatBufferDeserializationOption.GreedyMutable when isWriteThrough is false: - setValue(parent, newValue); - TProperty readValue = getValue(parent); + Assert.False(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); + Assert.Equal(expected, ((IFlatBufferProgressiveObject)parent).IsFieldPresent(index)); - // For value types, validate that they are the same. - if (typeof(TProperty).IsValueType) - { - Assert.Equal(newValue, readValue); - } - else if (option != FlatBufferDeserializationOption.Lazy) - { - Assert.True(object.ReferenceEquals(newValue, readValue)); - } + T item = getter(parent); - return; + for (int i = 0; i < 10; ++i) + { + Assert.True(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); + T next = getter(parent); - default: - Assert.Throws(() => setValue(parent, newValue)); - return; + if (typeof(T).IsValueType) + { + Assert.Equal(item, next); + } + else + { + Assert.True(object.ReferenceEquals(item, next)); + } } + + return item; } } \ No newline at end of file diff --git a/src/Tests/Stryker/CodeGen/FieldsPartial.cs b/src/Tests/Stryker/CodeGen/FieldsPartial.cs new file mode 100644 index 00000000..ceeb45e4 --- /dev/null +++ b/src/Tests/Stryker/CodeGen/FieldsPartial.cs @@ -0,0 +1,19 @@ +using FlatSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FlatSharpStrykerTests; + +public partial class Fields : IReferenceItem +{ + public static bool IsStaticInitialized { get; set; } + + public bool IsInitialized { get; set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) => this.IsInitialized = true; + + static partial void OnStaticInitialize() => IsStaticInitialized = true; +} diff --git a/src/Tests/Stryker/CodeGen/KeyPartial.cs b/src/Tests/Stryker/CodeGen/KeyPartial.cs new file mode 100644 index 00000000..8fc4c48a --- /dev/null +++ b/src/Tests/Stryker/CodeGen/KeyPartial.cs @@ -0,0 +1,19 @@ +using FlatSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FlatSharpStrykerTests; + +public partial class Key : IReferenceItem +{ + public static bool IsStaticInitialized { get; set; } + + public bool IsInitialized { get; set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) => this.IsInitialized = true; + + static partial void OnStaticInitialize() => IsStaticInitialized = true; +} diff --git a/src/Tests/Stryker/CodeGen/RootPartial.cs b/src/Tests/Stryker/CodeGen/RootPartial.cs new file mode 100644 index 00000000..abf19f6b --- /dev/null +++ b/src/Tests/Stryker/CodeGen/RootPartial.cs @@ -0,0 +1,19 @@ +using FlatSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FlatSharpStrykerTests; + +public partial class Root : IReferenceItem +{ + public static bool IsStaticInitialized { get; set; } + + public bool IsInitialized { get; set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) => this.IsInitialized = true; + + static partial void OnStaticInitialize() => IsStaticInitialized = true; +} diff --git a/src/Tests/Stryker/CodeGen/Schema.fbs b/src/Tests/Stryker/CodeGen/Schema.fbs index cbb9871f..b3197f69 100644 --- a/src/Tests/Stryker/CodeGen/Schema.fbs +++ b/src/Tests/Stryker/CodeGen/Schema.fbs @@ -82,10 +82,10 @@ table Fields /// Field 3 str : string; - /// Field 4 + /// Field 4, 5 union : FunUnion; - /// Field 5 + /// Field 6 scalar_with_default : int = 3; } diff --git a/src/Tests/Stryker/CodeGen/VectorPartial.cs b/src/Tests/Stryker/CodeGen/VectorPartial.cs new file mode 100644 index 00000000..e669bb66 --- /dev/null +++ b/src/Tests/Stryker/CodeGen/VectorPartial.cs @@ -0,0 +1,19 @@ +using FlatSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FlatSharpStrykerTests; + +public partial class Vectors : IReferenceItem +{ + public static bool IsStaticInitialized { get; set; } + + public bool IsInitialized { get; set; } + + partial void OnInitialized(FlatBufferDeserializationContext? context) => this.IsInitialized = true; + + static partial void OnStaticInitialize() => IsStaticInitialized = true; +} diff --git a/src/Tests/Stryker/Tests/PrimitivesFieldsTests.cs b/src/Tests/Stryker/Tests/PrimitivesFieldsTests.cs new file mode 100644 index 00000000..37e27174 --- /dev/null +++ b/src/Tests/Stryker/Tests/PrimitivesFieldsTests.cs @@ -0,0 +1,117 @@ +using FlatSharp.Internal; +using System.Linq.Expressions; + +namespace FlatSharpStrykerTests; + +public class PrimitivesFieldsTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void StringTableField(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Fields fields = parsed.Fields; + Assert.Equal("hello", fields.Str); + + Helpers.AssertMutationWorks(option, fields, false, s => s.Str, string.Empty); + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ScalarTableField(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + Fields fields = parsed.Fields; + + Assert.Equal(3, fields.Memory); + Helpers.AssertMutationWorks(option, fields, false, s => s.Memory, (byte)0); + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ScalarTableField_WithDefaultValue(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + Fields fields = parsed.Fields; + + Assert.Equal(3, fields.ScalarWithDefault); + Helpers.AssertMutationWorks(option, fields, false, s => s.ScalarWithDefault, 8); + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + [Fact] + public void ProgressiveStringTableField() + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer); + Fields fields = parsed.Fields; + Assert.Equal("hello", Helpers.TestProgressiveFieldLoad(3, true, fields, f => f.Str)); + } + + [Fact] + public void ProgressiveScalarTableField() + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer); + Fields fields = parsed.Fields; + Assert.Equal(3, Helpers.TestProgressiveFieldLoad(2, true, fields, f => f.Memory)); + } + + [Fact] + public void ProgressiveScalarTableFieldWithDefaultValue() + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer); + Fields fields = parsed.Fields; + Assert.Equal(3, Helpers.TestProgressiveFieldLoad(6, false, fields, f => f.ScalarWithDefault)); + } + + private Root CreateRoot(out byte[] expectedData) + { + static byte B(char c) => (byte)c; + + Root root = new() + { + Fields = new() + { + Memory = 3, + Str = "hello", + ScalarWithDefault = 3, + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 246, 255, 255, 255, // soffset to vtable + 20, 0, 0, 0, // uoffset to string + 3, 0, // ubyte scalar, padding + 12, 0, // vtable length + 9, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 8, 0, // field 2 (scalar) + 4, 0, // field 3 (str) + 0, 0, // padding + 5, 0, 0, 0, // string length + B('h'), B('e'), B('l'), B('l'), + B('o'), 0 + }; + + return root; + } +} diff --git a/src/Tests/Stryker/Tests/StructFieldTests.cs b/src/Tests/Stryker/Tests/StructFieldTests.cs index 239d988f..97c2c537 100644 --- a/src/Tests/Stryker/Tests/StructFieldTests.cs +++ b/src/Tests/Stryker/Tests/StructFieldTests.cs @@ -30,9 +30,11 @@ public void ValueStructTableField(FlatBufferDeserializationOption option) }; Root parsed = root.SerializeAndParse(option, out byte[] buffer); + Fields fields = parsed.Fields; - Assert.NotNull(parsed.Fields); + Assert.NotNull(fields); Assert.Null(parsed.Vectors); + Assert.NotNull(parsed.Fields.ValueStruct); ValueStruct vsParsed = parsed.Fields.ValueStruct.Value; @@ -42,7 +44,7 @@ public void ValueStructTableField(FlatBufferDeserializationOption option) Assert.Equal(vs.C(0), vsParsed.C(0)); Assert.Equal(vs.C(1), vsParsed.C(1)); - byte[] expectedBytes = new byte[] + byte[] expectedBytes = { 4, 0, 0, 0, // offset to table start 248, 255, 255, 255, // soffset to vtable. @@ -65,6 +67,7 @@ public void ValueStructTableField(FlatBufferDeserializationOption option) }; Helpers.AssertSequenceEqual(expectedBytes, buffer); + Helpers.AssertMutationWorks(option, fields, false, p => p.ValueStruct, default); } @@ -97,6 +100,7 @@ public void ReferenceStructTableField(FlatBufferDeserializationOption option) Assert.Equal(vs.C(1), vsParsed.C(1)); Helpers.AssertSequenceEqual(expectedBytes, buffer); + Helpers.AssertMutationWorks(option, parsed.Fields, false, p => p.RefStruct, new RefStruct()); } [Theory] @@ -128,9 +132,16 @@ public void ReferenceStructWriteThrough(FlatBufferDeserializationOption option) public void ReferenceStructOnDeserialized(FlatBufferDeserializationOption option) { Root root = CreateRootReferenceStruct(out RefStruct rs, out ValueStruct vs, out byte[] expectedBytes); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + Fields fields = parsed.Fields; + RefStruct rsp = fields.RefStruct; - RefStruct rsp = parsed.Fields.RefStruct; + Assert.True(parsed.IsInitialized); + Assert.True(Root.IsStaticInitialized); + + Assert.True(fields.IsInitialized); + Assert.True(Fields.IsStaticInitialized); Assert.True(rsp.IsInitialized); Assert.True(RefStruct.IsStaticInitialized); @@ -139,48 +150,23 @@ public void ReferenceStructOnDeserialized(FlatBufferDeserializationOption option [Fact] public void ProgressiveFieldLoads() { - static T LoadAndVerify(int index, bool expected, P parent, Func getter) - { - Assert.False(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); - Assert.Equal(expected, ((IFlatBufferProgressiveObject)parent).IsFieldPresent(index)); - - T item = getter(parent); - - for (int i = 0; i < 10; ++i) - { - Assert.True(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); - T next = getter(parent); - - if (typeof(T).IsValueType) - { - Assert.Equal(item, next); - } - else - { - Assert.True(object.ReferenceEquals(item, next)); - } - } - - return item; - } - Root root = CreateRootReferenceStruct(out RefStruct rs, out ValueStruct vs, out byte[] expectedBytes); Root parsed = root.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer); IFlatBufferProgressiveObject progressive = (IFlatBufferProgressiveObject)parsed; - Fields fields = LoadAndVerify(0, true, parsed, p => p.Fields); + Fields fields = Helpers.TestProgressiveFieldLoad(0, true, parsed, p => p.Fields); Assert.NotNull(fields); { - RefStruct @ref = LoadAndVerify(0, true, fields, f => f.RefStruct); - Assert.Equal(root.Fields.RefStruct.A, LoadAndVerify(0, true, @ref, r => r.A)); - Assert.Equal(root.Fields.RefStruct.B, LoadAndVerify(1, true, @ref, r => r.B)); - Assert.Equal(root.Fields.RefStruct.C[0], LoadAndVerify(2, true, @ref, r => r.__flatsharp__C_0)); - Assert.Equal(root.Fields.RefStruct.C[1], LoadAndVerify(3, true, @ref, r => r.__flatsharp__C_1)); - Assert.Equal(root.Fields.RefStruct.D[0], LoadAndVerify(4, true, @ref, r => r.__flatsharp__D_0)); - Assert.Equal(root.Fields.RefStruct.D[1], LoadAndVerify(5, true, @ref, r => r.__flatsharp__D_1)); - LoadAndVerify(6, true, @ref, r => r.E); + RefStruct @ref = Helpers.TestProgressiveFieldLoad(0, true, fields, f => f.RefStruct); + Assert.Equal(root.Fields.RefStruct.A, Helpers.TestProgressiveFieldLoad(0, true, @ref, r => r.A)); + Assert.Equal(root.Fields.RefStruct.B, Helpers.TestProgressiveFieldLoad(1, true, @ref, r => r.B)); + Assert.Equal(root.Fields.RefStruct.C[0], Helpers.TestProgressiveFieldLoad(2, true, @ref, r => r.__flatsharp__C_0)); + Assert.Equal(root.Fields.RefStruct.C[1], Helpers.TestProgressiveFieldLoad(3, true, @ref, r => r.__flatsharp__C_1)); + Assert.Equal(root.Fields.RefStruct.D[0], Helpers.TestProgressiveFieldLoad(4, true, @ref, r => r.__flatsharp__D_0)); + Assert.Equal(root.Fields.RefStruct.D[1], Helpers.TestProgressiveFieldLoad(5, true, @ref, r => r.__flatsharp__D_1)); + Helpers.TestProgressiveFieldLoad(6, true, @ref, r => r.E); } } diff --git a/src/Tests/Stryker/Tests/UnionFieldTests.cs b/src/Tests/Stryker/Tests/UnionFieldTests.cs new file mode 100644 index 00000000..4128b189 --- /dev/null +++ b/src/Tests/Stryker/Tests/UnionFieldTests.cs @@ -0,0 +1,329 @@ +using FlatSharp.Internal; +using System.Linq.Expressions; + +namespace FlatSharpStrykerTests; + +public class UnionFieldTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void StringMember(FlatBufferDeserializationOption option) + { + Root root = this.CreateRoot_StringMember(out var expectedData); + Root parsed = root.SerializeAndParse(option, out var actualData); + + Helpers.AssertSequenceEqual(expectedData, actualData); + + FunUnion union = parsed.Fields.Union.Value; + Assert.Equal(FunUnion.ItemKind.str, union.Kind); + Assert.Equal(3, union.Discriminator); + Assert.Equal("hello", union.str); + Assert.Equal("hello", union.Item3); + Assert.True(union.TryGet(out string str)); + Assert.Equal("hello", str); + + Helpers.AssertMutationWorks(option, parsed.Fields, false, f => f.Union, default); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ValueStructMember(FlatBufferDeserializationOption option) + { + void VerifyVs(ValueStruct expected, ValueStruct actual) + { + Assert.Equal(expected.A, actual.A); + Assert.Equal(expected.B, actual.B); + Assert.Equal(expected.C(0), actual.C(0)); + Assert.Equal(expected.C(1), actual.C(1)); + } + + Root root = this.CreateRoot_ValueStructMember(out var expectedData); + Root parsed = root.SerializeAndParse(option, out var actualData); + + Helpers.AssertSequenceEqual(expectedData, actualData); + + FunUnion sourceUnion = root.Fields.Union.Value; + FunUnion union = parsed.Fields.Union.Value; + + Assert.Equal(FunUnion.ItemKind.ValueStruct, union.Kind); + Assert.Equal(2, union.Discriminator); + VerifyVs(sourceUnion.ValueStruct, union.ValueStruct); + VerifyVs(sourceUnion.Item2, union.Item2); + + Assert.True(union.TryGet(out ValueStruct test)); + VerifyVs(sourceUnion.Item2, test); + + Helpers.AssertMutationWorks(option, parsed.Fields, false, f => f.Union, default); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void RefStructMember(FlatBufferDeserializationOption option) + { + void VerifyVs(RefStruct expected, RefStruct actual) + { + Assert.Equal(expected.A, actual.A); + Assert.Equal(expected.B, actual.B); + Assert.Equal(expected.C[0], actual.C[0]); + Assert.Equal(expected.C[1], actual.C[1]); + Assert.Equal(expected.D[0], actual.D[0]); + Assert.Equal(expected.D[1], actual.D[1]); + } + + Root root = this.CreateRoot_RefStructMember(out var expectedData); + Root parsed = root.SerializeAndParse(option, out var actualData); + + Helpers.AssertSequenceEqual(expectedData, actualData); + + FunUnion sourceUnion = root.Fields.Union.Value; + FunUnion union = parsed.Fields.Union.Value; + + Assert.Equal(FunUnion.ItemKind.RefStruct, union.Kind); + Assert.Equal(1, union.Discriminator); + VerifyVs(sourceUnion.RefStruct, union.RefStruct); + VerifyVs(sourceUnion.Item1, union.Item1); + + Assert.True(union.TryGet(out RefStruct test)); + VerifyVs(sourceUnion.Item1, test); + + Helpers.AssertMutationWorks(option, parsed.Fields, false, f => f.Union, default); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void KeyTableMember(FlatBufferDeserializationOption option) + { + void VerifyVs(Key expected, Key actual) + { + Assert.Equal(expected.Name, actual.Name); + Assert.Equal(expected.Value, actual.Value); + } + + Root root = this.CreateRoot_KeyMember(out var expectedData); + Root parsed = root.SerializeAndParse(option, out var actualData); + + Helpers.AssertSequenceEqual(expectedData, actualData); + + FunUnion sourceUnion = root.Fields.Union.Value; + FunUnion union = parsed.Fields.Union.Value; + + Assert.Equal(FunUnion.ItemKind.Key, union.Kind); + Assert.Equal(4, union.Discriminator); + VerifyVs(sourceUnion.Key, union.Key); + VerifyVs(sourceUnion.Item4, union.Item4); + + Assert.True(union.TryGet(out Key test)); + VerifyVs(sourceUnion.Item4, test); + + Helpers.AssertMutationWorks(option, test, false, k => k.Name, string.Empty); + Helpers.AssertMutationWorks(option, test, false, k => k.Value, Fruit.Apple); + Helpers.AssertMutationWorks(option, parsed.Fields, false, f => f.Union, default); + + Assert.True(test.IsInitialized); + Assert.True(Key.IsStaticInitialized); + } + + private Root CreateRoot_RefStructMember(out byte[] expectedData) + { + RefStruct vs = new() + { + A = 3, + B = Fruit.Apple, + __flatsharp__C_0 = -1, + __flatsharp__C_1 = -2, + E = default, + }; + + Root root = new() + { + Fields = new() + { + Union = new(vs) + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 246, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to union + 1, 0, // ubyte discriminator, padding + + 16, 0, // vtable length + 9, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 8, 0, // field 4 (discriminator) + 4, 0, // field 5 (uoffset) + + 0, 0, // padding + 3, 0, 0, 0, // 3 (ulong) + 0, 0, 0, 0, + 0, 0, 0, 0, // apple + 255, 254, // C[0,1] + 0, 0, // D[0,1] + 0, 0, 0, 0, // ValueStruct (default) + 0, 0, 0, 0, + 0, 0 + }; + + return root; + } + + private Root CreateRoot_ValueStructMember(out byte[] expectedData) + { + ValueStruct vs = new() + { + A = 3, + B = 4, + }; + + vs.C(0) = 1; + vs.C(1) = 2; + + Root root = new() + { + Fields = new() + { + Union = new(vs) + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 246, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to union + 2, 0, // ubyte discriminator, padding + + 16, 0, // vtable length + 9, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 8, 0, // field 4 (discriminator) + 4, 0, // field 5 (uoffset) + + 0, 0, // padding + 3, 0, 0, 0, + 4, 0, + 1, 0, + 2, 0, + }; + + return root; + } + + private Root CreateRoot_StringMember(out byte[] expectedData) + { + static byte B(char c) => (byte)c; + + Root root = new() + { + Fields = new() + { + Union = new("hello") + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 246, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to union + 3, 0, // ubyte discriminator, padding + + 16, 0, // vtable length + 9, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 8, 0, // field 4 (discriminator) + 4, 0, // field 5 (uoffset) + + 0, 0, // padding + 5, 0, 0, 0, // string length + B('h'), B('e'), B('l'), B('l'), + B('o'), 0 + }; + + return root; + } + + + private Root CreateRoot_KeyMember(out byte[] expectedData) + { + static byte B(char c) => (byte)c; + + Key key = new() + { + Name = "fred", + Value = Fruit.Banana, + }; + + Root root = new() + { + Fields = new() + { + Union = new(key) + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 246, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to union + 4, 0, // ubyte discriminator, padding + + 16, 0, // vtable length + 9, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 8, 0, // field 4 (discriminator) + 4, 0, // field 5 (uoffset) + + 0, 0, // padding + 36, 0, 0, 0, // soffset to vtable (note: vtable is shared since banana is default value) + 4, 0, 0, 0, // uoffset to string (name) + 4, 0, 0, 0, // string length + B('f'), B('r'), B('e'), B('d'), 0 + }; + + return root; + } +} From 181302780c40b93c48e57a03064a306bed49d702 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 00:03:35 -0800 Subject: [PATCH 18/31] start on vectors --- src/FlatSharp/TypeModel/TableMemberModel.cs | 20 ++- src/Tests/FlatSharpEndToEndTests/Helpers.cs | 161 ++++++++++++++++- .../Stryker/Tests/PrimitivesFieldsTests.cs | 49 +++++ .../Stryker/Tests/StringVectorFieldTests.cs | 83 +++++++++ src/Tests/Stryker/Tests/UnionFieldTests.cs | 169 +++++++++++++++++- 5 files changed, 474 insertions(+), 8 deletions(-) create mode 100644 src/Tests/Stryker/Tests/StringVectorFieldTests.cs diff --git a/src/FlatSharp/TypeModel/TableMemberModel.cs b/src/FlatSharp/TypeModel/TableMemberModel.cs index 5e0e97bf..76bdfe71 100644 --- a/src/FlatSharp/TypeModel/TableMemberModel.cs +++ b/src/FlatSharp/TypeModel/TableMemberModel.cs @@ -216,10 +216,9 @@ private string CreateWideReadItemBody( relativeOffsets.Add($@" int relativeOffset{i} = {vtableVariableName}.OffsetOf<{context.InputBufferTypeName}>({context.InputBufferVariableName}, {idx}); - if (relativeOffset{i} == 0) - {{ - {this.GetNotPresentStatement()} - }} + bool isZero{i} = relativeOffset{i} == 0; + allZero &= isZero{i}; + anyZero |= isZero{i}; "); absoluteLocations.Add($"relativeOffset{i} + {context.OffsetVariableName}"); @@ -232,8 +231,21 @@ private string CreateWideReadItemBody( }; return $@" + bool allZero = true; + bool anyZero = false; + {string.Join("\r\n", relativeOffsets)} + if (allZero) + {{ + {GetNotPresentStatement()} + }} + + if (anyZero) + {{ + throw new {typeof(System.IO.InvalidDataException).GetGlobalCompilableTypeName()}(""FlatBuffer table property '{this.FriendlyName}' was only partially included in the buffer.""); + }} + var absoluteLocations = ({string.Join(", ", absoluteLocations)}); return {adjustedContext.GetParseInvocation(this.PropertyInfo.PropertyType)};"; } diff --git a/src/Tests/FlatSharpEndToEndTests/Helpers.cs b/src/Tests/FlatSharpEndToEndTests/Helpers.cs index 5459dd93..cf26f531 100644 --- a/src/Tests/FlatSharpEndToEndTests/Helpers.cs +++ b/src/Tests/FlatSharpEndToEndTests/Helpers.cs @@ -70,8 +70,7 @@ public static void AssertSequenceEqual( Span expected, Span actual) { - var combined = expected.ToArray().Zip(actual.ToArray()).ToArray(); - + //var combined = expected.ToArray().Zip(actual.ToArray()).ToArray(); Assert.Equal(expected.Length, actual.Length); for (int i = 0; i < expected.Length; ++i) @@ -116,16 +115,94 @@ public static void AssertMutationWorks( return; default: - Assert.Throws(new Action(() => + var ex = Assert.Throws(new Action(() => { var ex = Assert.Throws(action).InnerException; throw ex; })); + if (isWriteThrough && option == FlatBufferDeserializationOption.GreedyMutable) + { + Assert.Equal("WriteThrough fields are implemented as readonly when using 'GreedyMutable' serializers.", ex.Message); + } + return; } } + public static void AssertMutationWorks( + FlatBufferDeserializationOption option, + bool isWriteThrough, + IList items, + T newValue) + { + if (option != FlatBufferDeserializationOption.Lazy) + { + T[] target = new T[items.Count]; + items.CopyTo(target, 0); + + for (int i = 0; i < items.Count; ++i) + { + Assert.Equal(i, items.IndexOf(items[i])); + Assert.True(items.Contains(items[i])); + Assert.Equal(items[i], target[i]); + } + } + + for (int i = 0; i < items.Count; ++i) + { + switch (option) + { + case FlatBufferDeserializationOption.Lazy when isWriteThrough: + case FlatBufferDeserializationOption.Progressive when isWriteThrough: + case FlatBufferDeserializationOption.GreedyMutable when isWriteThrough is false: + items[i] = newValue; + + // For value types, validate that they are the same. + if (typeof(T).IsValueType) + { + Assert.Equal(newValue, items[i]); + } + else if (option != FlatBufferDeserializationOption.Lazy) + { + Assert.True(object.ReferenceEquals(newValue, items[i])); + } + + break; + + default: + var ex = Assert.Throws(new Action(() => + { + items[i] = newValue; + })); + + if (isWriteThrough && option == FlatBufferDeserializationOption.GreedyMutable) + { + Assert.Equal("WriteThrough fields are implemented as readonly when using 'GreedyMutable' serializers.", ex.Message); + } + + break; + } + } + + if (option == FlatBufferDeserializationOption.GreedyMutable) + { + items.Clear(); + items.Add(default); + items.Remove(items[0]); + items.Insert(0, default); + items.RemoveAt(0); + } + else + { + Assert.Throws(() => items.Clear()); + Assert.Throws(() => items.Add(default)); + Assert.Throws(() => items.Remove(items[0])); + Assert.Throws(() => items.Insert(0, default)); + Assert.Throws(() => items.RemoveAt(0)); + } + } + public static T TestProgressiveFieldLoad(int index, bool expected, P parent, Func getter) { Assert.False(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); @@ -150,4 +227,82 @@ public static T TestProgressiveFieldLoad(int index, bool expected, P paren return item; } + + private static int counter; + + public static IList CreateList(params T[] values) + { + return (Interlocked.Increment(ref counter) % 3) switch + { + 0 => values, + 1 => new List(values), + _ => new DummyList(values), + }; + } + + private class DummyList : IList, IReadOnlyList + { + private readonly List list = new(); + + public DummyList(T[] values) + { + this.list.AddRange(values); + } + + public T this[int index] { get => ((IList)list)[index]; set => ((IList)list)[index] = value; } + + public int Count => ((ICollection)list).Count; + + public bool IsReadOnly => ((ICollection)list).IsReadOnly; + + public void Add(T item) + { + ((ICollection)list).Add(item); + } + + public void Clear() + { + ((ICollection)list).Clear(); + } + + public bool Contains(T item) + { + return ((ICollection)list).Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + ((ICollection)list).CopyTo(array, arrayIndex); + } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)list).GetEnumerator(); + } + + public int IndexOf(T item) + { + return ((IList)list).IndexOf(item); + } + + public void Insert(int index, T item) + { + ((IList)list).Insert(index, item); + } + + public bool Remove(T item) + { + return ((ICollection)list).Remove(item); + } + + public void RemoveAt(int index) + { + ((IList)list).RemoveAt(index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)list).GetEnumerator(); + } + } } \ No newline at end of file diff --git a/src/Tests/Stryker/Tests/PrimitivesFieldsTests.cs b/src/Tests/Stryker/Tests/PrimitivesFieldsTests.cs index 37e27174..ec8ba39c 100644 --- a/src/Tests/Stryker/Tests/PrimitivesFieldsTests.cs +++ b/src/Tests/Stryker/Tests/PrimitivesFieldsTests.cs @@ -46,6 +46,19 @@ public void ScalarTableField_WithDefaultValue(FlatBufferDeserializationOption op Helpers.AssertSequenceEqual(expectedData, buffer); } + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ScalarTableField_WithoutDefaultValue(FlatBufferDeserializationOption option) + { + Root root = CreateRoot_NotDefault(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + Fields fields = parsed.Fields; + + Assert.Equal(root.Fields.ScalarWithDefault, fields.ScalarWithDefault); + Helpers.AssertMutationWorks(option, fields, false, s => s.ScalarWithDefault, 8); + Helpers.AssertSequenceEqual(expectedData, buffer); + } + [Fact] public void ProgressiveStringTableField() { @@ -114,4 +127,40 @@ private Root CreateRoot(out byte[] expectedData) return root; } + + private Root CreateRoot_NotDefault(out byte[] expectedData) + { + Root root = new() + { + Fields = new() + { + ScalarWithDefault = 1, + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 248, 255, 255, 255, // soffset to vtable + 1, 0, 0, 0, // scalar + 18, 0, // vtable length + 8, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 0, 0, // field 4 + 0, 0, // field 5 + 4, 0, // field 6 + }; + + return root; + } } diff --git a/src/Tests/Stryker/Tests/StringVectorFieldTests.cs b/src/Tests/Stryker/Tests/StringVectorFieldTests.cs new file mode 100644 index 00000000..cd2c19af --- /dev/null +++ b/src/Tests/Stryker/Tests/StringVectorFieldTests.cs @@ -0,0 +1,83 @@ +using FlatSharp.Internal; +using System.Linq.Expressions; +using System.Threading; + +namespace FlatSharpStrykerTests; + +public class StringVectorFieldTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void StringTableField(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Vectors vectors = parsed.Vectors; + Assert.True(vectors.IsInitialized); + Assert.True(Vectors.IsStaticInitialized); + + IList theBoys = vectors.Str; + Assert.Equal(3, theBoys.Count); + Assert.Equal("billy", theBoys[0]); + Assert.Equal("mm", theBoys[1]); + Assert.Equal("frenchie", theBoys[2]); + + Helpers.AssertMutationWorks(option, vectors, false, s => s.Str, new string[0]); + Helpers.AssertMutationWorks(option, false, theBoys, string.Empty); + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + private Root CreateRoot(out byte[] expectedData) + { + static byte B(char c) => (byte)c; + + Root root = new() + { + Vectors = new() + { + Str = Helpers.CreateList("billy", "mm", "frenchie") + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 248, 255, 255, 255, // soffset to vtable + 16, 0, 0, 0, // uoffset to vector + + 12, 0, // vtable length + 8, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 4, 0, // field 3 (str) + + 3, 0, 0, 0, // vector length + 12, 0, 0, 0, + 20, 0, 0, 0, + 24, 0, 0, 0, + + 5, 0, 0, 0, + B('b'), B('i'), B('l'), B('l'), B('y'), 0, + 0, 0, + + 2, 0, 0, 0, + B('m'), B('m'), 0, 0, + + 8, 0, 0, 0, + B('f'), B('r'), B('e'), B('n'), + B('c'), B('h'), B('i'), B('e'), + 0, + }; + + return root; + } +} diff --git a/src/Tests/Stryker/Tests/UnionFieldTests.cs b/src/Tests/Stryker/Tests/UnionFieldTests.cs index 4128b189..4b9f4864 100644 --- a/src/Tests/Stryker/Tests/UnionFieldTests.cs +++ b/src/Tests/Stryker/Tests/UnionFieldTests.cs @@ -1,4 +1,5 @@ using FlatSharp.Internal; +using System.IO; using System.Linq.Expressions; namespace FlatSharpStrykerTests; @@ -123,6 +124,95 @@ void VerifyVs(Key expected, Key actual) Assert.True(Key.IsStaticInitialized); } + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void InvalidUnion_NoOffset(FlatBufferDeserializationOption option) + { + this.Create_InvalidUnion_NoOffset(out byte[] data); + + var ex = Assert.Throws(() => + { + Root root = Root.Serializer.Parse(data, option); + FunUnion union = root.Fields.Union.Value; + }); + + Assert.Equal("FlatBuffer table property 'FlatSharpStrykerTests.Fields.Union' was only partially included in the buffer.", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void InvalidUnion_NoDiscriminator(FlatBufferDeserializationOption option) + { + this.Create_InvalidUnion_NoDiscriminator(out byte[] data); + + var ex = Assert.Throws(() => + { + Root root = Root.Serializer.Parse(data, option); + FunUnion union = root.Fields.Union.Value; + }); + + Assert.Equal("FlatBuffer table property 'FlatSharpStrykerTests.Fields.Union' was only partially included in the buffer.", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void InvalidUnion_InvalidDiscriminator(FlatBufferDeserializationOption option) + { + this.Create_InvalidUnion_InvalidDiscriminator(out byte[] data); + + var ex = Assert.Throws(() => + { + Root root = Root.Serializer.Parse(data, option); + FunUnion union = root.Fields.Union.Value; + }); + + Assert.Equal("Exception parsing union 'FlatSharpStrykerTests.FunUnion'. Discriminator = 10", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void UnionNotPresent(FlatBufferDeserializationOption option) + { + Root source = this.CreateRoot_UnionNotPresent(out byte[] expectedData); + Root parsed = source.SerializeAndParse(option, out byte[] buffer); + + Assert.Null(parsed.Fields.Union); + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + private Root CreateRoot_UnionNotPresent(out byte[] expectedData) + { + RefStruct vs = new() + { + A = 3, + B = Fruit.Apple, + __flatsharp__C_0 = -1, + __flatsharp__C_1 = -2, + E = default, + }; + + Root root = new() + { + Fields = new() + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 252, 255, 255, 255, // soffset to vtable + 4, 0, 4, 0 // vtable + }; + + return root; + } + private Root CreateRoot_RefStructMember(out byte[] expectedData) { RefStruct vs = new() @@ -275,7 +365,6 @@ private Root CreateRoot_StringMember(out byte[] expectedData) return root; } - private Root CreateRoot_KeyMember(out byte[] expectedData) { static byte B(char c) => (byte)c; @@ -326,4 +415,82 @@ private Root CreateRoot_KeyMember(out byte[] expectedData) return root; } + + private void Create_InvalidUnion_NoOffset(out byte[] data) + { + data = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 250, 255, 255, 255, // soffset to vtable + 4, 0, // ubyte discriminator, padding + + 14, 0, // vtable length + 5, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 4, 0, // field 4 (discriminator) + }; + } + + private void Create_InvalidUnion_NoDiscriminator(out byte[] data) + { + data = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 248, 255, 255, 255, // soffset to vtable + 4, 0, 0, 0, // union offset + + 16, 0, // vtable length + 5, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 0, 0, // field 4 (discriminator) + 4, 0, // field 5 (offset) + }; + } + + private void Create_InvalidUnion_InvalidDiscriminator(out byte[] data) + { + data = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Fields subtable. + + // Vtable for Root (2 bytes padding) + 6, 0, 8, 0, + 4, 0, 0, 0, + + 246, 255, 255, 255, // soffset to vtable + 4, 0, 0, 0, // union offset + 10, 0, // discriminator + padding + + 16, 0, // vtable length + 5, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 8, 0, // field 4 (discriminator) + 4, 0, // field 5 (offset) + }; + } } From e9ca6240dd7188f9b873ffb83c06e283213faa4a Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 03:00:06 -0800 Subject: [PATCH 19/31] More --- .../BaseReferenceTypeSchemaModel.cs | 6 + src/FlatSharp/FlatBufferVectorHelpers.cs | 4 +- src/FlatSharp/TypeModel/UnionTypeModel.cs | 2 - src/Tests/FlatSharpEndToEndTests/Helpers.cs | 2 +- src/Tests/Stryker/CodeGen/Schema.fbs | 6 +- src/Tests/Stryker/Tests/IndexedVectorTests.cs | 173 +++++++++ src/Tests/Stryker/Tests/MemoryVectorTests.cs | 79 ++++ ...ctorFieldTests.cs => StringVectorTests.cs} | 2 +- src/Tests/Stryker/Tests/UnionFieldTests.cs | 21 ++ src/Tests/Stryker/Tests/UnionVectorTests.cs | 336 ++++++++++++++++++ 10 files changed, 622 insertions(+), 9 deletions(-) create mode 100644 src/Tests/Stryker/Tests/IndexedVectorTests.cs create mode 100644 src/Tests/Stryker/Tests/MemoryVectorTests.cs rename src/Tests/Stryker/Tests/{StringVectorFieldTests.cs => StringVectorTests.cs} (98%) create mode 100644 src/Tests/Stryker/Tests/UnionVectorTests.cs diff --git a/src/FlatSharp.Compiler/SchemaModel/BaseReferenceTypeSchemaModel.cs b/src/FlatSharp.Compiler/SchemaModel/BaseReferenceTypeSchemaModel.cs index 9db916f6..a9b642b2 100644 --- a/src/FlatSharp.Compiler/SchemaModel/BaseReferenceTypeSchemaModel.cs +++ b/src/FlatSharp.Compiler/SchemaModel/BaseReferenceTypeSchemaModel.cs @@ -145,6 +145,12 @@ private void EmitDefaultConstrutor(CodeWriter writer, CompileContext context) } writer.AppendLine("#pragma warning disable CS8618"); // nullable + + if (context.Options.MutationTestingMode) + { + writer.AppendLine($"[{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}]"); + } + writer.AppendLine($"public {this.Name}()"); using (writer.WithBlock()) { diff --git a/src/FlatSharp/FlatBufferVectorHelpers.cs b/src/FlatSharp/FlatBufferVectorHelpers.cs index 4fb5dda9..a46e347b 100644 --- a/src/FlatSharp/FlatBufferVectorHelpers.cs +++ b/src/FlatSharp/FlatBufferVectorHelpers.cs @@ -66,11 +66,11 @@ internal struct {className}<{context.InputBufferTypeName}> : IVectorItemAccessor [MethodImpl(MethodImplOptions.AggressiveInlining)] public {className}(int offset, TInputBuffer buffer) {{ - this.count = checked((int)buffer.ReadUInt(offset)); + this.count = (int)buffer.ReadUInt(offset); // Advance to the start of the element at index 0. Easiest to do this once // in the .ctor than repeatedly for each index. - this.offset = checked(offset + sizeof(uint)); + this.offset = offset + sizeof(uint); }} public int ItemSize => {inlineSize}; diff --git a/src/FlatSharp/TypeModel/UnionTypeModel.cs b/src/FlatSharp/TypeModel/UnionTypeModel.cs index e04ab9b3..1f41bf37 100644 --- a/src/FlatSharp/TypeModel/UnionTypeModel.cs +++ b/src/FlatSharp/TypeModel/UnionTypeModel.cs @@ -172,8 +172,6 @@ public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext c string body = $@" byte discriminator = {context.InputBufferVariableName}.{nameof(IInputBuffer.ReadByte)}({context.OffsetVariableName}.offset0); int offsetLocation = {context.OffsetVariableName}.offset1; - if (discriminator != 0 && offsetLocation == 0) - throw new System.IO.InvalidDataException(""FlatBuffer union had discriminator set but no offset.""); switch (discriminator) {{ diff --git a/src/Tests/FlatSharpEndToEndTests/Helpers.cs b/src/Tests/FlatSharpEndToEndTests/Helpers.cs index cf26f531..8f17ba50 100644 --- a/src/Tests/FlatSharpEndToEndTests/Helpers.cs +++ b/src/Tests/FlatSharpEndToEndTests/Helpers.cs @@ -70,7 +70,7 @@ public static void AssertSequenceEqual( Span expected, Span actual) { - //var combined = expected.ToArray().Zip(actual.ToArray()).ToArray(); + var combined = expected.ToArray().Zip(actual.ToArray()).ToArray(); Assert.Equal(expected.Length, actual.Length); for (int i = 0; i < expected.Length; ++i) diff --git a/src/Tests/Stryker/CodeGen/Schema.fbs b/src/Tests/Stryker/CodeGen/Schema.fbs index b3197f69..96d33f75 100644 --- a/src/Tests/Stryker/CodeGen/Schema.fbs +++ b/src/Tests/Stryker/CodeGen/Schema.fbs @@ -102,10 +102,10 @@ table Vectors /// Field 3 str : [ string ]; - - /// Field 4 + + /// Field 4, 5 union : [ FunUnion ]; - /// Field 5 + /// Field 6 indexed : [ Key ] (fs_vector:"IIndexedVector"); } \ No newline at end of file diff --git a/src/Tests/Stryker/Tests/IndexedVectorTests.cs b/src/Tests/Stryker/Tests/IndexedVectorTests.cs new file mode 100644 index 00000000..e73f993a --- /dev/null +++ b/src/Tests/Stryker/Tests/IndexedVectorTests.cs @@ -0,0 +1,173 @@ +using FlatSharp.Internal; +using System.Linq.Expressions; +using System.Threading; + +namespace FlatSharpStrykerTests; + +public class IndexedVectorTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Present(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Vectors vectors = parsed.Vectors; + Assert.True(vectors.IsInitialized); + Assert.True(Vectors.IsStaticInitialized); + + IIndexedVector vector = vectors.Indexed; + + var expected = root.Vectors.Indexed.ToArray(); + + foreach (var pair in expected) + { + Fruit f = pair.Value.Value; + string name = pair.Value.Name; + + Assert.True(vector.TryGetValue(name, out Key key)); + Assert.Equal(f, key.Value); + Assert.Equal(name, key.Name); + + key = vector[name]; + Assert.Equal(f, key.Value); + + Assert.True(vector.ContainsKey(name)); + } + + Assert.False(vector.ContainsKey("z")); + Assert.False(vector.TryGetValue("z", out _)); + + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Missing(FlatBufferDeserializationOption option) + { + Root root = CreateRoot_Missing(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Vectors vectors = parsed.Vectors; + Assert.True(vectors.IsInitialized); + Assert.True(Vectors.IsStaticInitialized); + + Assert.Null(vectors.Indexed); + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + private Root CreateRoot(out byte[] expectedData) + { + static byte B(char c) => (byte)c; + + var vector = new IndexedVector + { + new Key { Name = "a", Value = Fruit.Apple }, + new Key { Name = "b", Value = Fruit.Banana }, + new Key { Name = "p", Value = Fruit.Pear }, + new Key { Name = "s", Value = Fruit.Strawberry }, + }; + + Root root = new() + { + Vectors = new() + { + Indexed = vector, + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 248, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to vector + + 18, 0, // vtable length + 8, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (scalar) + 0, 0, // field 3 (str) + 0, 0, // field 4, 5 (union) + 0, 0, + 4, 0, // field 6 (indexed) + 0, 0, + + 4, 0, 0, 0, // vector length + 16, 0, 0, 0, + 40, 0, 0, 0, + 60, 0, 0, 0, + 76, 0, 0, 0, + + 244, 255, 255, 255, // soffset to vtable. + 0, 0, 0, 0, // apple + 12, 0, 0, 0, // offset to 'a' + + 8, 0, 12, 0, // vtable + 8, 0, 4, 0, + + 1, 0, 0, 0, + B('a'), 0, 0, 0, + + 248, 255, 255, 255, // non-shared vtable. (banana is default) + 12, 0, 0, 0, + + 6, 0, 8, 0, // vtable + 4, 0, 0, 0, + + 1, 0, 0, 0, + B('b'), 0, 0, 0, + + 40, 0, 0, 0, // shares vtable + 3, 0, 0, 0, // pear + 4, 0, 0, 0, + + 1, 0, 0, 0, + B('p'), 0, 0, 0, + + 60, 0, 0, 0, // shares vtable + 2, 0, 0, 0, // strawberry + 4, 0, 0, 0, + + 1, 0, 0, 0, + B('s'), 0, + }; + + return root; + } + + + private Root CreateRoot_Missing(out byte[] expectedData) + { + Root root = new() + { + Vectors = new() + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 252, 255, 255, 255, // soffset to vtable + + 4, 0, // vtable length + 4, 0, // table length + }; + + return root; + } +} diff --git a/src/Tests/Stryker/Tests/MemoryVectorTests.cs b/src/Tests/Stryker/Tests/MemoryVectorTests.cs new file mode 100644 index 00000000..67daaf72 --- /dev/null +++ b/src/Tests/Stryker/Tests/MemoryVectorTests.cs @@ -0,0 +1,79 @@ +using FlatSharp.Internal; +using System.Linq.Expressions; +using System.Threading; + +namespace FlatSharpStrykerTests; + +public class MemoryVectorTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void MemoryVector(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Vectors vectors = parsed.Vectors; + Assert.True(vectors.IsInitialized); + Assert.True(Vectors.IsStaticInitialized); + + Memory vector = vectors.Memory.Value; + Assert.Equal(5, vector.Length); + for (int i = 0; i < vector.Length; ++i) + { + Assert.Equal(i + 1, vector.Span[i]); + } + + Helpers.AssertMutationWorks(option, vectors, false, s => s.Memory, new byte[0]); + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void MemoryVector_NotPresent(FlatBufferDeserializationOption option) + { + Root root = new Root() { Vectors = new() }; + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Assert.Null(parsed.Vectors.Memory); + } + + private Root CreateRoot(out byte[] expectedData) + { + static byte B(char c) => (byte)c; + + Root root = new() + { + Vectors = new() + { + Memory = new byte[] { 1, 2, 3, 4, 5, } + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 248, 255, 255, 255, // soffset to vtable + 16, 0, 0, 0, // uoffset to vector + + 10, 0, // vtable length + 8, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 4, 0, // field 2 (memory) + 0, 0, // padding + + 5, 0, 0, 0, // vector length + 1, 2, 3, 4, 5, + }; + + return root; + } +} diff --git a/src/Tests/Stryker/Tests/StringVectorFieldTests.cs b/src/Tests/Stryker/Tests/StringVectorTests.cs similarity index 98% rename from src/Tests/Stryker/Tests/StringVectorFieldTests.cs rename to src/Tests/Stryker/Tests/StringVectorTests.cs index cd2c19af..8fb23636 100644 --- a/src/Tests/Stryker/Tests/StringVectorFieldTests.cs +++ b/src/Tests/Stryker/Tests/StringVectorTests.cs @@ -4,7 +4,7 @@ namespace FlatSharpStrykerTests; -public class StringVectorFieldTests +public class StringVectorTests { [Theory] [ClassData(typeof(DeserializationOptionClassData))] diff --git a/src/Tests/Stryker/Tests/UnionFieldTests.cs b/src/Tests/Stryker/Tests/UnionFieldTests.cs index 4b9f4864..71e18561 100644 --- a/src/Tests/Stryker/Tests/UnionFieldTests.cs +++ b/src/Tests/Stryker/Tests/UnionFieldTests.cs @@ -6,6 +6,27 @@ namespace FlatSharpStrykerTests; public class UnionFieldTests { + [Fact] + public void InvalidConstructors() + { + Assert.Throws(() => new FunUnion((string)null)); + Assert.Throws(() => new FunUnion((RefStruct)null)); + Assert.Throws(() => new FunUnion((Key)null)); + } + + [Fact] + public void InvalidGetters() + { + FunUnion a = new FunUnion(new RefStruct()); + + Assert.Throws(() => a.ValueStruct); + Assert.Throws(() => a.Key); + Assert.Throws(() => a.str); + + a = new FunUnion(new ValueStruct()); + Assert.Throws(() => a.RefStruct); + } + [Theory] [ClassData(typeof(DeserializationOptionClassData))] public void StringMember(FlatBufferDeserializationOption option) diff --git a/src/Tests/Stryker/Tests/UnionVectorTests.cs b/src/Tests/Stryker/Tests/UnionVectorTests.cs new file mode 100644 index 00000000..8745a127 --- /dev/null +++ b/src/Tests/Stryker/Tests/UnionVectorTests.cs @@ -0,0 +1,336 @@ +using FlatSharp.Internal; +using System.IO; +using System.Linq.Expressions; +using System.Threading; + +namespace FlatSharpStrykerTests; + +public class UnionVectorTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void UnionVector(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Vectors vectors = parsed.Vectors; + Assert.True(vectors.IsInitialized); + Assert.True(Vectors.IsStaticInitialized); + + IList sourceUnions = root.Vectors.Union; + IList unions = vectors.Union; + + Assert.NotNull(unions); + Assert.Equal(sourceUnions.Count, unions.Count); + + for (int i = 0; i < unions.Count; ++i) + { + FunUnion left = sourceUnions[i]; + FunUnion right = unions[i]; + + Assert.Equal(sourceUnions[i].Discriminator, unions[i].Discriminator); + Assert.Equal(sourceUnions[i].Kind, unions[i].Kind); + + { + object li = left.Accept(new()); + object ri = right.Accept(new()); + + Assert.IsAssignableFrom(li.GetType(), ri); + } + + switch (left.Kind) + { + case FunUnion.ItemKind.Key: + { + Assert.Equal(left.Key.Name, right.Key.Name); + Assert.Equal(left.Item4.Value, right.Item4.Value); + + Assert.True(left.TryGet(out Key _)); + Assert.True(right.TryGet(out Key _)); + Assert.False(left.TryGet(out RefStruct _)); + Assert.False(right.TryGet(out RefStruct _)); + } + break; + + + case FunUnion.ItemKind.RefStruct: + { + Assert.Equal(left.RefStruct.A, right.RefStruct.A); + Assert.Equal(left.Item1.B, right.Item1.B); + Assert.True(left.TryGet(out RefStruct _)); + Assert.True(right.TryGet(out RefStruct _)); + Assert.False(left.TryGet(out Key _)); + Assert.False(right.TryGet(out Key _)); + } + break; + + case FunUnion.ItemKind.str: + { + Assert.Equal(left.str, right.str); + Assert.Equal(left.Item3, right.Item3); + Assert.True(left.TryGet(out string _)); + Assert.True(right.TryGet(out string _)); + Assert.False(left.TryGet(out ValueStruct _)); + Assert.False(right.TryGet(out ValueStruct _)); + } + break; + + case FunUnion.ItemKind.ValueStruct: + { + Assert.Equal(left.ValueStruct.A, right.ValueStruct.A); + Assert.Equal(left.Item2.B, right.Item2.B); + Assert.True(left.TryGet(out ValueStruct _)); + Assert.True(right.TryGet(out ValueStruct _)); + Assert.False(left.TryGet(out string _)); + Assert.False(right.TryGet(out string _)); + } + break; + } + } + + Helpers.AssertSequenceEqual(expectedData, buffer); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void UnionVector_Invalid_MissingOffset(FlatBufferDeserializationOption option) + { + CreateInvalid_MissingOffset(out byte[] buffer); + + Assert.Throws(() => + { + var item = Root.Serializer.Parse(buffer, option); + var x = item.Vectors.Union[0]; + }); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void UnionVector_Invalid_InvalidDiscriminator(FlatBufferDeserializationOption option) + { + CreateInvalid_InvalidDiscriminator(out byte[] buffer); + + var ex = Assert.Throws(() => + { + var item = Root.Serializer.Parse(buffer, option); + var x = item.Vectors.Union[0]; + }); + + Assert.Equal("Exception parsing union 'FlatSharpStrykerTests.FunUnion'. Discriminator = 7", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void UnionVector_Invalid_MismatchedCounts(FlatBufferDeserializationOption option) + { + CreateInvalid_MismatchedCounts(out byte[] buffer); + + var ex = Assert.Throws(() => + { + var item = Root.Serializer.Parse(buffer, option); + var x = item.Vectors.Union[0]; + }); + + Assert.Equal("Union vector had mismatched number of discriminators and offsets.", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void NotPresent(FlatBufferDeserializationOption option) + { + Root root = new Root() { Vectors = new() }; + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + Assert.Null(parsed.Vectors.Union); + } + + private struct Visitor : FunUnion.Visitor + { + public object Visit(RefStruct item) => item; + + public object Visit(ValueStruct item) => item; + + public object Visit(string item) => item; + + public object Visit(Key item) => item; + } + + private Root CreateRoot(out byte[] expectedData) + { + static byte B(char c) => (byte)c; + + Root root = new() + { + Vectors = new() + { + Union = Helpers.CreateList( + new FunUnion(new Key { Name = "b", Value = Fruit.Apple }), + new FunUnion(new ValueStruct { A = 1, B = 2 }), + new FunUnion(new RefStruct { A = 3, B = Fruit.Banana, __flatsharp__C_0 = 5, __flatsharp__C_1 = 6, __flatsharp__D_0 = 7, __flatsharp__D_1 = 8 }), + new FunUnion("c")) + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 244, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to union discriminators + 28, 0, 0, 0, // uoffset to union offsets + + 16, 0, // vtable length + 12, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (memory) + 0, 0, // field 3 (stirng) + 4, 0, // field 4 (union vector discriminators) + 8, 0, // field 5 (union vector offsets) + + 4, 0, 0, 0, + 4, 2, 1, 3, // discriminators + + 4, 0, 0, 0, // union offsets + 16, 0, 0, 0, + 40, 0, 0, 0, + 52, 0, 0, 0, + 76, 0, 0, 0, + + 244, 255, 255, 255, // key soffset to vtable + 0, 0, 0, 0, // apple + 12, 0, 0, 0, // offset to name. + + 8, 0, 12, 0, // key vtable + 8, 0, 4, 0, + + 1, 0, 0, 0, // key name. + B('b'), 0, 0, 0, + + 1, 0, 0, 0, // valuestruct + 2, 0, 0, 0, + 0, 0, 0, 0, // end value struct + padding + + 0, 0, 0, 0, // padding to 8 byte alignment. + + 3, 0, 0, 0, // ref struct + 0, 0, 0, 0, + 1, 0, 0, 0, + 5, 6, 7, 8, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + + 1, 0, 0, 0, // string + B('c'), 0 + }; + + return root; + } + + private void CreateInvalid_MissingOffset(out byte[] expectedData) + { + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 244, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to union discriminators + 28, 0, 0, 0, // uoffset to union offsets + + 16, 0, // vtable length + 12, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (memory) + 0, 0, // field 3 (stirng) + 4, 0, // field 4 (union vector discriminators) + 8, 0, // field 5 (union vector offsets) + + 1, 0, 0, 0, + 4, 0, 0, 0, // discriminators + + 1, 0, 0, 0, // union offsets + 0, 0, 0, 0, + }; + } + + private void CreateInvalid_InvalidDiscriminator(out byte[] expectedData) + { + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 244, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to union discriminators + 28, 0, 0, 0, // uoffset to union offsets + + 16, 0, // vtable length + 12, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (memory) + 0, 0, // field 3 (stirng) + 4, 0, // field 4 (union vector discriminators) + 8, 0, // field 5 (union vector offsets) + + 1, 0, 0, 0, + 7, 0, 0, 0, // discriminators + + 1, 0, 0, 0, // union offsets + 0, 0, 0, 0, + }; + } + + private void CreateInvalid_MismatchedCounts(out byte[] expectedData) + { + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 244, 255, 255, 255, // soffset to vtable + 24, 0, 0, 0, // uoffset to union discriminators + 28, 0, 0, 0, // uoffset to union offsets + + 16, 0, // vtable length + 12, 0, // table length + 0, 0, // field 0 + 0, 0, // field 1 + 0, 0, // field 2 (memory) + 0, 0, // field 3 (stirng) + 4, 0, // field 4 (union vector discriminators) + 8, 0, // field 5 (union vector offsets) + + 3, 0, 0, 0, + 1, 2, 3, 0, // discriminators + + 1, 0, 0, 0, // union offsets + 0, 0, 0, 0, + }; + } +} From a9bb3a8d76af3150ff946d58398752cdff5af14e Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 16:55:51 -0800 Subject: [PATCH 20/31] 80%! --- src/FlatSharp.Compiler/FlatSharpCompiler.cs | 5 + .../SchemaModel/TableSchemaModel.cs | 6 +- .../Attributes/FlatBufferTableAttribute.cs | 5 + .../IFlatBufferProgressiveObject.cs | 33 --- .../DeserializeClassDefinition.cs | 55 ----- .../RoslynSerializerGenerator.cs | 10 +- src/FlatSharp/TypeModel/TableTypeModel.cs | 5 + src/Tests/FlatSharpEndToEndTests/Helpers.cs | 59 ++++- src/Tests/Stryker/CodeGen/MockBitConverter.cs | 20 ++ src/Tests/Stryker/Tests/FullTreeTests.cs | 206 ++++++++++++++++++ src/Tests/Stryker/Tests/IndexedVectorTests.cs | 113 +++++++++- .../Stryker/Tests/RefStructVectorTests.cs | 133 +++++++++++ src/Tests/Stryker/Tests/StructFieldTests.cs | 124 ++++++----- .../Stryker/Tests/ValueStructVectorTests.cs | 118 ++++++++++ 14 files changed, 736 insertions(+), 156 deletions(-) delete mode 100644 src/FlatSharp.Runtime/IFlatBufferProgressiveObject.cs create mode 100644 src/Tests/Stryker/CodeGen/MockBitConverter.cs create mode 100644 src/Tests/Stryker/Tests/FullTreeTests.cs create mode 100644 src/Tests/Stryker/Tests/RefStructVectorTests.cs create mode 100644 src/Tests/Stryker/Tests/ValueStructVectorTests.cs diff --git a/src/FlatSharp.Compiler/FlatSharpCompiler.cs b/src/FlatSharp.Compiler/FlatSharpCompiler.cs index b4e542fa..11929d08 100644 --- a/src/FlatSharp.Compiler/FlatSharpCompiler.cs +++ b/src/FlatSharp.Compiler/FlatSharpCompiler.cs @@ -115,6 +115,11 @@ private static int RunCompiler(CompilerOptions options) try { cSharp = CreateCSharp(bfbs, inputHash, options); + + if (options.MutationTestingMode) + { + cSharp = cSharp.Replace("BitConverter", "MockBitConverter"); + } } catch (Exception ex) { diff --git a/src/FlatSharp.Compiler/SchemaModel/TableSchemaModel.cs b/src/FlatSharp.Compiler/SchemaModel/TableSchemaModel.cs index b5dcbdac..564488aa 100644 --- a/src/FlatSharp.Compiler/SchemaModel/TableSchemaModel.cs +++ b/src/FlatSharp.Compiler/SchemaModel/TableSchemaModel.cs @@ -86,10 +86,12 @@ protected override void EmitClassDefinition(CodeWriter writer, CompileContext co string fileId = string.Empty; if (this.Schema.RootTable?.Name == this.FullName && !string.IsNullOrEmpty(this.Schema.FileIdentifier)) { - fileId = $"{nameof(FlatBufferTableAttribute.FileIdentifier)} = \"{this.Schema.FileIdentifier}\""; + fileId = $", {nameof(FlatBufferTableAttribute.FileIdentifier)} = \"{this.Schema.FileIdentifier}\""; } - string attribute = $"[FlatBufferTable({fileId})]"; + string emitSerializer = $"{nameof(FlatBufferTableAttribute.BuildSerializer)} = {(this.Attributes.DeserializationOption is not null ? "true" : "false")}"; + + string attribute = $"[FlatBufferTable({emitSerializer}{fileId})]"; writer.AppendSummaryComment(this.Documentation); writer.AppendLine(attribute); diff --git a/src/FlatSharp.Runtime/Attributes/FlatBufferTableAttribute.cs b/src/FlatSharp.Runtime/Attributes/FlatBufferTableAttribute.cs index f5663935..be8006af 100644 --- a/src/FlatSharp.Runtime/Attributes/FlatBufferTableAttribute.cs +++ b/src/FlatSharp.Runtime/Attributes/FlatBufferTableAttribute.cs @@ -26,4 +26,9 @@ public class FlatBufferTableAttribute : Attribute /// Specifies the file identifier for serialized tables. Must be precisely 4 ASCII characters. /// public string? FileIdentifier { get; set; } + + /// + /// Indicates that a serializer should be built for the given table. + /// + public bool BuildSerializer { get; set; } = true; } diff --git a/src/FlatSharp.Runtime/IFlatBufferProgressiveObject.cs b/src/FlatSharp.Runtime/IFlatBufferProgressiveObject.cs deleted file mode 100644 index 8831269b..00000000 --- a/src/FlatSharp.Runtime/IFlatBufferProgressiveObject.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2021 James Courtney - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace FlatSharp.Internal; - -/// -/// An interface applied to progressive items derialized by FlatSharp. Used for testing. -/// -public interface IFlatBufferProgressiveObject -{ - /// - /// Indicates if the given field is present in the buffer. - /// - bool IsFieldPresent(int index); - - /// - /// Indicates if the given field is loaded in the object. - /// - bool IsFieldLoaded(int index); -} \ No newline at end of file diff --git a/src/FlatSharp/Serialization/DeserializeClassDefinition.cs b/src/FlatSharp/Serialization/DeserializeClassDefinition.cs index 3bb26e6a..2bb74a76 100644 --- a/src/FlatSharp/Serialization/DeserializeClassDefinition.cs +++ b/src/FlatSharp/Serialization/DeserializeClassDefinition.cs @@ -293,7 +293,6 @@ private sealed class {this.ClassName} , {interfaceGlobalName} , {typeof(IPoolableObject).GetGlobalCompilableTypeName()} , {typeof(IPoolableObjectDebug).GetGlobalCompilableTypeName()} - {(isProgressive ? $", {typeof(IFlatBufferProgressiveObject).GetGlobalCompilableTypeName()}" : string.Empty)} where TInputBuffer : IInputBuffer {{ private static readonly {typeof(FlatBufferDeserializationContext).GetGlobalCompilableTypeName()} {CtorContextVariableName} @@ -325,65 +324,11 @@ private sealed class {this.ClassName} set => this.{IsRootVariableName} = value; }} - {( - this.options.DeserializationOption == FlatBufferDeserializationOption.Progressive - ? this.ImplementProgressiveReflectionMethods() - : string.Empty - )} - {string.Join("\r\n", this.propertyOverrides)} {string.Join("\r\n", this.readMethods)} }} "; } - - protected virtual string ImplementProgressiveReflectionMethods() - { - FlatSharpInternal.Assert(this.options.DeserializationOption == FlatBufferDeserializationOption.Progressive, "expecting progressive"); - - string interfaceName = typeof(IFlatBufferProgressiveObject).GetGlobalCompilableTypeName(); - - string isLoaded = this.options.DeserializationOption switch - { - FlatBufferDeserializationOption.Lazy => "return false;", - FlatBufferDeserializationOption.Greedy or FlatBufferDeserializationOption.GreedyMutable => "return true;", - FlatBufferDeserializationOption.Progressive => - string.Join("\r\n", - this.itemModels - .Select( - imm => $"if (index == {imm.Index}) {{ return ({GetHasValueFieldName(imm)} & {GetHasValueFieldMask(imm)}) != 0; }}") - .Concat(new[] { "return false;" })), - _ => string.Empty, // won't compile - }; - - if (this.typeModel.SchemaType == FlatBufferSchemaType.Table) - { - return $$""" - [{{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}}] - bool {{interfaceName}}.IsFieldPresent(int index) => {{vtableAccessor}}.OffsetOf(this.{{InputBufferVariableName}}, index) != 0; - - [{{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}}] - bool {{interfaceName}}.IsFieldLoaded(int index) - { - {{isLoaded}} - } - """; - } - else - { - return $$""" - [{{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}}] - bool {{interfaceName}}.IsFieldPresent(int index) => true; - - [{{typeof(ExcludeFromCodeCoverageAttribute).GetCompilableTypeName()}}] - bool {{interfaceName}}.IsFieldLoaded(int index) - { - {{isLoaded}} - } - """; - } - } - protected virtual string GetSetterBody(ItemMemberModel itemModel) { List setterLines = new List(); diff --git a/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs b/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs index 16f88903..383dea7c 100644 --- a/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs +++ b/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs @@ -624,8 +624,14 @@ internal string ImplementHelperClass(ITypeModel typeModel, IMethodNameResolver r string serializerBody = string.Empty; if (typeModel.SchemaType == FlatBufferSchemaType.Table) { - // Generate a serializer as well. - (serializerBody, _) = ImplementInterfaceMethod(typeModel.ClrType, resolver); + TableTypeModel? tableModel = typeModel as TableTypeModel; + FlatSharpInternal.Assert(tableModel is not null, "expecting table"); + + if (tableModel.ShouldBuildISerializer) + { + // Generate a serializer as well. + (serializerBody, _) = ImplementInterfaceMethod(typeModel.ClrType, resolver); + } } string @class = diff --git a/src/FlatSharp/TypeModel/TableTypeModel.cs b/src/FlatSharp/TypeModel/TableTypeModel.cs index 398158cf..2d73f201 100644 --- a/src/FlatSharp/TypeModel/TableTypeModel.cs +++ b/src/FlatSharp/TypeModel/TableTypeModel.cs @@ -109,6 +109,11 @@ internal TableTypeModel(Type clrType, TypeModelContainer typeModelProvider) : ba /// public TableMemberModel? KeyMember { get; private set; } + /// + /// If we should build an ISerailizer implementation for this type. + /// + public bool ShouldBuildISerializer => this.attribute.BuildSerializer; + /// /// Gets the maximum size of a table assuming all members are populated include the vtable offset. /// Does not consider alignment of the table, but does consider worst-case alignment of the members. diff --git a/src/Tests/FlatSharpEndToEndTests/Helpers.cs b/src/Tests/FlatSharpEndToEndTests/Helpers.cs index 8f17ba50..813145aa 100644 --- a/src/Tests/FlatSharpEndToEndTests/Helpers.cs +++ b/src/Tests/FlatSharpEndToEndTests/Helpers.cs @@ -16,6 +16,7 @@ using FlatSharp.Internal; using System.Linq.Expressions; +using System.Reflection; using System.Threading; namespace FlatSharpEndToEndTests; @@ -89,6 +90,29 @@ public static void AssertMutationWorks( Expression> propertyLambda, TProperty newValue) { + Assert.True(parent is IFlatBufferDeserializedObject); + + { + var dobj = (IFlatBufferDeserializedObject)parent; + + Assert.Equal(option, dobj.DeserializationContext.DeserializationOption); + Assert.Equal(typeof(TSource), dobj.TableOrStructType); + + switch (option) + { + case FlatBufferDeserializationOption.Greedy: + case FlatBufferDeserializationOption.GreedyMutable: + Assert.False(dobj.CanSerializeWithMemoryCopy); + Assert.Null(dobj.InputBuffer); + break; + + default: + Assert.True(dobj.CanSerializeWithMemoryCopy); + Assert.NotNull(dobj.InputBuffer); + break; + } + } + MemberExpression member = propertyLambda.Body as MemberExpression; PropertyInfo propInfo = member.Member as PropertyInfo; Action action = () => propInfo.SetMethod.Invoke(parent, new object[] { newValue }); @@ -205,14 +229,39 @@ public static void AssertMutationWorks( public static T TestProgressiveFieldLoad(int index, bool expected, P parent, Func getter) { - Assert.False(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); - Assert.Equal(expected, ((IFlatBufferProgressiveObject)parent).IsFieldPresent(index)); + IFlatBufferDeserializedObject obj = (IFlatBufferDeserializedObject)parent; - T item = getter(parent); + int maskNum = index / 8; + byte bitMask = (byte)(1 << (index % 8)); + + FieldInfo mask = parent.GetType().GetField($"__mask{maskNum}", BindingFlags.NonPublic | BindingFlags.Instance); + FieldInfo vtable = parent.GetType().GetField("__vtable", BindingFlags.NonPublic | BindingFlags.Instance); + + bool IsFieldLoaded() + { + byte value = (byte)mask.GetValue(parent); + return (value & bitMask) != 0; + } + + bool IsFieldPresent() + { + if (typeof(P).GetCustomAttribute() is not null) + { + return true; + } + + IVTable vt = (IVTable)vtable.GetValue(parent); + return vt.OffsetOf(obj.InputBuffer!, index) != 0; + } + + Assert.False(IsFieldLoaded()); + Assert.Equal(expected, IsFieldPresent()); for (int i = 0; i < 10; ++i) { - Assert.True(((IFlatBufferProgressiveObject)parent).IsFieldLoaded(index)); + T item = getter(parent); + + Assert.True(IsFieldLoaded()); T next = getter(parent); if (typeof(T).IsValueType) @@ -225,7 +274,7 @@ public static T TestProgressiveFieldLoad(int index, bool expected, P paren } } - return item; + return getter(parent); } private static int counter; diff --git a/src/Tests/Stryker/CodeGen/MockBitConverter.cs b/src/Tests/Stryker/CodeGen/MockBitConverter.cs new file mode 100644 index 00000000..36bcbadc --- /dev/null +++ b/src/Tests/Stryker/CodeGen/MockBitConverter.cs @@ -0,0 +1,20 @@ +using FlatSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace FlatSharp.Internal; + +public static class MockBitConverter +{ + private static readonly ThreadLocal isLE = new(() => true); + + public static bool IsLittleEndian + { + get => isLE.Value; + set => isLE.Value = value; + } +} diff --git a/src/Tests/Stryker/Tests/FullTreeTests.cs b/src/Tests/Stryker/Tests/FullTreeTests.cs new file mode 100644 index 00000000..861d4c06 --- /dev/null +++ b/src/Tests/Stryker/Tests/FullTreeTests.cs @@ -0,0 +1,206 @@ +using FlatSharp.Internal; +using NuGet.Frameworks; +using System.Linq.Expressions; +using System.Net.Http.Headers; +using System.Threading; + +namespace FlatSharpStrykerTests; + +public class FullTreeTests +{ + [Fact] + public void GetMaxSize() + { + Root root = this.CreateRoot(); + int maxSize = Root.Serializer.GetMaxSize(root); + + Assert.Equal(730, maxSize); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Clone(FlatBufferDeserializationOption option) + { + Root root = this.CreateRoot().SerializeAndParse(option); + + Root copy = new Root(root); + + Assert.NotSame(root, copy); + Assert.Equal(typeof(Root), copy.GetType()); + Assert.NotEqual(typeof(Root), root.GetType()); + + { + Vectors rv = root.Vectors; + Vectors cv = copy.Vectors; + + Assert.NotSame(rv, cv); + Assert.NotSame(rv.GetType(), cv.GetType()); + Assert.Equal(typeof(Vectors), cv.GetType()); + + Verify(rv.RefStruct, cv.RefStruct, Verify); + Verify(rv.ValueStruct, cv.ValueStruct, Verify); + Verify(rv.Str, cv.Str, Assert.Equal); + Verify(rv.Union, cv.Union, Assert.Equal); + Verify(rv.Indexed, cv.Indexed, Verify); + } + + { + Fields rf = root.Fields; + Fields cf = copy.Fields; + + Assert.NotSame(rf, cf); + Assert.NotSame(rf.GetType(), cf.GetType()); + Assert.Equal(typeof(Fields), cf.GetType()); + + Assert.Equal(rf.Memory, cf.Memory); + Assert.Equal(rf.ScalarWithDefault, cf.ScalarWithDefault); + Assert.Equal(rf.Str, cf.Str); + Verify(rf.RefStruct, cf.RefStruct); + Verify(rf.ValueStruct, cf.ValueStruct, Verify); + Verify(rf.Union, cf.Union, Verify); + Assert.Equal(rf.Memory, cf.Memory); + } + } + + private static void Verify(IIndexedVector a, IIndexedVector b, Action verify) + where T : class + { + Assert.NotSame(a, b); + Assert.Equal(a.Count, b.Count); + + foreach (var kvp in a) + { + TKey key = kvp.Key; + T value = kvp.Value; + + Assert.True(b.TryGetValue(key, out T? bValue)); + verify(value, bValue); + } + } + + private static void Verify(IList a, IList b, Action comparer) + { + Assert.NotSame(a, b); + Assert.Equal(a.Count, b.Count); + + for (int i = 0; i < a.Count; ++i) + { + comparer(a[i], b[i]); + } + } + + private static void Verify(T? a, T? b, Action action) where T : struct + { + Assert.Equal(a.HasValue, b.HasValue); + if (a.HasValue) + { + action(a.Value, b.Value); + } + } + + private static void Verify(ValueStruct a, ValueStruct b) + { + Assert.Equal(a.A, b.A); + Assert.Equal(a.B, b.B); + Assert.Equal(a.C(0), b.C(0)); + Assert.Equal(a.C(1), b.C(1)); + } + + private static void Verify(FunUnion a, FunUnion b) + { + Assert.Equal(a.Discriminator, b.Discriminator); + + switch (a.Kind) + { + case FunUnion.ItemKind.Key: + Verify(a.Key, b.Key); + return; + + case FunUnion.ItemKind.RefStruct: + Verify(a.RefStruct, b.RefStruct); + return; + + case FunUnion.ItemKind.str: + Assert.Equal(a.str, b.str); + return; + + case FunUnion.ItemKind.ValueStruct: + Verify(a.ValueStruct, b.ValueStruct); + return; + + default: + Assert.False(true); + return; + } + } + + private static void Verify(Key a, Key b) + { + Assert.NotSame(a, b); + Assert.Equal(a.Name, b.Name); + Assert.Equal(a.Value, b.Value); + } + + private static void Verify(RefStruct a, RefStruct b) + { + Assert.NotSame(a, b); + Assert.Equal(a.A, b.A); + Assert.Equal(a.B, b.B); + Assert.Equal(a.C[0], b.C[0]); + Assert.Equal(a.C[1], b.C[1]); + Assert.Equal(a.D[0], b.D[0]); + Assert.Equal(a.D[1], b.D[1]); + Verify(a.E, b.E); + } + + private Root CreateRoot() + { + static RefStruct CreateRefStruct() + { + RefStruct rs = new() + { + A = 1, + B = Fruit.Strawberry, + E = new ValueStruct + { + A = 4, + B = 5, + } + }; + + rs.C.CopyFrom(new sbyte[] { 1, 2 }.AsSpan()); + rs.D.CopyFrom(new sbyte[] { 1, 2 }.ToList()); + + return rs; + } + + return new() + { + Fields = new() + { + Memory = 1, + RefStruct = CreateRefStruct(), + ScalarWithDefault = 1234, + Str = "hello", + Union = new(new Key() { Name = "fred", Value = Fruit.Strawberry }), + ValueStruct = CreateRefStruct().E + }, + + Vectors = new() + { + Indexed = new IndexedVector + { + { new Key { Name = "velma", Value = Fruit.Banana } }, + { new Key { Name = "scooby", Value = Fruit.Pear } }, + { new Key { Name = "shaggy", Value = Fruit.Apple } }, + }, + + Memory = new byte[] { 1, 2, 3, 4, }, + RefStruct = Helpers.CreateList(CreateRefStruct(), CreateRefStruct()), + Str = Helpers.CreateList("abc", "def"), + Union = Helpers.CreateList(new FunUnion("hi"), new FunUnion(new ValueStruct())), + ValueStruct = Helpers.CreateList(CreateRefStruct().E, CreateRefStruct().E), + }, + }; + } +} diff --git a/src/Tests/Stryker/Tests/IndexedVectorTests.cs b/src/Tests/Stryker/Tests/IndexedVectorTests.cs index e73f993a..0d3f14fa 100644 --- a/src/Tests/Stryker/Tests/IndexedVectorTests.cs +++ b/src/Tests/Stryker/Tests/IndexedVectorTests.cs @@ -1,4 +1,5 @@ using FlatSharp.Internal; +using System.IO; using System.Linq.Expressions; using System.Threading; @@ -57,6 +58,114 @@ public void Missing(FlatBufferDeserializationOption option) Helpers.AssertSequenceEqual(expectedData, buffer); } + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void KeyNotSet_Serialize(FlatBufferDeserializationOption option) + { + Key key = new Key { Name = "fred", Value = Fruit.Apple }; + + Root root = new() + { + Vectors = new() + { + Indexed = new IndexedVector + { + { key } + } + } + }; + + key.Name = null!; + + var ex = Assert.Throws(() => root.SerializeAndParse(option)); + Assert.Equal("Table property 'FlatSharpStrykerTests.Key.Name' is marked as required, but was not set.", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void KeyNotSet_Parse(FlatBufferDeserializationOption option) + { + // missing required field. + byte[] data = + { + 4, 0, 0, 0, + 248, 255, 255, 255, + 12, 0, 0, 0, + + 6, 0, 8, 0, + 4, 0, 0, 0, + + 246, 255, 255, 255, + 24, 0, 0, 0, + 4, 0, + + 16, 0, 9, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 8, 0, 4, 0, + + 0, 0, + + 248, 255, 255, 255, + 0, 0, 0, 0, + + 8, 0, 8, 0, + 0, 0, 4, 0, + }; + + var ex = Assert.Throws(() => + { + var root = Root.Serializer.Parse(data, option); + string name = root.Fields.Union.Value.Key.Name; + }); + + Assert.Equal("Table property 'FlatSharpStrykerTests.Key.Name' is marked as required, but was missing from the buffer.", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Big(FlatBufferDeserializationOption option) + { + Root root = new Root + { + Vectors = new() + { + Indexed = new IndexedVector(), + } + }; + + List expectedKeys = new(); + for (int i = 0; i < 1000; ++i) + { + Key key = new() { Name = Guid.NewGuid().ToString("n"), Value = Fruit.Apple }; + expectedKeys.Add(key.Name); + root.Vectors.Indexed.Add(key); + } + + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + + var parsedIndexed = parsed.Vectors.Indexed; + + for (int i = 0; i < expectedKeys.Count; ++i) + { + Assert.True(parsedIndexed.ContainsKey(expectedKeys[i])); + Assert.NotNull(parsedIndexed[expectedKeys[i]]); + Assert.True(parsedIndexed.TryGetValue(expectedKeys[i], out Key key)); + Assert.NotNull(key); + Assert.Equal(expectedKeys[i], key.Name); + } + + for (int i = 0; i < expectedKeys.Count; ++i) + { + string unexpectedKey = Guid.NewGuid().ToString("n"); + + Assert.False(parsedIndexed.ContainsKey(unexpectedKey)); + Assert.Throws(() => parsedIndexed[unexpectedKey]); + Assert.False(parsedIndexed.TryGetValue(unexpectedKey, out Key key)); + Assert.Null(key); + } + } + private Root CreateRoot(out byte[] expectedData) { static byte B(char c) => (byte)c; @@ -115,10 +224,10 @@ private Root CreateRoot(out byte[] expectedData) 8, 0, 4, 0, 1, 0, 0, 0, - B('a'), 0, 0, 0, + B('a'), 0, 0, 0, 248, 255, 255, 255, // non-shared vtable. (banana is default) - 12, 0, 0, 0, + 12, 0, 0, 0, 6, 0, 8, 0, // vtable 4, 0, 0, 0, diff --git a/src/Tests/Stryker/Tests/RefStructVectorTests.cs b/src/Tests/Stryker/Tests/RefStructVectorTests.cs new file mode 100644 index 00000000..bf287feb --- /dev/null +++ b/src/Tests/Stryker/Tests/RefStructVectorTests.cs @@ -0,0 +1,133 @@ +using FlatSharp.Internal; +using System.Linq.Expressions; +using System.Threading; + +namespace FlatSharpStrykerTests; + +public class RefStructVectorTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Present(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] actualData); + + Assert.NotNull(root.Vectors.RefStruct); + + var vsr = root.Vectors.RefStruct; + var vsp = parsed.Vectors.RefStruct; + + Assert.Equal(vsr.Count, vsp.Count); + + for (int i = 0; i < vsr.Count; ++i) + { + var r = vsr[i]; + var p = vsp[i]; + + Assert.Equal(r.A, p.A); + Assert.Equal(r.B, p.B); + Assert.Equal(r.C[0], p.C[0]); + Assert.Equal(r.C[1], p.C[1]); + Assert.Equal(r.D[0], p.D[0]); + Assert.Equal(r.D[1], p.D[1]); + } + + Helpers.AssertSequenceEqual(expectedData, actualData); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Missing(FlatBufferDeserializationOption option) + { + Root root = CreateRoot_Missing(out byte[] expectedData); + root.SerializeAndParse(option, out byte[] actualData); + + Assert.Null(root.Vectors.RefStruct); + Helpers.AssertSequenceEqual(expectedData, actualData); + } + + private Root CreateRoot(out byte[] expectedData) + { + var rs1 = new RefStruct { A = 1, B = Fruit.Banana }; + rs1.C.CopyFrom(new sbyte[] { 3, 4 }.AsSpan()); + rs1.D.CopyFrom(new List { 5, 6, }); + + var rs2 = new RefStruct { A = 7, B = Fruit.Pear }; + rs2.C.CopyFrom(new sbyte[] { 9, 10 }.AsSpan()); + rs2.D.CopyFrom(new List { 11, 12, }); + + Root root = new() + { + Vectors = new() + { + RefStruct = Helpers.CreateList(rs1, rs2) + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 248, 255, 255, 255, // soffset to vtable (shared) + 12, 0, 0, 0, // uoffset to vector + + 6, 0, 8, 0, // vtable + 4, 0, 0, 0, + + 2, 0, 0, 0, + + 1, 0, 0, 0, + 0, 0, 0, 0, + 1, 0, 0, 0, + 3, 4, 5, 6, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + + 7, 0, 0, 0, + 0, 0, 0, 0, + 3, 0, 0, 0, + 9, 10, 11, 12, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + }; + + return root; + } + + private Root CreateRoot_Missing(out byte[] expectedData) + { + Root root = new() + { + Vectors = new() + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 252, 255, 255, 255, // soffset to vtable + + 4, 0, // vtable length + 4, 0, // table length + }; + + return root; + } +} diff --git a/src/Tests/Stryker/Tests/StructFieldTests.cs b/src/Tests/Stryker/Tests/StructFieldTests.cs index 97c2c537..82798787 100644 --- a/src/Tests/Stryker/Tests/StructFieldTests.cs +++ b/src/Tests/Stryker/Tests/StructFieldTests.cs @@ -9,65 +9,77 @@ public class StructFieldTests [ClassData(typeof(DeserializationOptionClassData))] public void ValueStructTableField(FlatBufferDeserializationOption option) { - ValueStruct vs = new() + try { - A = 4, - B = 3, - }; - - vs.C(0) = 1; - vs.C(1) = 2; - - Assert.Throws(() => vs.C(2) = 4); - Assert.Throws(() => vs.C(-1) = 4); - - Root root = new Root - { - Fields = new() + for (int i = 0; i < 2; ++i) { - ValueStruct = vs, + MockBitConverter.IsLittleEndian = i == 0; + + ValueStruct vs = new() + { + A = 4, + B = 3, + }; + + vs.C(0) = 1; + vs.C(1) = 2; + + Assert.Throws(() => vs.C(2) = 4); + Assert.Throws(() => vs.C(-1) = 4); + + Root root = new Root + { + Fields = new() + { + ValueStruct = vs, + } + }; + + Root parsed = root.SerializeAndParse(option, out byte[] buffer); + Fields fields = parsed.Fields; + + Assert.NotNull(fields); + Assert.Null(parsed.Vectors); + + Assert.NotNull(parsed.Fields.ValueStruct); + + ValueStruct vsParsed = parsed.Fields.ValueStruct.Value; + + Assert.Equal(vs.A, vsParsed.A); + Assert.Equal(vs.B, vsParsed.B); + Assert.Equal(vs.C(0), vsParsed.C(0)); + Assert.Equal(vs.C(1), vsParsed.C(1)); + + byte[] expectedBytes = + { + 4, 0, 0, 0, // offset to table start + 248, 255, 255, 255, // soffset to vtable. + 12, 0, 0, 0, // uoffset to field 0 (fields table) + 6, 0, // vtable length + 8, 0, // table length + 4, 0, // offset of field 0 + 0, 0, // padding + + 242, 255, 255, 255, // soffset to vtable + 4, 0, 0, 0, // valuestruct.a + 3, 0, // valuestruct.b, padding + 1, 0, // valuestruct.c[0] + 2, 0, // valuestruct.c[1] + + 8, 0, // vtable length + 14, 0, // table length + 0, 0, // field 0 (not present) + 4, 0, // field 1 + }; + + Helpers.AssertSequenceEqual(expectedBytes, buffer); + Helpers.AssertMutationWorks(option, fields, false, p => p.ValueStruct, default); } - }; - - Root parsed = root.SerializeAndParse(option, out byte[] buffer); - Fields fields = parsed.Fields; - - Assert.NotNull(fields); - Assert.Null(parsed.Vectors); - - Assert.NotNull(parsed.Fields.ValueStruct); - - ValueStruct vsParsed = parsed.Fields.ValueStruct.Value; - - Assert.Equal(vs.A, vsParsed.A); - Assert.Equal(vs.B, vsParsed.B); - Assert.Equal(vs.C(0), vsParsed.C(0)); - Assert.Equal(vs.C(1), vsParsed.C(1)); - - byte[] expectedBytes = + } + finally { - 4, 0, 0, 0, // offset to table start - 248, 255, 255, 255, // soffset to vtable. - 12, 0, 0, 0, // uoffset to field 0 (fields table) - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of field 0 - 0, 0, // padding - - 242, 255, 255, 255, // soffset to vtable - 4, 0, 0, 0, // valuestruct.a - 3, 0, // valuestruct.b, padding - 1, 0, // valuestruct.c[0] - 2, 0, // valuestruct.c[1] - - 8, 0, // vtable length - 14, 0, // table length - 0, 0, // field 0 (not present) - 4, 0, // field 1 - }; - - Helpers.AssertSequenceEqual(expectedBytes, buffer); - Helpers.AssertMutationWorks(option, fields, false, p => p.ValueStruct, default); + MockBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + } } @@ -153,8 +165,6 @@ public void ProgressiveFieldLoads() Root root = CreateRootReferenceStruct(out RefStruct rs, out ValueStruct vs, out byte[] expectedBytes); Root parsed = root.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer); - IFlatBufferProgressiveObject progressive = (IFlatBufferProgressiveObject)parsed; - Fields fields = Helpers.TestProgressiveFieldLoad(0, true, parsed, p => p.Fields); Assert.NotNull(fields); diff --git a/src/Tests/Stryker/Tests/ValueStructVectorTests.cs b/src/Tests/Stryker/Tests/ValueStructVectorTests.cs new file mode 100644 index 00000000..3e09fc49 --- /dev/null +++ b/src/Tests/Stryker/Tests/ValueStructVectorTests.cs @@ -0,0 +1,118 @@ +using FlatSharp.Internal; +using System.Linq.Expressions; +using System.Threading; + +namespace FlatSharpStrykerTests; + +public class ValueStructVectorTests +{ + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Present(FlatBufferDeserializationOption option) + { + Root root = CreateRoot(out byte[] expectedData); + Root parsed = root.SerializeAndParse(option, out byte[] actualData); + + Assert.NotNull(parsed.Vectors.ValueStruct); + + var vsr = root.Vectors.ValueStruct; + var vsp = parsed.Vectors.ValueStruct; + + Assert.Equal(vsr.Count, vsp.Count); + + for (int i = 0; i < vsr.Count; ++i) + { + var r = vsr[i]; + var p = vsp[i]; + + Assert.Equal(r.A, p.A); + Assert.Equal(r.B, p.B); + Assert.Equal(r.C(0), p.C(0)); + Assert.Equal(r.C(1), p.C(1)); + } + + Helpers.AssertSequenceEqual(expectedData, actualData); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Missing(FlatBufferDeserializationOption option) + { + Root root = CreateRoot_Missing(out byte[] expectedData); + root.SerializeAndParse(option, out byte[] actualData); + + Assert.Null(root.Vectors.ValueStruct); + Helpers.AssertSequenceEqual(expectedData, actualData); + } + + private Root CreateRoot(out byte[] expectedData) + { + var vs1 = new ValueStruct { A = 1, B = 2 }; + var vs2 = new ValueStruct { A = 5, B = 6 }; + + vs1.C(0) = 3; + vs1.C(1) = 4; + vs2.C(0) = 7; + vs2.C(1) = 8; + + Root root = new() + { + Vectors = new() + { + ValueStruct = Helpers.CreateList(vs1, vs2) + } + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 8, 0, 0, 0, // soffset to vtable (shared) + 4, 0, 0, 0, // uoffset to vector + + 2, 0, 0, 0, + + 1, 0, 0, 0, + 2, 0, 3, 0, + 4, 0, 0, 0, + + 5, 0, 0, 0, + 6, 0, 7, 0, + 8, 0, 0, 0, + }; + + return root; + } + + private Root CreateRoot_Missing(out byte[] expectedData) + { + Root root = new() + { + Vectors = new() + }; + + expectedData = new byte[] + { + 4, 0, 0, 0, + 248, 255, 255, 255, // soffset to vtable + 12, 0, 0, 0, // uoffset to Vectors subtable. + + // Vtable for Root + 8, 0, 8, 0, + 0, 0, 4, 0, + + 252, 255, 255, 255, // soffset to vtable + + 4, 0, // vtable length + 4, 0, // table length + }; + + return root; + } +} From a3788e8cb19333596fdf17723b8dbff9d8d1d167 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 16:56:47 -0800 Subject: [PATCH 21/31] comment --- src/Tests/FlatSharpEndToEndTests/Helpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tests/FlatSharpEndToEndTests/Helpers.cs b/src/Tests/FlatSharpEndToEndTests/Helpers.cs index 813145aa..2e557643 100644 --- a/src/Tests/FlatSharpEndToEndTests/Helpers.cs +++ b/src/Tests/FlatSharpEndToEndTests/Helpers.cs @@ -71,7 +71,7 @@ public static void AssertSequenceEqual( Span expected, Span actual) { - var combined = expected.ToArray().Zip(actual.ToArray()).ToArray(); + //var combined = expected.ToArray().Zip(actual.ToArray()).ToArray(); Assert.Equal(expected.Length, actual.Length); for (int i = 0; i < expected.Length; ++i) From 5868c73f02276abd3f9d18cf41967844409fc4f5 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 17:27:37 -0800 Subject: [PATCH 22/31] Fix tests --- .../FlatSharpCompilerTests/IncludeTests.cs | 12 ------------ src/Tests/FlatSharpCompilerTests/StructTests.cs | 17 ----------------- 2 files changed, 29 deletions(-) diff --git a/src/Tests/FlatSharpCompilerTests/IncludeTests.cs b/src/Tests/FlatSharpCompilerTests/IncludeTests.cs index ecff5a8a..0d86b18f 100644 --- a/src/Tests/FlatSharpCompilerTests/IncludeTests.cs +++ b/src/Tests/FlatSharpCompilerTests/IncludeTests.cs @@ -51,12 +51,6 @@ namespace Foobar; PropertyInfo tableB = aType.GetProperty("TableB"); Assert.Equal(bType, tableB.PropertyType); - - var compiled = CompilerTestHelpers.CompilerTestSerializer.Compile(aType); - - byte[] data = new byte[100]; - compiled.Write(data, Activator.CreateInstance(aType)); - compiled.Parse(data); }); } @@ -139,12 +133,6 @@ namespace Foobar; Assert.Equal(bType, tableB.PropertyType); Assert.Equal(cType, tableC.PropertyType); Assert.Equal(dType, tableD.PropertyType); - - var compiled = CompilerTestHelpers.CompilerTestSerializer.Compile(aType); - - byte[] data = new byte[100]; - compiled.Write(data, Activator.CreateInstance(aType)); - compiled.Parse(data); }); } diff --git a/src/Tests/FlatSharpCompilerTests/StructTests.cs b/src/Tests/FlatSharpCompilerTests/StructTests.cs index ad619170..9de80fa9 100644 --- a/src/Tests/FlatSharpCompilerTests/StructTests.cs +++ b/src/Tests/FlatSharpCompilerTests/StructTests.cs @@ -59,22 +59,5 @@ struct Bar { dynamic dTable = table; Assert.Equal(3, dTable.defaultInt); dTable.foo = dFoo; - - byte[] destination = new byte[1024]; - - var serializer = CompilerTestHelpers.CompilerTestSerializer.Compile(table); - int bytesWritten = serializer.Write(destination, table); - object parsed = serializer.Parse(destination); - - Assert.True(tableType.IsAssignableFrom(parsed.GetType())); - - dynamic dParsedTable = parsed; - Assert.Equal(3, dParsedTable.defaultInt); - - dynamic dParsedFoo = dParsedTable.foo; - Assert.Equal(dFoo.id, dParsedFoo.id); - Assert.Equal(dFoo.count, dParsedFoo.count); - Assert.Equal(dFoo.prefix, dParsedFoo.prefix); - Assert.Equal(dFoo.length, dParsedFoo.length); } } From ce3c4cfbdce304b77a937ea2bcada0ae7d7fe715 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 17:57:40 -0800 Subject: [PATCH 23/31] Update codecov.yml --- .github/workflows/codecov.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index dddd331b..9301129a 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -78,9 +78,30 @@ jobs: --output pooling.coverage.xml ` --targetargs "src\FlatSharp.Compiler\bin\Debug\net7.0\FlatSharp.Compiler.dll --nullable-warnings false --normalize-field-names true --gen-poolable true --input `"$fbs`" -o src/tests/FlatSharpPoolableEndToEndTests" + - name: Run FlatSharp.Compiler (Stryker Tests) + # You may pin to the exact commit or the version. + # uses: Amadevus/pwsh-script@97a8b211a5922816aa8a69ced41fa32f23477186 + uses: Amadevus/pwsh-script@v2.0.3 + with: + # PowerShell script to execute in Actions-hydrated context + script: | + $fbs = (gci -r src/tests/Stryker/*.fbs) -join ";" + coverlet ` + .\src\FlatSharp.Compiler\bin\Debug\net7.0 ` + --skipautoprops ` + --use-source-link ` + --format opencover ` + --target "dotnet" ` + --output pooling.coverage.xml ` + --targetargs "src/FlatSharp.Compiler/bin/Debug/net7.0/FlatSharp.Compiler.dll --nullable-warnings false --normalize-field-names true --input `"$fbs`" -o src/tests/Stryker/CodeGen --mutation-testing-mode" + - name: Test working-directory: src run: dotnet test -c Debug -p:SignAssembly=false --collect:"XPlat Code Coverage" --settings Tests/coverlet.runsettings -f net7.0 + - name: Stryker Tests + working-directory: src/tests/Stryker/Tests + run: dotnet test -c Debug -p:SignAssembly=false --collect:"XPlat Code Coverage" --settings Tests/coverlet.runsettings -f net7.0 + - name: Upload run: codecov -f **/*coverage*.xml From 627ecefb90cd888e23025d0c30bb16da5d66f97f Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 18:05:23 -0800 Subject: [PATCH 24/31] Add coverlet to stryker --- src/Tests/Stryker/Tests/StrykerTests.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Tests/Stryker/Tests/StrykerTests.csproj b/src/Tests/Stryker/Tests/StrykerTests.csproj index 55a3f637..ab48db44 100644 --- a/src/Tests/Stryker/Tests/StrykerTests.csproj +++ b/src/Tests/Stryker/Tests/StrykerTests.csproj @@ -14,6 +14,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 2227ee97b400db2b5e143a5001b474ee19d51cab Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 18:08:25 -0800 Subject: [PATCH 25/31] Fix build error --- src/Tests/Stryker/Tests/MemoryVectorTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Tests/Stryker/Tests/MemoryVectorTests.cs b/src/Tests/Stryker/Tests/MemoryVectorTests.cs index 67daaf72..f555c386 100644 --- a/src/Tests/Stryker/Tests/MemoryVectorTests.cs +++ b/src/Tests/Stryker/Tests/MemoryVectorTests.cs @@ -40,8 +40,6 @@ public void MemoryVector_NotPresent(FlatBufferDeserializationOption option) private Root CreateRoot(out byte[] expectedData) { - static byte B(char c) => (byte)c; - Root root = new() { Vectors = new() From 8cb14bb0c1107bf5c97228241c94514e2901d272 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 18:20:37 -0800 Subject: [PATCH 26/31] Update codecov.yml --- .github/workflows/codecov.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 9301129a..d8197a1d 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -99,9 +99,5 @@ jobs: working-directory: src run: dotnet test -c Debug -p:SignAssembly=false --collect:"XPlat Code Coverage" --settings Tests/coverlet.runsettings -f net7.0 - - name: Stryker Tests - working-directory: src/tests/Stryker/Tests - run: dotnet test -c Debug -p:SignAssembly=false --collect:"XPlat Code Coverage" --settings Tests/coverlet.runsettings -f net7.0 - - name: Upload run: codecov -f **/*coverage*.xml From 3151125b08d8aa7940c3049878d9f40906d0d4ee Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 18:21:14 -0800 Subject: [PATCH 27/31] Remove coverlet --- src/Tests/Stryker/Tests/StrykerTests.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Tests/Stryker/Tests/StrykerTests.csproj b/src/Tests/Stryker/Tests/StrykerTests.csproj index ab48db44..55a3f637 100644 --- a/src/Tests/Stryker/Tests/StrykerTests.csproj +++ b/src/Tests/Stryker/Tests/StrykerTests.csproj @@ -14,10 +14,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - From c7c9f832315ed67442d90834ae37f6911b983185 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 18:34:29 -0800 Subject: [PATCH 28/31] Update codecov.yml --- .github/workflows/codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index d8197a1d..e8028d18 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -92,7 +92,7 @@ jobs: --use-source-link ` --format opencover ` --target "dotnet" ` - --output pooling.coverage.xml ` + --output stryker.coverage.xml ` --targetargs "src/FlatSharp.Compiler/bin/Debug/net7.0/FlatSharp.Compiler.dll --nullable-warnings false --normalize-field-names true --input `"$fbs`" -o src/tests/Stryker/CodeGen --mutation-testing-mode" - name: Test From c1d3a5891243d6d0ffae1c01fec496a4ed1a4e47 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 19:18:54 -0800 Subject: [PATCH 29/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 89891cbb..624eb80e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ All FlatSharp packages are published on nuget.org: - **FlatSharp.Runtime**: The runtime library. You always need this. - **FlatSharp.Compiler**: Build time compiler for generating C# from an FBS schema. -FlatSharp is a mature library and has been shipped to production at Microsoft, Unity3D, and others. Full status can be found at [ProjectStatus.md](ProjectStatus.md). +FlatSharp is a mature library and has been shipped to production at Microsoft, Unity3D, and others. Full status can be found at [ProjectStatus.md](ProjectStatus.md). FlatSharp is [extensively tested](https://github.com/jamescourtney/FlatSharp/wiki/Testing), using Mutation Testing, Code Coverage, Oracle Testing, and other techniques to ensure the library doesn't regress. ### Getting Started If you're completely new to FlatBuffers, take a minute to look over [the FlatBuffer overview](https://google.github.io/flatbuffers/index.html#flatbuffers_overview). Additionally, it's worth the time to understand the different elements of [FlatBuffer schemas](https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html). From 25600c6a12bae29366542ae7c2067ede010d7996 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 19:20:31 -0800 Subject: [PATCH 30/31] Delete stryker-config.json --- src/FlatSharp.Compiler/stryker-config.json | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/FlatSharp.Compiler/stryker-config.json diff --git a/src/FlatSharp.Compiler/stryker-config.json b/src/FlatSharp.Compiler/stryker-config.json deleted file mode 100644 index d7ba5551..00000000 --- a/src/FlatSharp.Compiler/stryker-config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "stryker-config": - { - "project": "FlatSharp.Compiler.csproj", - "test-projects": [ - "../Tests/FlatSharpCompilerTests/FlatSharpCompilerTests.csproj" - ], - "target-framework": "net6.0" - } -} \ No newline at end of file From 5193f69f3a22a1f6914ac5a4b21d5fe358b9a6a2 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Fri, 27 Jan 2023 20:11:41 -0800 Subject: [PATCH 31/31] Add 80% threshold --- src/Tests/Stryker/Tests/stryker-config.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Tests/Stryker/Tests/stryker-config.json b/src/Tests/Stryker/Tests/stryker-config.json index 6b027876..b7de38b4 100644 --- a/src/Tests/Stryker/Tests/stryker-config.json +++ b/src/Tests/Stryker/Tests/stryker-config.json @@ -7,6 +7,7 @@ "FlatSharp.Internal.FlatSharpInternal.AssertSizeOf" // Ignore assertions. ], - "reporters": [ "html", "cleartext" ] + "reporters": [ "html", "cleartext" ], + "thresholds": { "high": 90, "low": 80, "break": 80 } } }