From ddc432dfe6008d6559f4b8e17a8874623d009d72 Mon Sep 17 00:00:00 2001 From: James Courtney Date: Wed, 9 Aug 2023 15:13:12 -0700 Subject: [PATCH] Delete Legacy Tests (#398) Delete legacy tests, replace ifs with asserts, and more --- .github/workflows/codecov.yml | 4 - .github/workflows/dotnet.yml | 5 - .../FlatSharp.Compiler.csproj | 2 + src/FlatSharp.Compiler/FlatSharpCompiler.cs | 6 +- .../BaseReferenceTypeSchemaModel.cs | 2 +- .../SchemaModel/ValueUnionSchemaModel.cs | 6 + src/FlatSharp.Runtime/FlatSharpInternal.cs | 13 + .../GeneratedSerializerWrapper.cs | 4 +- .../IO/SpanWriterExtensions.cs | 7 +- src/FlatSharp.Runtime/SortedVectorHelpers.cs | 28 +- src/FlatSharp.sln | 7 - src/FlatSharp/Serialization/CSharpHelpers.cs | 5 +- .../RoslynSerializerGenerator.cs | 8 +- src/FlatSharp/TypeModel/EnumTypeModel.cs | 18 +- .../TypeModel/FlatSharpTypeModelProvider.cs | 10 +- src/FlatSharp/TypeModel/ItemMemberModel.cs | 33 +- src/FlatSharp/TypeModel/NullableTypeModel.cs | 26 +- src/FlatSharp/TypeModel/RuntimeTypeModel.cs | 15 +- src/FlatSharp/TypeModel/ScalarTypeModel.cs | 5 - src/FlatSharp/TypeModel/StringTypeModel.cs | 5 - src/FlatSharp/TypeModel/StructMemberModel.cs | 21 +- src/FlatSharp/TypeModel/StructTypeModel.cs | 49 +- src/FlatSharp/TypeModel/TableMemberModel.cs | 14 +- src/FlatSharp/TypeModel/TableTypeModel.cs | 152 +- src/FlatSharp/TypeModel/TypeModelContainer.cs | 28 +- src/FlatSharp/TypeModel/UnionTypeModel.cs | 38 +- .../TypeModel/ValueStructTypeModel.cs | 62 +- .../TypeModel/Vectors/BaseVectorTypeModel.cs | 73 +- .../TypeModel/Vectors/ListVectorTypeModel.cs | 4 +- .../Vectors/MemoryVectorTypeModel.cs | 4 +- .../UnityNativeArrayVectorTypeModel.cs | 8 +- .../BaseVectorOfUnionTypeModel.cs | 20 - src/Tests/CompileTests/CSharp8/CSharp8.csproj | 1 + .../FlatSharpCompilerTests.csproj | 1 - .../InvalidAttributeTests.cs | 157 ++ .../FlatSharpCompilerTests/SortedVectors.cs | 59 + .../FlatSharpCompilerTests/UnionTests.cs | 22 + .../ValueStructTests.cs | 36 + .../WriteThroughTests.cs | 31 + .../GeneratedSerializerWrapperTests.cs | 44 + .../ClassLib/SpanComparerTests.cs | 15 + .../FlatSharpEndToEndTests.csproj | 5 +- .../Grpc/EchoServiceTestCases.cs | 11 +- src/Tests/FlatSharpEndToEndTests/Helpers.cs | 21 + .../ValueStructs/ValueStructTests.cs | 109 +- .../ValueStructs/ValueStructs.fbs | 17 + .../Vectors/Sorted/SortedVectorTests.cs | 99 +- .../Vectors/Sorted/SortedVectors.fbs | 28 +- .../Vectors/Standard/StandardVectorTests.cs | 66 +- .../Vectors/Standard/StandardVectors.fbs | 2 + .../FlatSharpPoolableEndToEndTests.csproj | 4 +- .../FlatSharpTests/ClassLib/TypeModelTests.cs | 1540 ----------------- .../FlatSharpTests/FlatSharpTests.csproj | 38 - src/Tests/FlatSharpTests/GlobalUsings.cs | 31 - src/Tests/FlatSharpTests/ModuleInitializer.cs | 42 - .../SerializationTests/ScalarVectorTests.cs | 248 --- .../TableSerializationTests.cs | 149 -- .../SerializationTests/UnionTests.cs | 198 --- .../SerializationTests/ValueStructTests.cs | 871 ---------- .../VectorDeserializationTests.cs | 650 ------- .../VectorSerializationTests.cs | 721 -------- .../SerializationTests/WriteThroughTests.cs | 493 ------ src/Tests/FlatSharpTests/Tests.bfbs | Bin 4632 -> 0 bytes src/Tests/FlatSharpTests/Tests.fbs | 151 -- .../FlatSharpTests/Util/ContextHelpers.cs | 54 - .../FlatSharpTests/Util/ExtensionMethods.cs | 34 - .../FlatSharpTests/Util/Utf8StringComparer.cs | 25 - src/Tests/FlatSharpTests/regenerateOracle.cmd | 4 - .../FlatSharpTests/stryker-flatsharp.cmd | 1 - src/Tests/FlatSharpTests/stryker-runtime.cmd | 1 - src/Tests/unittest.props | 4 + 71 files changed, 980 insertions(+), 5685 deletions(-) create mode 100644 src/Tests/FlatSharpEndToEndTests/ClassLib/GeneratedSerializerWrapperTests.cs delete mode 100644 src/Tests/FlatSharpTests/ClassLib/TypeModelTests.cs delete mode 100644 src/Tests/FlatSharpTests/FlatSharpTests.csproj delete mode 100644 src/Tests/FlatSharpTests/GlobalUsings.cs delete mode 100644 src/Tests/FlatSharpTests/ModuleInitializer.cs delete mode 100644 src/Tests/FlatSharpTests/SerializationTests/ScalarVectorTests.cs delete mode 100644 src/Tests/FlatSharpTests/SerializationTests/TableSerializationTests.cs delete mode 100644 src/Tests/FlatSharpTests/SerializationTests/UnionTests.cs delete mode 100644 src/Tests/FlatSharpTests/SerializationTests/ValueStructTests.cs delete mode 100644 src/Tests/FlatSharpTests/SerializationTests/VectorDeserializationTests.cs delete mode 100644 src/Tests/FlatSharpTests/SerializationTests/VectorSerializationTests.cs delete mode 100644 src/Tests/FlatSharpTests/SerializationTests/WriteThroughTests.cs delete mode 100644 src/Tests/FlatSharpTests/Tests.bfbs delete mode 100644 src/Tests/FlatSharpTests/Tests.fbs delete mode 100644 src/Tests/FlatSharpTests/Util/ContextHelpers.cs delete mode 100644 src/Tests/FlatSharpTests/Util/ExtensionMethods.cs delete mode 100644 src/Tests/FlatSharpTests/Util/Utf8StringComparer.cs delete mode 100644 src/Tests/FlatSharpTests/regenerateOracle.cmd delete mode 100644 src/Tests/FlatSharpTests/stryker-flatsharp.cmd delete mode 100644 src/Tests/FlatSharpTests/stryker-runtime.cmd diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 95a5f0be..9dc1726b 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -95,10 +95,6 @@ jobs: --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: Legacy Tests - working-directory: src - run: dotnet test Tests/FlatSharpTests -c Debug -p:SignAssembly=false --collect:"XPlat Code Coverage" --settings Tests/coverlet.runsettings -f net7.0 - - name: E2E Tests working-directory: src run: dotnet test Tests/FlatSharpEndToEndTests -c Debug -p:SignAssembly=false --collect:"XPlat Code Coverage" --settings Tests/coverlet.runsettings -f net7.0 diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 75b74624..79cc2041 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -72,11 +72,6 @@ jobs: working-directory: src/Tests/FlatSharpCompilerTests run: dotnet test -c Release /p:SignAssembly=true --verbosity normal - - if: runner.os == 'Windows' - name: Legacy Test - working-directory: src/Tests/FlatSharpTests - run: dotnet test -c Release /p:SignAssembly=true --verbosity normal - - if: runner.os == 'Windows' name: Upload Packages uses: actions/upload-artifact@v2 diff --git a/src/FlatSharp.Compiler/FlatSharp.Compiler.csproj b/src/FlatSharp.Compiler/FlatSharp.Compiler.csproj index bdf00684..62f0f469 100644 --- a/src/FlatSharp.Compiler/FlatSharp.Compiler.csproj +++ b/src/FlatSharp.Compiler/FlatSharp.Compiler.csproj @@ -10,6 +10,7 @@ true $(NuspecProperties);OutDir=$(MSBuildThisFileDirectory)\bin\$(Configuration) annotations + true $(NoWarn);CS3021;3021;NU5127 @@ -53,6 +54,7 @@ + diff --git a/src/FlatSharp.Compiler/FlatSharpCompiler.cs b/src/FlatSharp.Compiler/FlatSharpCompiler.cs index 09edbd36..acfb644c 100644 --- a/src/FlatSharp.Compiler/FlatSharpCompiler.cs +++ b/src/FlatSharp.Compiler/FlatSharpCompiler.cs @@ -594,7 +594,7 @@ private static string GetFlatcPath() Options = localOptions, InputHash = inputHash, PreviousAssembly = assembly, - TypeModelContainer = TypeModelContainer.CreateDefault().WithUnitySupport(localOptions.UnityAssemblyPath is not null), + TypeModelContainer = TypeModelContainer.CreateDefault().WithUnitySupport(true), }); return true; @@ -679,6 +679,10 @@ private static Assembly[] BuildAdditionalReferences(CompilerOptions options, IEn { references.Add(Assembly.LoadFrom(options.UnityAssemblyPath)); } + else + { + references.Add(typeof(Unity.Collections.NativeArray<>).Assembly); + } return references.ToArray(); } diff --git a/src/FlatSharp.Compiler/SchemaModel/BaseReferenceTypeSchemaModel.cs b/src/FlatSharp.Compiler/SchemaModel/BaseReferenceTypeSchemaModel.cs index b0b6e24f..9dcc72f8 100644 --- a/src/FlatSharp.Compiler/SchemaModel/BaseReferenceTypeSchemaModel.cs +++ b/src/FlatSharp.Compiler/SchemaModel/BaseReferenceTypeSchemaModel.cs @@ -195,7 +195,7 @@ protected virtual void EmitStaticConstructor(CodeWriter writer, CompileContext c using (writer.WithBlock()) { var keyProperty = this.properties.Values.SingleOrDefault(p => p.Field.Key); - if (keyProperty is not null) + if (keyProperty is not null && context.CompilePass == CodeWritingPass.LastPass) { writer.AppendLine($"global::FlatSharp.SortedVectorHelpers.RegisterKeyLookup<{this.Name}, {keyProperty.Field.Type.ResolveTypeOrElementTypeName(this.Schema, keyProperty.Attributes)}>(x => x.{keyProperty.FieldName}, {keyProperty.Index});"); } diff --git a/src/FlatSharp.Compiler/SchemaModel/ValueUnionSchemaModel.cs b/src/FlatSharp.Compiler/SchemaModel/ValueUnionSchemaModel.cs index ebdb5db3..ead64604 100644 --- a/src/FlatSharp.Compiler/SchemaModel/ValueUnionSchemaModel.cs +++ b/src/FlatSharp.Compiler/SchemaModel/ValueUnionSchemaModel.cs @@ -71,6 +71,7 @@ protected override void OnWriteCode(CodeWriter writer, CompileContext context) bool generateUnsafeItemsOriginal = context.CompilePass >= CodeWritingPass.LastPass && this.Attributes.UnsafeUnion == true; bool generateUnsafeItems = generateUnsafeItemsOriginal; + HashSet seenTypes = new(); List<(string resolvedType, EnumVal value, int? size)> innerTypes = new List<(string, EnumVal, int?)>(); foreach (var inner in this.union.Values.Select(x => x.Value)) { @@ -85,6 +86,11 @@ protected override void OnWriteCode(CodeWriter writer, CompileContext context) long discriminator = inner.Value; string typeName = inner.UnionType.ResolveTypeOrElementTypeName(this.Schema, this.Attributes); + if (!seenTypes.Add(typeName)) + { + ErrorContext.Current.RegisterError($"FlatSharp unions may not contain duplicate types. Union = {this.FullName}"); + continue; + } int? size = null; Type? type = context.PreviousAssembly?.GetType(typeName); diff --git a/src/FlatSharp.Runtime/FlatSharpInternal.cs b/src/FlatSharp.Runtime/FlatSharpInternal.cs index 0a026c9a..c3c31a0f 100644 --- a/src/FlatSharp.Runtime/FlatSharpInternal.cs +++ b/src/FlatSharp.Runtime/FlatSharpInternal.cs @@ -71,6 +71,19 @@ public static void AssertLittleEndian() throw new FlatSharpInternalException(message); } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void AssertWellAligned(int alignment) + where TElement : unmanaged + { + var size = Unsafe.SizeOf(); + + // Inlining this method should allow this check to be elided as the alignment is a constant from the callsite. + if (size % alignment != 0) + { + throw new InvalidOperationException($"Type '{typeof(TElement).FullName}' does not support Unsafe Span operations because the size ({size}) is not a multiple of the alignment ({alignment})."); + } + } } [ExcludeFromCodeCoverage] diff --git a/src/FlatSharp.Runtime/GeneratedSerializerWrapper.cs b/src/FlatSharp.Runtime/GeneratedSerializerWrapper.cs index 14380c4f..4c5e0ce1 100644 --- a/src/FlatSharp.Runtime/GeneratedSerializerWrapper.cs +++ b/src/FlatSharp.Runtime/GeneratedSerializerWrapper.cs @@ -57,8 +57,6 @@ private GeneratedSerializerWrapper(GeneratedSerializerWrapper template) public Type RootType => typeof(T); - public string? CSharp => this.lazyCSharp.Value; - public FlatBufferDeserializationOption DeserializationOption => this.option; public int GetMaxSize(T item) @@ -223,11 +221,13 @@ int ISerializer.Write(TSpanWriter writer, Span destination, o }; } + [ExcludeFromCodeCoverage] public ISerializer WithSettings(Action settingsCallback) { return this.WithSettingsCore(settingsCallback); } + [ExcludeFromCodeCoverage] ISerializer ISerializer.WithSettings(Action settingsCallback) { return this.WithSettingsCore(settingsCallback); diff --git a/src/FlatSharp.Runtime/IO/SpanWriterExtensions.cs b/src/FlatSharp.Runtime/IO/SpanWriterExtensions.cs index 438aa496..2142f4a1 100644 --- a/src/FlatSharp.Runtime/IO/SpanWriterExtensions.cs +++ b/src/FlatSharp.Runtime/IO/SpanWriterExtensions.cs @@ -50,15 +50,10 @@ public static class SpanWriterExtensions { // Since we are copying bytes here, only LE is supported. FlatSharpInternal.AssertLittleEndian(); + FlatSharpInternal.AssertWellAligned(alignment); var size = Unsafe.SizeOf(); - // Inlining this method should allow this check to be elided as the alignment is a constant from the callsite. - if (size % alignment != 0) - { - throw new InvalidOperationException($"Type '{typeof(TElement).FullName}' does not support Unsafe Span serialization because the size ({size}) is not a multiple of the alignment ({alignment})."); - } - int numberOfItems = buffer.Length; int vectorStartOffset = ctx.AllocateVector( itemAlignment: alignment, diff --git a/src/FlatSharp.Runtime/SortedVectorHelpers.cs b/src/FlatSharp.Runtime/SortedVectorHelpers.cs index 50e21658..0844b197 100644 --- a/src/FlatSharp.Runtime/SortedVectorHelpers.cs +++ b/src/FlatSharp.Runtime/SortedVectorHelpers.cs @@ -58,8 +58,8 @@ public static class SortedVectorHelpers { using SimpleStringComparer cmp = new SimpleStringComparer(str); - return BinarySearchByFlatBufferKey, TTable, string?, SimpleStringComparer>( - new ListIndexable(sortedVector), + return BinarySearchByFlatBufferKey, TTable, string, SimpleStringComparer>( + new ListIndexable(sortedVector), sortedVector, cmp); } @@ -86,8 +86,8 @@ public static class SortedVectorHelpers { using SimpleStringComparer cmp = new SimpleStringComparer(str); - return BinarySearchByFlatBufferKey, TTable, string?, SimpleStringComparer>( - new ReadOnlyListIndexable(sortedVector), + return BinarySearchByFlatBufferKey, TTable, string, SimpleStringComparer>( + new ReadOnlyListIndexable(sortedVector), sortedVector, cmp); } @@ -235,6 +235,7 @@ private static void EnsureInitialized() } } + [ExcludeFromCodeCoverage] [MethodImpl(MethodImplOptions.NoInlining)] [DoesNotReturn] private static void ThrowNotInitialized() @@ -319,8 +320,14 @@ public ReadOnlyMemory KeyAt(int index) // Follow soffset to start of vtable. int vtableStart = offset - buffer.ReadInt(offset); - // Offset within the table. - int tableOffset = buffer.ReadUShort(vtableStart + 4 + (2 * this.keyIndex)); + ushort vtableLength = buffer.ReadUShort(vtableStart); + int tableOffset = 0; + int keyFieldOffset = 4 + (2 * this.keyIndex); + + if (keyFieldOffset + sizeof(ushort) <= vtableLength) + { + tableOffset = buffer.ReadUShort(vtableStart + keyFieldOffset); + } if (tableOffset == 0) { @@ -371,7 +378,7 @@ public void Dispose() } } - private struct SimpleStringComparer : ISimpleComparer, ISimpleComparer> + private struct SimpleStringComparer : ISimpleComparer, ISimpleComparer> { private readonly byte[] pooledArray; private readonly int length; @@ -385,12 +392,9 @@ public SimpleStringComparer(string right) this.length = enc.GetBytes(right, 0, right.Length, this.pooledArray, 0); } - public int CompareTo(string? left) + public int CompareTo(string left) { - if (left is null) - { - throw new InvalidOperationException("Sorted FlatBuffer vectors may not have null-valued keys."); - } + FlatSharpInternal.Assert(left is not null, "Sorted FlatBuffer vectors may not have null-valued keys."); var enc = SerializationHelpers.Encoding; int comp; diff --git a/src/FlatSharp.sln b/src/FlatSharp.sln index efb2d133..ff404d56 100644 --- a/src/FlatSharp.sln +++ b/src/FlatSharp.sln @@ -17,8 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmark", "Benchmarks\Ben EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExperimentalBenchmark", "Benchmarks\ExperimentalBenchmark\ExperimentalBenchmark.csproj", "{BD1950AE-7A04-4677-AD81-35F512D45112}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharpTests", "Tests\FlatSharpTests\FlatSharpTests.csproj", "{2D7F2684-3C86-4D97-8E9B-824D5F6E8FB8}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlatSharpCompilerTests", "Tests\FlatSharpCompilerTests\FlatSharpCompilerTests.csproj", "{A72F5D45-6AAF-40F7-AB73-1F4E33CE9A9D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.FlatBuffers", "Google.FlatBuffers\Google.FlatBuffers.csproj", "{D0C5D64D-68B1-467A-A097-A49B17A637E8}" @@ -61,10 +59,6 @@ Global {BD1950AE-7A04-4677-AD81-35F512D45112}.Debug|Any CPU.Build.0 = Debug|Any CPU {BD1950AE-7A04-4677-AD81-35F512D45112}.Release|Any CPU.ActiveCfg = Release|Any CPU {BD1950AE-7A04-4677-AD81-35F512D45112}.Release|Any CPU.Build.0 = Release|Any CPU - {2D7F2684-3C86-4D97-8E9B-824D5F6E8FB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2D7F2684-3C86-4D97-8E9B-824D5F6E8FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2D7F2684-3C86-4D97-8E9B-824D5F6E8FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2D7F2684-3C86-4D97-8E9B-824D5F6E8FB8}.Release|Any CPU.Build.0 = Release|Any CPU {A72F5D45-6AAF-40F7-AB73-1F4E33CE9A9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A72F5D45-6AAF-40F7-AB73-1F4E33CE9A9D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A72F5D45-6AAF-40F7-AB73-1F4E33CE9A9D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -100,7 +94,6 @@ Global GlobalSection(NestedProjects) = preSolution {09AC4B32-5EAF-4FB0-B2E3-925EF4F5DDAD} = {83478353-8C5A-41C2-84C2-F79488B43CB0} {BD1950AE-7A04-4677-AD81-35F512D45112} = {83478353-8C5A-41C2-84C2-F79488B43CB0} - {2D7F2684-3C86-4D97-8E9B-824D5F6E8FB8} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} {A72F5D45-6AAF-40F7-AB73-1F4E33CE9A9D} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} {E6B5266D-A983-4A9C-BA2D-85AE1B7CEF07} = {D1E90BAE-FC51-44DB-8215-1D9BB6059886} {F9C48012-AB11-4541-911C-BAF2B0B1158B} = {83478353-8C5A-41C2-84C2-F79488B43CB0} diff --git a/src/FlatSharp/Serialization/CSharpHelpers.cs b/src/FlatSharp/Serialization/CSharpHelpers.cs index e786331a..abe5f0fb 100644 --- a/src/FlatSharp/Serialization/CSharpHelpers.cs +++ b/src/FlatSharp/Serialization/CSharpHelpers.cs @@ -34,6 +34,7 @@ internal static string GetGlobalCompilableTypeName(this Type t) internal static string GetCompilableTypeName(this Type t) { FlatSharpInternal.Assert(!string.IsNullOrEmpty(t.FullName), $"{t} has null/empty full name."); + FlatSharpInternal.Assert(!t.IsArray, "Not expecting an array"); string name; if (t.IsGenericType) @@ -46,10 +47,6 @@ internal static string GetCompilableTypeName(this Type t) name = $"{t.FullName.Split('`')[0]}<{string.Join(", ", parameters)}>"; } - else if (t.IsArray) - { - name = $"{GetCompilableTypeName(t.GetElementType()!)}[]"; - } else { name = t.FullName; diff --git a/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs b/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs index 90236e87..48fa3a68 100644 --- a/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs +++ b/src/FlatSharp/Serialization/RoslynSerializerGenerator.cs @@ -155,10 +155,10 @@ public RoslynSerializerGenerator(FlatBufferSerializerOptions options, TypeModelC internal (string text, string serializerTypeName) GenerateCSharpRecursive() { ITypeModel rootModel = this.typeModelContainer.CreateTypeModel(typeof(TRoot)); - if (rootModel.SchemaType != FlatBufferSchemaType.Table) - { - throw new InvalidFlatBufferDefinitionException($"Can only compile [FlatBufferTable] elements as root types. Type '{typeof(TRoot).GetCompilableTypeName()}' is a {rootModel.SchemaType}."); - } + + FlatSharpInternal.Assert( + rootModel.SchemaType == FlatBufferSchemaType.Table, + $"Can only compile [FlatBufferTable] elements as root types. Type '{typeof(TRoot).GetCompilableTypeName()}' is a {rootModel.SchemaType}."); IMethodNameResolver resolver = new DefaultMethodNameResolver(); diff --git a/src/FlatSharp/TypeModel/EnumTypeModel.cs b/src/FlatSharp/TypeModel/EnumTypeModel.cs index 4b99bbf8..56e55866 100644 --- a/src/FlatSharp/TypeModel/EnumTypeModel.cs +++ b/src/FlatSharp/TypeModel/EnumTypeModel.cs @@ -41,15 +41,13 @@ public override void Initialize() this.underlyingTypeModel = this.typeModelContainer.CreateTypeModel(underlyingType); var attribute = enumType.GetCustomAttribute(); - if (attribute == null) - { - throw new InvalidFlatBufferDefinitionException($"Enum '{CSharpHelpers.GetCompilableTypeName(enumType)}' is not tagged with a [FlatBufferEnum] attribute."); - } + FlatSharpInternal.Assert( + attribute is not null, + $"Enum '{CSharpHelpers.GetCompilableTypeName(enumType)}' is not tagged with a [FlatBufferEnum] attribute."); - if (attribute.DeclaredUnderlyingType != Enum.GetUnderlyingType(enumType)) - { - throw new InvalidFlatBufferDefinitionException($"Enum '{CSharpHelpers.GetCompilableTypeName(enumType)}' declared underlying type '{attribute.DeclaredUnderlyingType}', but was actually '{CSharpHelpers.GetCompilableTypeName(Enum.GetUnderlyingType(enumType))}'"); - } + FlatSharpInternal.Assert( + attribute.DeclaredUnderlyingType == Enum.GetUnderlyingType(enumType), + $"Enum '{CSharpHelpers.GetCompilableTypeName(enumType)}' declared underlying type '{attribute.DeclaredUnderlyingType}', but was actually '{CSharpHelpers.GetCompilableTypeName(Enum.GetUnderlyingType(enumType))}'"); } public override bool IsParsingInvariant => true; @@ -66,10 +64,6 @@ public override void Initialize() public override bool IsValidVectorMember => true; - public override bool IsValidUnionMember => false; - - public override bool IsValidSortedVectorKey => false; - public override bool SerializesInline => true; public override bool SerializeMethodRequiresContext => this.underlyingTypeModel.SerializeMethodRequiresContext; diff --git a/src/FlatSharp/TypeModel/FlatSharpTypeModelProvider.cs b/src/FlatSharp/TypeModel/FlatSharpTypeModelProvider.cs index 9f713394..809086cd 100644 --- a/src/FlatSharp/TypeModel/FlatSharpTypeModelProvider.cs +++ b/src/FlatSharp/TypeModel/FlatSharpTypeModelProvider.cs @@ -86,11 +86,11 @@ public bool TryCreateTypeModel(TypeModelContainer container, Type type, [NotNull var tableAttribute = type.GetCustomAttribute(); var structAttribute = type.GetCustomAttribute(); - if (tableAttribute is not null && structAttribute is not null) - { - throw new InvalidFlatBufferDefinitionException($"Type '{CSharpHelpers.GetCompilableTypeName(type)}' is declared as both [FlatBufferTable] and [FlatBufferStruct]."); - } - else if (tableAttribute is not null) + FlatSharpInternal.Assert( + tableAttribute is null || structAttribute is null, + $"Type '{CSharpHelpers.GetCompilableTypeName(type)}' is declared as both [FlatBufferTable] and [FlatBufferStruct]."); + + if (tableAttribute is not null) { typeModel = new TableTypeModel(type, container); return true; diff --git a/src/FlatSharp/TypeModel/ItemMemberModel.cs b/src/FlatSharp/TypeModel/ItemMemberModel.cs index 2dd766d6..21545ffc 100644 --- a/src/FlatSharp/TypeModel/ItemMemberModel.cs +++ b/src/FlatSharp/TypeModel/ItemMemberModel.cs @@ -48,10 +48,7 @@ public enum SetMethodKind this.IsRequired = attribute.Required; this.Attribute = attribute; - if (getMethod is null) - { - throw new InvalidFlatBufferDefinitionException($"Property {this.DeclaringType} on did not declare a getter."); - } + FlatSharpInternal.Assert(getMethod is not null, $"Property {this.DeclaringType} on did not declare a getter."); if (setMethod is not null) { @@ -66,22 +63,19 @@ public enum SetMethodKind internal virtual void Validate() { MethodInfo getMethod = this.PropertyInfo.GetMethod!; // validated in .ctor. - var setMethod = this.PropertyInfo.SetMethod; + bool validAccessor = getMethod.IsPublic || !string.IsNullOrEmpty(this.CustomAccessor); - if (!getMethod.IsPublic && string.IsNullOrEmpty(this.CustomAccessor)) - { - throw new InvalidFlatBufferDefinitionException($"Property {this.DeclaringType} must declare a public getter."); - } + FlatSharpInternal.Assert( + validAccessor, + $"Property {this.DeclaringType} must declare a public getter."); - if (!ValidateVirtualPropertyMethod(getMethod, false)) - { - throw new InvalidFlatBufferDefinitionException($"Property {this.DeclaringType} did not declare a public/protected virtual getter."); - } + FlatSharpInternal.Assert( + ValidateVirtualPropertyMethod(getMethod, false), + $"Property {this.DeclaringType} did not declare a public/protected virtual getter."); - if (!ValidateVirtualPropertyMethod(setMethod, true)) - { - throw new InvalidFlatBufferDefinitionException($"Property {this.DeclaringType} declared a set method, but it was not public/protected and virtual."); - } + FlatSharpInternal.Assert( + ValidateVirtualPropertyMethod(this.PropertyInfo.SetMethod, true), + $"Property {this.DeclaringType} declared a set method, but it was not public/protected and virtual."); } protected string DeclaringType @@ -124,10 +118,7 @@ private static bool ValidateVirtualPropertyMethod(MethodInfo? method, bool allow return allowNull; } - if (!CanBeOverridden(method)) - { - return false; - } + FlatSharpInternal.Assert(CanBeOverridden(method), "virtual method expected"); return method.IsPublic || method.IsFamilyOrAssembly || method.IsFamily; } diff --git a/src/FlatSharp/TypeModel/NullableTypeModel.cs b/src/FlatSharp/TypeModel/NullableTypeModel.cs index 0983f3d9..2611f7b4 100644 --- a/src/FlatSharp/TypeModel/NullableTypeModel.cs +++ b/src/FlatSharp/TypeModel/NullableTypeModel.cs @@ -52,31 +52,11 @@ internal NullableTypeModel(TypeModelContainer container, Type type) : base(type, /// public override bool IsFixedSize => this.underlyingTypeModel.IsFixedSize; - /// - /// Nullables can't be part of Structs. - /// - public override bool IsValidStructMember => false; - /// /// Nullables can be part of Tables. /// public override bool IsValidTableMember => this.underlyingTypeModel.IsValidTableMember; - /// - /// Nullables can't be part of Unions. - /// - public override bool IsValidUnionMember => false; - - /// - /// Optionals can't be part of unions. - /// - public override bool IsValidVectorMember => false; - - /// - /// Optionals can't be keys. - /// - public override bool IsValidSortedVectorKey => false; - /// /// Defer to underlying type for serializing. /// @@ -92,7 +72,11 @@ public override bool SerializeMethodRequiresContext /// /// Defer to underlying type model about whether we need this. /// - public override TableFieldContextRequirements TableFieldContextRequirements => this.underlyingTypeModel.TableFieldContextRequirements; + public override TableFieldContextRequirements TableFieldContextRequirements + => this.underlyingTypeModel.TableFieldContextRequirements; + + public override bool TryGetUnderlyingVectorType([NotNullWhen(true)] out ITypeModel? typeModel) + => this.underlyingTypeModel.TryGetUnderlyingVectorType(out typeModel); public override IEnumerable Children => new[] { this.underlyingTypeModel }; diff --git a/src/FlatSharp/TypeModel/RuntimeTypeModel.cs b/src/FlatSharp/TypeModel/RuntimeTypeModel.cs index 21c7f1a4..a28973a0 100644 --- a/src/FlatSharp/TypeModel/RuntimeTypeModel.cs +++ b/src/FlatSharp/TypeModel/RuntimeTypeModel.cs @@ -67,27 +67,32 @@ public virtual void Validate() /// /// Indicates if this type model can be part of a struct. /// - public abstract bool IsValidStructMember { get; } + [ExcludeFromCodeCoverage] + public virtual bool IsValidStructMember => false; /// /// Indicates if this type model can be part of a table. /// - public abstract bool IsValidTableMember { get; } + [ExcludeFromCodeCoverage] + public virtual bool IsValidTableMember => false; /// /// Indicates if this type model can be part of a vector. /// - public abstract bool IsValidVectorMember { get; } + [ExcludeFromCodeCoverage] + public virtual bool IsValidVectorMember => false; /// /// Indicates if this type model can be part of a union. /// - public abstract bool IsValidUnionMember { get; } + [ExcludeFromCodeCoverage] + public virtual bool IsValidUnionMember => false; /// /// Indicates if this type model can be a sorted vector key. /// - public abstract bool IsValidSortedVectorKey { get; } + [ExcludeFromCodeCoverage] + public virtual bool IsValidSortedVectorKey => false; /// /// When true, indicates that this type model's serialize method writes inline, rather than by offset. diff --git a/src/FlatSharp/TypeModel/ScalarTypeModel.cs b/src/FlatSharp/TypeModel/ScalarTypeModel.cs index 6824308e..b48e9d4e 100644 --- a/src/FlatSharp/TypeModel/ScalarTypeModel.cs +++ b/src/FlatSharp/TypeModel/ScalarTypeModel.cs @@ -63,11 +63,6 @@ public abstract class ScalarTypeModel : RuntimeTypeModel /// public override bool IsValidTableMember => true; - /// - /// Scalars can't be part of Unions. - /// - public override bool IsValidUnionMember => false; - /// /// Scalars can be part of Vectors. /// diff --git a/src/FlatSharp/TypeModel/StringTypeModel.cs b/src/FlatSharp/TypeModel/StringTypeModel.cs index cb8b7b4a..4a0e1d03 100644 --- a/src/FlatSharp/TypeModel/StringTypeModel.cs +++ b/src/FlatSharp/TypeModel/StringTypeModel.cs @@ -48,11 +48,6 @@ internal StringTypeModel(TypeModelContainer container) : base(typeof(string), co /// public override bool IsFixedSize => false; - /// - /// Strings can't be part of structs. - /// - public override bool IsValidStructMember => false; - /// /// Strings can be part of tables. /// diff --git a/src/FlatSharp/TypeModel/StructMemberModel.cs b/src/FlatSharp/TypeModel/StructMemberModel.cs index 79b8f0d3..6c5e6705 100644 --- a/src/FlatSharp/TypeModel/StructMemberModel.cs +++ b/src/FlatSharp/TypeModel/StructMemberModel.cs @@ -38,20 +38,17 @@ internal override void Validate() { base.Validate(); - if (this.ItemTypeModel.SerializeMethodRequiresContext) - { - throw new InvalidFlatBufferDefinitionException($"The type model for struct member '{this.FriendlyName}' requires a serialization context, but Structs do not have one."); - } + FlatSharpInternal.Assert( + !this.ItemTypeModel.SerializeMethodRequiresContext, + $"The type model for struct member '{this.FriendlyName}' requires a serialization context, but Structs do not have one."); - if (this.Attribute.Required) - { - throw new InvalidFlatBufferDefinitionException($"Struct member '{this.FriendlyName}' declared the Required attribute. Required is not valid inside structs."); - } + FlatSharpInternal.Assert( + !this.Attribute.Required, + $"Struct member '{this.FriendlyName}' declared the Required attribute. Required is not valid inside structs."); - if (this.Attribute.SharedString) - { - throw new InvalidFlatBufferDefinitionException($"Struct member '{this.FriendlyName}' declared the SharedString attribute. SharedString is not valid inside structs."); - } + FlatSharpInternal.Assert( + !this.Attribute.SharedString, + $"Struct member '{this.FriendlyName}' declared the SharedString attribute. SharedString is not valid inside structs."); if (this.Attribute.Key) { diff --git a/src/FlatSharp/TypeModel/StructTypeModel.cs b/src/FlatSharp/TypeModel/StructTypeModel.cs index 4cfbce26..1fde3d9d 100644 --- a/src/FlatSharp/TypeModel/StructTypeModel.cs +++ b/src/FlatSharp/TypeModel/StructTypeModel.cs @@ -73,11 +73,6 @@ internal StructTypeModel(Type clrType, TypeModelContainer container) : base(clrT /// public override bool IsValidVectorMember => true; - /// - /// Structs can't be keys of sorted vectors. - /// - public override bool IsValidSortedVectorKey => false; - /// /// We only need context if one of our children needs it. /// @@ -210,10 +205,9 @@ public override void Validate() var properties = this.GetProperties(); - if (!properties.Any()) - { - throw new InvalidFlatBufferDefinitionException($"Can't create struct type model from type {this.GetCompilableTypeName()} because it does not have any non-static [FlatBufferItem] properties. Structs cannot be empty."); - } + FlatSharpInternal.Assert( + properties.Any(), + $"Can't create struct type model from type {this.GetCompilableTypeName()} because it does not have any non-static [FlatBufferItem] properties. Structs cannot be empty."); ushort expectedIndex = 0; @@ -222,26 +216,22 @@ public override void Validate() var propertyAttribute = item.Attribute; var property = item.Property; - if (propertyAttribute.Deprecated) - { - throw new InvalidFlatBufferDefinitionException($"FlatBuffer struct {this.GetCompilableTypeName()} may not have deprecated properties"); - } + FlatSharpInternal.Assert( + !propertyAttribute.Deprecated, + $"FlatBuffer struct {this.GetCompilableTypeName()} may not have deprecated properties"); - if (propertyAttribute.ForceWrite) - { - throw new InvalidFlatBufferDefinitionException($"FlatBuffer struct {this.GetCompilableTypeName()} may not have properties with the ForceWrite option set to true."); - } + FlatSharpInternal.Assert( + !propertyAttribute.ForceWrite, + $"FlatBuffer struct {this.GetCompilableTypeName()} may not have properties with the ForceWrite option set to true."); ushort index = propertyAttribute.Index; - if (index != expectedIndex) - { - throw new InvalidFlatBufferDefinitionException($"FlatBuffer struct {this.GetCompilableTypeName()} does not declare an item with index {expectedIndex}. Structs must have sequenential indexes starting at 0."); - } + FlatSharpInternal.Assert( + index == expectedIndex, + $"FlatBuffer struct {this.GetCompilableTypeName()} does not declare an item with index {expectedIndex}. Structs must have sequenential indexes starting at 0."); - if (propertyAttribute.DefaultValue is not null) - { - throw new InvalidFlatBufferDefinitionException($"FlatBuffer struct {this.GetCompilableTypeName()} declares default value on index {expectedIndex}. Structs may not have default values."); - } + FlatSharpInternal.Assert( + propertyAttribute.DefaultValue is null, + $"FlatBuffer struct {this.GetCompilableTypeName()} declares default value on index {expectedIndex}. Structs may not have default values."); expectedIndex++; ITypeModel propertyModel = this.typeModelContainer.CreateTypeModel(property.PropertyType); @@ -269,10 +259,11 @@ public override void Validate() { ITypeModel memberModel = member.ItemTypeModel; - if (!memberModel.IsValidStructMember || memberModel.PhysicalLayout.Length > 1) - { - throw new InvalidFlatBufferDefinitionException($"Struct '{this.GetCompilableTypeName()}' property {member.PropertyInfo.Name} (Index {member.Index}) with type {CSharpHelpers.GetCompilableTypeName(member.PropertyInfo.PropertyType)} cannot be part of a flatbuffer struct."); - } + bool validMember = memberModel.IsValidStructMember && memberModel.PhysicalLayout.Length == 1; + + FlatSharpInternal.Assert( + validMember, + $"Struct '{this.GetCompilableTypeName()}' property {member.PropertyInfo.Name} (Index {member.Index}) with type {CSharpHelpers.GetCompilableTypeName(member.PropertyInfo.PropertyType)} cannot be part of a flatbuffer struct."); member.Validate(); } diff --git a/src/FlatSharp/TypeModel/TableMemberModel.cs b/src/FlatSharp/TypeModel/TableMemberModel.cs index 5a754dcb..dab23cc1 100644 --- a/src/FlatSharp/TypeModel/TableMemberModel.cs +++ b/src/FlatSharp/TypeModel/TableMemberModel.cs @@ -66,10 +66,9 @@ internal override void Validate() { if (IsValueStruct(this.ItemTypeModel)) { - if (!this.IsRequired) - { - throw new InvalidFlatBufferDefinitionException($"Table property '{this.FriendlyName}' declared the WriteThrough attribute, but the field is not marked as required. WriteThrough fields must also be required."); - } + FlatSharpInternal.Assert( + this.IsRequired, + $"Table property '{this.FriendlyName}' declared the WriteThrough attribute, but the field is not marked as required. WriteThrough fields must also be required."); FlatSharpInternal.Assert( !this.ItemTypeModel.SerializeMethodRequiresContext, @@ -95,10 +94,9 @@ internal override void Validate() if (this.IsRequired) { - if (this.ItemTypeModel.SchemaType == FlatBufferSchemaType.Scalar) - { - throw new InvalidFlatBufferDefinitionException($"Table property '{this.FriendlyName}' declared the Required attribute. Required is only valid on non-scalar table fields."); - } + FlatSharpInternal.Assert( + this.ItemTypeModel.SchemaType != FlatBufferSchemaType.Scalar, + $"Table property '{this.FriendlyName}' declared the Required attribute. Required is only valid on non-scalar table fields."); FlatSharpInternal.Assert( this.DefaultValue is null, diff --git a/src/FlatSharp/TypeModel/TableTypeModel.cs b/src/FlatSharp/TypeModel/TableTypeModel.cs index 86b9d26e..afbaed0b 100644 --- a/src/FlatSharp/TypeModel/TableTypeModel.cs +++ b/src/FlatSharp/TypeModel/TableTypeModel.cs @@ -64,11 +64,6 @@ internal TableTypeModel(Type clrType, TypeModelContainer typeModelProvider) : ba /// public override bool IsFixedSize => false; - /// - /// Tables can't be part of structs. - /// - public override bool IsValidStructMember => false; - /// /// Tables can be part of tables. /// @@ -84,11 +79,6 @@ internal TableTypeModel(Type clrType, TypeModelContainer typeModelProvider) : ba /// public override bool IsValidVectorMember => true; - /// - /// Tables can't be keys of sorted vectors. - /// - public override bool IsValidSortedVectorKey => false; - /// /// Tables are written by reference. /// @@ -138,6 +128,8 @@ public override void Initialize() public override void Validate() { + base.Validate(); + { FlatBufferTableAttribute? attr = this.ClrType.GetCustomAttribute(); FlatSharpInternal.Assert(attr != null, "Table object missing attribute"); @@ -174,20 +166,18 @@ public override void Validate() if (property.Attribute.Key) { - if (this.KeyMember is not null) - { - throw new InvalidFlatBufferDefinitionException($"Table {this.GetCompilableTypeName()} has more than one [FlatBufferItemAttribute] with Key set to true."); - } + FlatSharpInternal.Assert( + this.KeyMember is null, + $"Table {this.GetCompilableTypeName()} has more than one [FlatBufferItemAttribute] with Key set to true."); this.KeyMember = model; } for (int i = 0; i < model.ItemTypeModel.PhysicalLayout.Length; ++i) { - if (!this.occupiedVtableSlots.Add(index + i)) - { - throw new InvalidFlatBufferDefinitionException($"FlatBuffer Table {this.GetCompilableTypeName()} already defines a property with index {index}. This may happen when unions are declared as these are double-wide members."); - } + FlatSharpInternal.Assert( + this.occupiedVtableSlots.Add(index + i), + $"FlatBuffer Table {this.GetCompilableTypeName()} already defines a property with index {index}. This may happen when unions are declared as these are double-wide members."); } this.memberTypes[index] = model; @@ -239,10 +229,9 @@ private void ValidateForceWrite(TableMemberModel model) { if (model.ForceWrite) { - if (!model.ItemTypeModel.ClassifyContextually(this.SchemaType).IsRequiredValue()) - { - throw new InvalidFlatBufferDefinitionException($"Property '{model.PropertyInfo.Name}' on table '{this.GetCompilableTypeName()}' declares the {nameof(FlatBufferItemAttribute.ForceWrite)} option, but the type is not supported for force write."); - } + FlatSharpInternal.Assert( + model.ItemTypeModel.ClassifyContextually(this.SchemaType).IsRequiredValue(), + $"Property '{model.PropertyInfo.Name}' on table '{this.GetCompilableTypeName()}' declares the {nameof(FlatBufferItemAttribute.ForceWrite)} option, but the type is not supported for force write."); } } @@ -255,10 +244,9 @@ private void ValidateKey(TableMemberModel memberModel) throw new InvalidFlatBufferDefinitionException($"Table {this.GetCompilableTypeName()} declares a key property on a type that that does not support being a key in a sorted vector."); } - if (!memberModel.ItemTypeModel.TryGetSpanComparerType(out _)) - { - throw new InvalidFlatBufferDefinitionException($"Table {this.GetCompilableTypeName()} declares a key property on a type whose type model does not supply a ISpanComparer type."); - } + FlatSharpInternal.Assert( + memberModel.ItemTypeModel.TryGetSpanComparerType(out _), + $"Table {this.GetCompilableTypeName()} declares a key property on a type whose type model does not supply a ISpanComparer type."); if (memberModel.IsDeprecated) { @@ -271,15 +259,13 @@ private static (ITypeModel itemModel, TableMemberModel keyMember, Type spanCompa { if (model.IsSortedVector) { - if (model.ItemTypeModel.SchemaType != FlatBufferSchemaType.Vector) - { - throw new InvalidFlatBufferDefinitionException($"Property '{model.PropertyInfo.Name}' declares the sortedVector option, but the underlying type was not a vector."); - } + FlatSharpInternal.Assert( + model.ItemTypeModel.SchemaType == FlatBufferSchemaType.Vector, + $"Property '{model.PropertyInfo.Name}' declares the sortedVector option, but the underlying type was not a vector."); - if (!model.ItemTypeModel.TryGetUnderlyingVectorType(out ITypeModel? memberTypeModel)) - { - throw new InvalidFlatBufferDefinitionException($"Property '{model.PropertyInfo.Name}' declares the sortedVector option, but the underlying type model did not report the underlying vector type."); - } + FlatSharpInternal.Assert( + model.ItemTypeModel.TryGetUnderlyingVectorType(out ITypeModel? memberTypeModel), + $"Property '{model.PropertyInfo.Name}' declares the sortedVector option, but the underlying type model did not report the underlying vector type."); if (memberTypeModel.SchemaType != FlatBufferSchemaType.Table) { @@ -291,15 +277,13 @@ private static (ITypeModel itemModel, TableMemberModel keyMember, Type spanCompa throw new InvalidFlatBufferDefinitionException($"Property '{model.PropertyInfo.Name}' declares a sorted vector, but the member does not have a key defined. Type = {model.ItemTypeModel.GetCompilableTypeName()}."); } - if (!member.ItemTypeModel.TryGetSpanComparerType(out var spanComparer)) - { - throw new InvalidFlatBufferDefinitionException($"Property '{model.PropertyInfo.Name}' declares a sorted vector, but the key does not have an implementation of ISpanComparer. Keys must be non-nullable scalars or strings. KeyType = {model.ItemTypeModel.GetCompilableTypeName()}"); - } + FlatSharpInternal.Assert( + member.ItemTypeModel.TryGetSpanComparerType(out var spanComparer), + $"Property '{model.PropertyInfo.Name}' declares a sorted vector, but the key does not have an implementation of ISpanComparer. Keys must be non-nullable scalars or strings. KeyType = {model.ItemTypeModel.GetCompilableTypeName()}"); - if (member.ItemTypeModel.PhysicalLayout.Length != 1) - { - throw new InvalidFlatBufferDefinitionException($"Property '{model.PropertyInfo.Name}' declares a sorted vector, but the sort key's vtable is not compatible with sorting. KeyType = {model.ItemTypeModel.GetCompilableTypeName()}"); - } + FlatSharpInternal.Assert( + member.ItemTypeModel.PhysicalLayout.Length == 1, + $"Property '{model.PropertyInfo.Name}' declares a sorted vector, but the sort key's vtable is not compatible with sorting. KeyType = {model.ItemTypeModel.GetCompilableTypeName()}"); return (memberTypeModel, member, spanComparer); } @@ -314,33 +298,23 @@ private static (ITypeModel itemModel, TableMemberModel keyMember, Type spanCompa .GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) .Where(m => m.Name == OnDeserializedMethodName); - if (methods.Count() > 1) - { - throw new InvalidFlatBufferDefinitionException($"Type '{typeModel.GetCompilableTypeName()}' provides more than one '{OnDeserializedMethodName}' method."); - } + FlatSharpInternal.Assert( + methods.Count() <= 1, + $"Type '{typeModel.GetCompilableTypeName()}' provides more than one '{OnDeserializedMethodName}' method."); - var method = methods.SingleOrDefault(); + MethodInfo? method = methods.SingleOrDefault(); if (method is null) { return null; } - string message = $"Type '{typeModel.GetCompilableTypeName()}' provides an unusable '{OnDeserializedMethodName}' method. '{OnDeserializedMethodName}' must be protected, have a return type of void, and accept a single parameter of type '{nameof(FlatBufferDeserializationContext)}'."; - if (!method.IsFamily || - method.ReturnType != typeof(void) || - method.GetParameters().Length != 1) - { - throw new InvalidFlatBufferDefinitionException(message); - } + bool invalidMethod = !method.IsFamily + || method.ReturnType != typeof(void) + || method.GetParameters().Length != 1; - var firstParameter = method.GetParameters()[0]; - if (firstParameter.IsOut || - firstParameter.IsOptional || - firstParameter.IsIn || - firstParameter.ParameterType != typeof(FlatBufferDeserializationContext)) - { - throw new InvalidFlatBufferDefinitionException(message); - } + FlatSharpInternal.Assert( + !invalidMethod, + $"Type '{typeModel.GetCompilableTypeName()}' provides an unusable '{OnDeserializedMethodName}' method. '{OnDeserializedMethodName}' must be protected, have a return type of void, and accept a single parameter of type '{nameof(FlatBufferDeserializationContext)}'."); return method; } @@ -349,30 +323,11 @@ internal static void EnsureClassCanBeInheritedByOutsideAssembly(Type type, out C { string typeName = CSharpHelpers.GetCompilableTypeName(type); - if (!type.IsClass) - { - throw new InvalidFlatBufferDefinitionException($"Can't create type model from type {typeName} because it is not a class."); - } - - if (type.IsSealed) - { - throw new InvalidFlatBufferDefinitionException($"Can't create type model from type {typeName} because it is sealed."); - } - - if (type.IsAbstract) - { - throw new InvalidFlatBufferDefinitionException($"Can't create type model from type {typeName} because it is abstract."); - } - - if (type.BaseType != typeof(object)) - { - throw new InvalidFlatBufferDefinitionException($"Can't create type model from type {typeName} its base class is not System.Object."); - } - - if (!type.IsPublic && !type.IsNestedPublic) - { - throw new InvalidFlatBufferDefinitionException($"Can't create type model from type {typeName} because it is not public."); - } + FlatSharpInternal.Assert(type.IsClass, $"Can't create type model from type {typeName} because it is not a class."); + FlatSharpInternal.Assert(!type.IsSealed, $"Can't create type model from type {typeName} because it is sealed."); + FlatSharpInternal.Assert(!type.IsAbstract, $"Can't create type model from type {typeName} because it is abstract."); + FlatSharpInternal.Assert(type.BaseType == typeof(object), $"Can't create type model from type {typeName} its base class is not System.Object."); + FlatSharpInternal.Assert(type.IsPublic || type.IsNestedPublic, $"Can't create type model from type {typeName} because it is not public."); var defaultCtor = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) @@ -392,18 +347,11 @@ internal static void EnsureClassCanBeInheritedByOutsideAssembly(Type type, out C FlatSharpInternal.Assert(IsVisible(specialCtor), "Special deserialize constructor should be visible."); defaultConstructor = specialCtor; } - else if (defaultCtor is not null) - { - if (!IsVisible(defaultCtor)) - { - throw new InvalidFlatBufferDefinitionException($"Default constructor for '{typeName}' is not visible to subclasses outside the assembly."); - } - - defaultConstructor = defaultCtor; - } else { - throw new InvalidFlatBufferDefinitionException($"Unable to find a usable constructor for '{typeName}'. The type must supply a default constructor or single parameter constructor accepting '{nameof(FlatBufferDeserializationContext)}' that is visible to subclasses outside the assembly."); + FlatSharpInternal.Assert(defaultCtor is not null, $"No default constructor found for '{typeName}'."); + FlatSharpInternal.Assert(IsVisible(defaultCtor), $"Default constructor for '{typeName}' is not visible to subclasses outside the assembly."); + defaultConstructor = defaultCtor; } } @@ -411,18 +359,14 @@ private static void ValidateFileIdentifier(string? fileIdentifier) { if (!string.IsNullOrEmpty(fileIdentifier)) { - if (fileIdentifier.Length != FileIdentifierSize) - { - throw new InvalidFlatBufferDefinitionException($"File identifier '{fileIdentifier}' is invalid. FileIdentifiers must be exactly {FileIdentifierSize} ASCII characters."); - } + FlatSharpInternal.Assert( + fileIdentifier.Length == FileIdentifierSize, + $"File identifier '{fileIdentifier}' is invalid. FileIdentifiers must be exactly {FileIdentifierSize} ASCII characters."); for (int i = 0; i < fileIdentifier.Length; ++i) { char c = fileIdentifier[i]; - if (c >= 128) - { - throw new InvalidFlatBufferDefinitionException($"File identifier '{fileIdentifier}' contains non-ASCII characters. Character '{c}' is invalid."); - } + FlatSharpInternal.Assert(c < 128, $"File identifier '{fileIdentifier}' contains non-ASCII characters. Character '{c}' is invalid."); } } } diff --git a/src/FlatSharp/TypeModel/TypeModelContainer.cs b/src/FlatSharp/TypeModel/TypeModelContainer.cs index 00f412a9..81eeb0c0 100644 --- a/src/FlatSharp/TypeModel/TypeModelContainer.cs +++ b/src/FlatSharp/TypeModel/TypeModelContainer.cs @@ -39,14 +39,6 @@ private TypeModelContainer() { } - /// - /// Creates an empty container with no types supported. No types will be supported unless explicitly registered. Have fun! - /// - public static TypeModelContainer CreateEmpty() - { - return new TypeModelContainer(); - } - /// /// Creates a FlatSharp type model container with default support. /// @@ -71,14 +63,17 @@ public static TypeModelContainer CreateDefault() /// public void RegisterProvider(ITypeModelProvider provider) { - if (provider is null) - { - throw new ArgumentNullException(nameof(provider)); - } - + FlatSharpInternal.Assert(provider is not null, "Provider can't be null"); this.providers.Add(provider); } + /// + /// Attempts to look up a type model for the given type, if it exists. + /// + public bool TryGetTypeModel( + Type type, + [NotNullWhen(true)] out ITypeModel? typeModel) => this.cache.TryGetValue(type, out typeModel); + /// /// Attempts to resolve a type model from the given type. /// @@ -267,10 +262,9 @@ private void ProcessValidationQueue() /// public ITypeModel CreateTypeModel(Type type) { - if (!this.TryCreateTypeModel(type, out var typeModel)) - { - throw new InvalidFlatBufferDefinitionException($"Failed to create or find type model for type '{CSharpHelpers.GetCompilableTypeName(type)}'."); - } + FlatSharpInternal.Assert( + this.TryCreateTypeModel(type, out var typeModel), + $"Failed to create or find type model for type '{CSharpHelpers.GetCompilableTypeName(type)}'."); return typeModel; } diff --git a/src/FlatSharp/TypeModel/UnionTypeModel.cs b/src/FlatSharp/TypeModel/UnionTypeModel.cs index 5d31f2db..f6cf1a2c 100644 --- a/src/FlatSharp/TypeModel/UnionTypeModel.cs +++ b/src/FlatSharp/TypeModel/UnionTypeModel.cs @@ -57,31 +57,11 @@ internal UnionTypeModel(Type unionType, TypeModelContainer provider) : base(unio /// public override bool IsFixedSize => false; - /// - /// Unions can't be part of structs. - /// - public override bool IsValidStructMember => false; - /// /// Unions can be part of tables. /// public override bool IsValidTableMember => true; - /// - /// Unions can't be part of unions. - /// - public override bool IsValidUnionMember => false; - - /// - /// Unions can't be part of vectors. - /// - public override bool IsValidVectorMember => false; - - /// - /// Unions can't be keys of sorted vectors. - /// - public override bool IsValidSortedVectorKey => false; - /// /// Unions are pointers. /// @@ -362,21 +342,19 @@ public override void Initialize() public override void Validate() { + base.Validate(); HashSet uniqueTypes = new HashSet(); foreach (var item in this.memberTypeModels) { - if (!item.IsValidUnionMember) - { - throw new InvalidFlatBufferDefinitionException($"Unions may not store '{item.GetCompilableTypeName()}'."); - } - else if (!uniqueTypes.Add(item.ClrType)) - { - throw new InvalidFlatBufferDefinitionException($"Unions must consist of unique types. The type '{item.GetCompilableTypeName()}' was repeated."); - } - } + FlatSharpInternal.Assert( + item.IsValidUnionMember, + $"Unions may not store '{item.GetCompilableTypeName()}'."); - base.Validate(); + FlatSharpInternal.Assert( + uniqueTypes.Add(item.ClrType), + $"Unions must consist of unique types. The type '{item.GetCompilableTypeName()}' was repeated."); + } } public override string GetDeserializedTypeName(IMethodNameResolver nameResolver, FlatBufferDeserializationOption option, string inputBufferTypeName) diff --git a/src/FlatSharp/TypeModel/ValueStructTypeModel.cs b/src/FlatSharp/TypeModel/ValueStructTypeModel.cs index 895617dd..6e3fe25c 100644 --- a/src/FlatSharp/TypeModel/ValueStructTypeModel.cs +++ b/src/FlatSharp/TypeModel/ValueStructTypeModel.cs @@ -78,11 +78,6 @@ internal ValueStructTypeModel(Type clrType, TypeModelContainer container) : base /// public override bool IsValidVectorMember => true; - /// - /// Structs can't be keys of sorted vectors. - /// - public override bool IsValidSortedVectorKey => false; - /// /// Structs are written inline. /// @@ -239,14 +234,15 @@ private CodeGeneratedMethod CreateExternalParseMethod(ParserCodeGenContext conte public override void Initialize() { var structAttribute = this.ClrType.GetCustomAttribute(); + FlatSharpInternal.Assert(structAttribute is not null, "Struct attribute was null"); FlatSharpInternal.Assert(this.ClrType.IsValueType, "Struct was not a value type"); - if (this.ClrType.StructLayoutAttribute is null || - this.ClrType.StructLayoutAttribute.Value != LayoutKind.Explicit || - !this.ClrType.IsExplicitLayout) { - throw new InvalidFlatBufferDefinitionException($"Value struct '{this.GetCompilableTypeName()}' must have [StructLayout(LayoutKind.Explicit)] specified."); + string msg = $"Value struct '{this.GetCompilableTypeName()}' must have [StructLayout(LayoutKind.Explicit)] specified."; + FlatSharpInternal.Assert(this.ClrType.StructLayoutAttribute is not null, msg); + FlatSharpInternal.Assert(this.ClrType.StructLayoutAttribute.Value == LayoutKind.Explicit, msg); + FlatSharpInternal.Assert(this.ClrType.IsExplicitLayout, msg); } var fields = this.ClrType @@ -260,10 +256,7 @@ public override void Initialize() .OrderBy(x => x.OffsetAttribute!.Value) .ToList(); - if (fields.Count == 0) - { - throw new InvalidFlatBufferDefinitionException($"Value struct '{this.GetCompilableTypeName()}' is empty or has no public fields."); - } + FlatSharpInternal.Assert(fields.Count > 0, $"Value struct '{this.GetCompilableTypeName()}' is empty or has no public fields."); this.inlineSize = 0; foreach (var item in fields) @@ -274,20 +267,18 @@ public override void Initialize() ITypeModel propertyModel = this.typeModelContainer.CreateTypeModel(field.FieldType); - if (!propertyModel.IsValidStructMember || propertyModel.PhysicalLayout.Length > 1) - { - throw new InvalidFlatBufferDefinitionException($"Struct '{this.GetCompilableTypeName()}' field {field.Name} cannot be part of a flatbuffer struct. Structs may only contain scalars and other structs."); - } + bool validMember = propertyModel.IsValidStructMember && propertyModel.PhysicalLayout.Length == 1; + FlatSharpInternal.Assert( + validMember, + $"Struct '{this.GetCompilableTypeName()}' field {field.Name} cannot be part of a flatbuffer struct. Structs may only contain scalars and other structs."); - if (!field.IsPublic && string.IsNullOrEmpty(accessor)) - { - throw new InvalidFlatBufferDefinitionException($"Struct '{this.GetCompilableTypeName()}' field {field.Name} is not public and does not declare a custom accessor. Non-public fields must also specify a custom accessor."); - } + FlatSharpInternal.Assert( + !string.IsNullOrEmpty(accessor), + $"Struct '{this.GetCompilableTypeName()}' field {field.Name} is not public and does not declare a custom accessor. Fields must also specify a custom accessor."); - if (!propertyModel.ClrType.IsValueType) - { - throw new InvalidFlatBufferDefinitionException($"Struct '{this.GetCompilableTypeName()}' field {field.Name} must be a value type if the struct is a value type."); - } + FlatSharpInternal.Assert( + propertyModel.ClrType.IsValueType, + $"Struct '{this.GetCompilableTypeName()}' field {field.Name} must be a value type if the struct is a value type."); int propertySize = propertyModel.PhysicalLayout[0].InlineSize; int propertyAlignment = propertyModel.PhysicalLayout[0].Alignment; @@ -296,25 +287,26 @@ public override void Initialize() // Pad for alignment. this.inlineSize += SerializationHelpers.GetAlignmentError(this.inlineSize, propertyAlignment); - this.members.Add((this.inlineSize, accessor ?? field.Name, propertyModel)); - if (offsetAttribute?.Value != this.inlineSize) - { - throw new InvalidFlatBufferDefinitionException($"Struct '{this.ClrType.GetCompilableTypeName()}' property '{field.Name}' defines invalid [FieldOffset] attribute. Expected: [FieldOffset({this.inlineSize})]."); - } + this.members.Add((this.inlineSize, accessor, propertyModel)); + + FlatSharpInternal.Assert( + offsetAttribute is not null, + $"Struct '{this.ClrType.GetCompilableTypeName()}' missing offset attribute."); + FlatSharpInternal.Assert( + offsetAttribute.Value == this.inlineSize, + $"Struct '{this.ClrType.GetCompilableTypeName()}' property '{field.Name}' defines invalid [FieldOffset] attribute. Expected: [FieldOffset({this.inlineSize})]."); this.inlineSize += propertyModel.PhysicalLayout[0].InlineSize; } - if (!this.ClrType.IsPublic && !this.ClrType.IsNestedPublic) - { - throw new InvalidFlatBufferDefinitionException($"Can't create type model from type {this.ClrType.GetCompilableTypeName()} because it is not public."); - } + FlatSharpInternal.Assert( + this.ClrType.IsPublic || this.ClrType.IsNestedPublic, + $"Can't create type model from type {this.ClrType.GetCompilableTypeName()} because it is not public."); this.isExternal = this.ClrType.GetCustomAttribute() is not null; this.CanMarshalOnSerialize = false; this.CanMarshalOnParse = false; - if (UnsafeSizeOf(this.ClrType) == this.inlineSize) { this.CanMarshalOnParse = structAttribute.MemoryMarshalBehavior switch diff --git a/src/FlatSharp/TypeModel/Vectors/BaseVectorTypeModel.cs b/src/FlatSharp/TypeModel/Vectors/BaseVectorTypeModel.cs index 6c6809bd..7f72faee 100644 --- a/src/FlatSharp/TypeModel/Vectors/BaseVectorTypeModel.cs +++ b/src/FlatSharp/TypeModel/Vectors/BaseVectorTypeModel.cs @@ -48,31 +48,11 @@ internal BaseVectorTypeModel(Type vectorType, TypeModelContainer provider) : bas /// public override bool IsFixedSize => false; - /// - /// Vectors can't be part of structs. - /// - public override bool IsValidStructMember => false; - /// /// Vectors can be part of tables. /// public override bool IsValidTableMember => true; - /// - /// Vectors can't be part of unions. - /// - public override bool IsValidUnionMember => false; - - /// - /// Vectors can't be part of vectors. - /// - public override bool IsValidVectorMember => false; - - /// - /// Vector's can't be keys of sorted vectors. - /// - public override bool IsValidSortedVectorKey => false; - /// /// Gets the type model for this vector's elements. /// @@ -213,15 +193,13 @@ public sealed override void Initialize() public override void Validate() { - if (!this.ItemTypeModel.IsValidVectorMember) - { - throw new InvalidFlatBufferDefinitionException($"Type '{this.ItemTypeModel.GetCompilableTypeName()}' is not a valid vector member."); - } + FlatSharpInternal.Assert( + this.ItemTypeModel.IsValidVectorMember, + $"Type '{this.ItemTypeModel.GetCompilableTypeName()}' is not a valid vector member."); - if (this.ItemTypeModel.PhysicalLayout.Length != 1) - { - throw new InvalidFlatBufferDefinitionException($"Vectors may only store vtable layouts with one item. Consider a custom vector type model for other vector kinds."); - } + FlatSharpInternal.Assert( + this.ItemTypeModel.PhysicalLayout.Length == 1, + $"Vectors may only store vtable layouts with one item. Consider a custom vector type model for other vector kinds."); base.Validate(); } @@ -249,21 +227,46 @@ protected string GetThrowIfNullStatement(string variableName) internal static bool ValidateWriteThrough( bool writeThroughSupported, ITypeModel model, - IReadOnlyDictionary> contexts, - FlatBufferSerializerOptions options) + TypeModelContainer container, + IReadOnlyDictionary> contexts) { - if (!contexts.TryGetValue(model, out var fieldsForModel)) + // Helper to validate writethrough for a single model. + static bool ValidateModel( + ITypeModel model, + IReadOnlyDictionary> contexts, + bool writeThroughSupported) { + if (contexts.TryGetValue(model, out var fieldsForModel)) + { + var firstWriteThrough = fieldsForModel.Where(x => x.WriteThrough).FirstOrDefault(); + if (firstWriteThrough is not null && !writeThroughSupported) + { + throw new InvalidFlatBufferDefinitionException($"Field '{firstWriteThrough.FullName}' declares the WriteThrough option. WriteThrough is only supported for IList vectors."); + } + + return firstWriteThrough is not null; + } + return false; } - var firstWriteThrough = fieldsForModel.Where(x => x.WriteThrough).FirstOrDefault(); - if (firstWriteThrough is not null && !writeThroughSupported) + if (ValidateModel(model, contexts, writeThroughSupported)) { - throw new InvalidFlatBufferDefinitionException($"Field '{firstWriteThrough.FullName}' declares the WriteThrough option. WriteThrough is only supported for IList vectors."); + return true; + } + + if (model.ClrType.IsValueType) + { + // Some vectors are structs: Memory and UnityNativeArray. We have + // to reverse those back to a type here and then see if *those* are writethrough. + Type nullable = typeof(Nullable<>).MakeGenericType(model.ClrType); + if (container.TryGetTypeModel(nullable, out var nullableModel)) + { + return ValidateModel(nullableModel, contexts, writeThroughSupported); + } } - return firstWriteThrough is not null; + return false; } } diff --git a/src/FlatSharp/TypeModel/Vectors/ListVectorTypeModel.cs b/src/FlatSharp/TypeModel/Vectors/ListVectorTypeModel.cs index 638af72e..e1c31d47 100644 --- a/src/FlatSharp/TypeModel/Vectors/ListVectorTypeModel.cs +++ b/src/FlatSharp/TypeModel/Vectors/ListVectorTypeModel.cs @@ -101,8 +101,8 @@ public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext c bool isEverWriteThrough = ValidateWriteThrough( writeThroughSupported: !this.isReadOnly, this, - context.AllFieldContexts, - context.Options); + this.typeModelContainer, + context.AllFieldContexts); Func? createVector = context.Options.DeserializationOption switch { diff --git a/src/FlatSharp/TypeModel/Vectors/MemoryVectorTypeModel.cs b/src/FlatSharp/TypeModel/Vectors/MemoryVectorTypeModel.cs index c5172c9d..8972f448 100644 --- a/src/FlatSharp/TypeModel/Vectors/MemoryVectorTypeModel.cs +++ b/src/FlatSharp/TypeModel/Vectors/MemoryVectorTypeModel.cs @@ -51,8 +51,8 @@ public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext c ValidateWriteThrough( writeThroughSupported: false, this, - context.AllFieldContexts, - context.Options); + this.typeModelContainer, + context.AllFieldContexts); string method = nameof(InputBufferExtensions.ReadByteMemoryBlock); if (this.isReadOnly) diff --git a/src/FlatSharp/TypeModel/Vectors/UnityNativeArrayVectorTypeModel.cs b/src/FlatSharp/TypeModel/Vectors/UnityNativeArrayVectorTypeModel.cs index 155c8b22..4fa9ef19 100644 --- a/src/FlatSharp/TypeModel/Vectors/UnityNativeArrayVectorTypeModel.cs +++ b/src/FlatSharp/TypeModel/Vectors/UnityNativeArrayVectorTypeModel.cs @@ -53,12 +53,15 @@ public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext c ValidateWriteThrough( writeThroughSupported: false, this, - context.AllFieldContexts, - context.Options); + this.typeModelContainer, + context.AllFieldContexts); + + string alignmentCheck = $"FlatSharpInternal.AssertWellAligned<{this.ItemTypeModel.GetGlobalCompilableTypeName()}>({this.ItemTypeModel.PhysicalLayout[0].Alignment});"; if (context.Options.GreedyDeserialize) { string body = $@" + {alignmentCheck} var bufferSpan = {context.InputBufferVariableName}.UnsafeReadSpan<{context.InputBufferTypeName}, {this.ItemTypeModel.GetGlobalCompilableTypeName()}>({context.OffsetVariableName}); var nativeArray = new NativeArray<{this.ItemTypeModel.GetGlobalCompilableTypeName()}>(bufferSpan.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); bufferSpan.CopyTo(nativeArray.AsSpan()); @@ -70,6 +73,7 @@ public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext c else { string body = $@" + {alignmentCheck} if (!buffer.IsPinned) {{ throw new NotSupportedException(""Non-greedy parsing of a NativeArray requires a pinned buffer.""); diff --git a/src/FlatSharp/TypeModel/VectorsOfUnion/BaseVectorOfUnionTypeModel.cs b/src/FlatSharp/TypeModel/VectorsOfUnion/BaseVectorOfUnionTypeModel.cs index 06be3e73..9e5245d9 100644 --- a/src/FlatSharp/TypeModel/VectorsOfUnion/BaseVectorOfUnionTypeModel.cs +++ b/src/FlatSharp/TypeModel/VectorsOfUnion/BaseVectorOfUnionTypeModel.cs @@ -48,31 +48,11 @@ internal BaseVectorOfUnionTypeModel(Type vectorType, TypeModelContainer provider /// public override bool IsFixedSize => false; - /// - /// Vectors can't be part of structs. - /// - public override bool IsValidStructMember => false; - /// /// Vectors can be part of tables. /// public override bool IsValidTableMember => true; - /// - /// Vectors can't be part of unions. - /// - public override bool IsValidUnionMember => false; - - /// - /// Vectors can't be part of vectors. - /// - public override bool IsValidVectorMember => false; - - /// - /// Vector's can't be keys of sorted vectors. - /// - public override bool IsValidSortedVectorKey => false; - /// /// Gets the type model for this vector's elements. /// diff --git a/src/Tests/CompileTests/CSharp8/CSharp8.csproj b/src/Tests/CompileTests/CSharp8/CSharp8.csproj index 34587b25..408391b9 100644 --- a/src/Tests/CompileTests/CSharp8/CSharp8.csproj +++ b/src/Tests/CompileTests/CSharp8/CSharp8.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Tests/FlatSharpCompilerTests/FlatSharpCompilerTests.csproj b/src/Tests/FlatSharpCompilerTests/FlatSharpCompilerTests.csproj index 2afe7c51..758a7d16 100644 --- a/src/Tests/FlatSharpCompilerTests/FlatSharpCompilerTests.csproj +++ b/src/Tests/FlatSharpCompilerTests/FlatSharpCompilerTests.csproj @@ -19,7 +19,6 @@ - diff --git a/src/Tests/FlatSharpCompilerTests/InvalidAttributeTests.cs b/src/Tests/FlatSharpCompilerTests/InvalidAttributeTests.cs index 733259a1..37219bc6 100644 --- a/src/Tests/FlatSharpCompilerTests/InvalidAttributeTests.cs +++ b/src/Tests/FlatSharpCompilerTests/InvalidAttributeTests.cs @@ -155,4 +155,161 @@ namespace ns; var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); Assert.Contains("Unable to parse 'fs_vector' value 'banana'. Valid values are: IList, IReadOnlyList, Memory, ReadOnlyMemory, IIndexedVector, UnityNativeArray.", ex.Message); } + + [Fact] + public void Table_DeprecatedKey() + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + table Foo {{ Value : string (key, deprecated); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("Table ns.Foo declares a key property that is deprecated.", ex.Message); + } + + + [Theory] + [InlineData("FunUnion", "UnityNativeArray vectors only support scalar or struct generic arguments. Type = Unity.Collections.NativeArray.")] + [InlineData("FunTable", "UnityNativeArray vectors only support scalar or struct generic arguments. Type = Unity.Collections.NativeArray.")] + [InlineData("RefStruct", "UnityNativeArray vectors only support value types. Type = Unity.Collections.NativeArray.")] + [InlineData("string", "UnityNativeArray vectors only support scalar or struct generic arguments. Type = Unity.Collections.NativeArray.")] + public void UnityNativeVector_InvalidType(string type, string error) + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + + struct RefStruct {{ Value: int; }} + table FunTable {{ Value : int; }} + union FunUnion {{ FunTable }} + + table Foo {{ Value : [ {type} ] ({MetadataKeys.VectorKind}:""UnityNativeArray""); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains(error, ex.Message); + } + + [Theory] + [InlineData("struct")] + [InlineData("table")] + public void Struct_RequiredMember_NotAllowed(string itemType) + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + {itemType} RefStruct {{ Value: int (required); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("only non-scalar fields in tables may be 'required'", ex.Message); + } + + [Fact] + public void Struct_SharedStringMember_NotAllowed() + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + struct RefStruct {{ Value: int ({MetadataKeys.SharedString}); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("The attribute 'fs_sharedString' is never valid on StructField elements.", ex.Message); + } + + [Theory] + [InlineData("Memory")] + [InlineData("ReadOnlyMemory")] + public void MemoryVector_OfNonByte_NotAllowed(string type) + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + table Table {{ Value: [ int ] ({MetadataKeys.VectorKind}:""{type}""); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("Memory vectors may only be ReadOnlyMemory or Memory.", ex.Message); + } + + [Fact] + public void IndexedVector_NonTable() + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + struct Item {{ key : int; }} + table Table {{ Value: [ Item ] ({MetadataKeys.VectorKind}:""IIndexedVector""); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("Indexed vector values must be flatbuffer tables. Type = 'ns.Item'", ex.Message); + } + + + [Fact] + public void VectorOfUnion_WriteThrough_NotAllowed() + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + struct Item {{ key : int; }} + union FunUnion {{ Item }} + table Table {{ Value: [ FunUnion ] ({MetadataKeys.WriteThrough}); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("Table property 'ns.Table.Value' declared the WriteThrough on a vector. Vector WriteThrough is only valid for value type structs.", ex.Message); + } + + [Fact] + public void VectorOfUnion_Sorted_NotAllowed() + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + struct Item {{ key : int; }} + union FunUnion {{ Item }} + table Table {{ Value: [ FunUnion ] ({MetadataKeys.SortedVector}); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("Property 'Value' declares a sorted vector, but the member is not a table. Type = System.Collections.Generic.IList.", ex.Message); + } + + [Fact] + public void Struct_Serializer_NotAllowed() + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + struct Item ({MetadataKeys.SerializerKind}) {{ key : int; }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("The attribute 'fs_serializer' is never valid on ReferenceStruct elements.", ex.Message); + } + + [Theory] + [InlineData("RefStruct")] + [InlineData("Union")] + [InlineData("FunTable")] + [InlineData("int")] + [InlineData("string")] + public void Table_WriteThrough_NotAllowed(string type) + { + string schema = @$" + {MetadataHelpers.AllAttributes} + namespace ns; + union Union {{ RefStruct }} + struct RefStruct {{ key : int; }} + table FunTable {{ x : {type} ({MetadataKeys.WriteThrough}); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("Table property 'ns.FunTable.X' declared the WriteThrough attribute. WriteThrough on tables is only supported for value type structs.", ex.Message); + } } diff --git a/src/Tests/FlatSharpCompilerTests/SortedVectors.cs b/src/Tests/FlatSharpCompilerTests/SortedVectors.cs index 51226d8a..8ea1ffda 100644 --- a/src/Tests/FlatSharpCompilerTests/SortedVectors.cs +++ b/src/Tests/FlatSharpCompilerTests/SortedVectors.cs @@ -20,6 +20,65 @@ namespace FlatSharpTests.Compiler; public class SortedVectors { + [Theory] + [InlineData("string")] + [InlineData("int")] + public void SortedVector_DoubleKey_NotAllowed(string type) + { + string schema = $@" + {MetadataHelpers.AllAttributes} + namespace ns; + table VectorMember {{ + Key : {type} (key); + Key2 : {type} (key); + }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("only one field may be set as 'key'", ex.Message); + } + + [Fact] + public void SortedVector_OfStruct_NotAllowed() + { + string schema = $@" + {MetadataHelpers.AllAttributes} + namespace ns; + struct VectorMember {{ + Key : int; + }} + + table Table {{ + Items : [ VectorMember ] ({MetadataKeys.SortedVector}); + }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("Property 'Items' declares a sorted vector, but the member is not a table.", ex.Message); + } + + [Fact] + public void SortedVector_UnionKey_NotAllowed() + { + string schema = $@" + {MetadataHelpers.AllAttributes} + namespace ns; + + union FunUnion {{ str : string }} + + table VectorMember {{ + Key : FunUnion (key); + }} + + table Table {{ + Items : [ VectorMember ] ({MetadataKeys.SortedVector}); + }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains("'key' field must be string, scalar type or fixed size array of scalars", ex.Message); + } + [Fact] public void SortedVector_StringKey() { diff --git a/src/Tests/FlatSharpCompilerTests/UnionTests.cs b/src/Tests/FlatSharpCompilerTests/UnionTests.cs index 21231853..c1f9c20f 100644 --- a/src/Tests/FlatSharpCompilerTests/UnionTests.cs +++ b/src/Tests/FlatSharpCompilerTests/UnionTests.cs @@ -88,4 +88,26 @@ struct ValueStructB (fs_valueStruct) {{ x : int; }} schema, new()); } + + [Theory] + [InlineData("ValueStruct")] + [InlineData("string")] + [InlineData("Table")] + public void Union_DuplicateTypes_NotAllowed(string type) + { + string schema = $@" + {MetadataHelpers.AllAttributes} + namespace UnionTests; + struct ValueStruct (fs_valueStruct) {{ x : int; }} + table Table {{ x : int; }} + union MyUnion {{ a : {type}, b : {type} }} + "; + + // Usage is validated in the EndtoEndTests. Compiling is good enough for this project. + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly( + schema, + new())); + + Assert.Contains("FlatSharp unions may not contain duplicate types. Union = UnionTests.MyUnion", ex.Message); + } } diff --git a/src/Tests/FlatSharpCompilerTests/ValueStructTests.cs b/src/Tests/FlatSharpCompilerTests/ValueStructTests.cs index 8c22b460..a5711a8c 100644 --- a/src/Tests/FlatSharpCompilerTests/ValueStructTests.cs +++ b/src/Tests/FlatSharpCompilerTests/ValueStructTests.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using FlatSharp.TypeModel; using System.Runtime.InteropServices; namespace FlatSharpTests.Compiler; @@ -176,4 +177,39 @@ struct StructB ({MetadataKeys.ValueStruct}) {{ var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); Assert.Contains($"The attribute 'fs_writeThrough' is never valid on ValueStructField elements.", ex.Message); } + + [Fact] + public void ValueStruct_SortedVectorKey_NotAllowed() + { + string schema = $@" + {MetadataHelpers.AllAttributes} + namespace ValueStructTests; + struct StructB ({MetadataKeys.ValueStruct}) {{ + A : int; + }} + + table Table ({MetadataKeys.SerializerKind}) {{ + A : [ Item ] ({MetadataKeys.SortedVector}); + }} + + table Item {{ + k : StructB (key); + }}"; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains($"Table ValueStructTests.Item declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); + } + + [Fact] + public void ValueStruct_Empty_NotAllowed() + { + string schema = $@" + {MetadataHelpers.AllAttributes} + namespace ValueStructTests; + struct StructB ({MetadataKeys.ValueStruct}) {{ + }}"; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Contains($"size 0 structs not allowed", ex.Message); + } } diff --git a/src/Tests/FlatSharpCompilerTests/WriteThroughTests.cs b/src/Tests/FlatSharpCompilerTests/WriteThroughTests.cs index 116bfdd3..ec46579a 100644 --- a/src/Tests/FlatSharpCompilerTests/WriteThroughTests.cs +++ b/src/Tests/FlatSharpCompilerTests/WriteThroughTests.cs @@ -14,6 +14,9 @@ * limitations under the License. */ +using FlatSharp.TypeModel; +using Microsoft.CodeAnalysis.Options; + namespace FlatSharpTests.Compiler; public class WriteThroughTests @@ -56,4 +59,32 @@ struct Struct ({MetadataKeys.WriteThrough}) {{ foo:int; bar:int ({MetadataKeys.W Test(FlatBufferDeserializationOption.Lazy); Test(FlatBufferDeserializationOption.Progressive); } + + [Fact] + public void UnityVector_WriteThrough_NotAllowed() + { + string schema = $@" + {MetadataHelpers.AllAttributes} + namespace ForceWriteTests; + struct VS ({MetadataKeys.ValueStruct}) {{ V : int; }} + table Table ({MetadataKeys.SerializerKind}) {{ V : [ VS ] ({MetadataKeys.VectorKind}:""UnityNativeArray"", {MetadataKeys.WriteThrough}); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Equal("Field 'ForceWriteTests.Table.V' declares the WriteThrough option. WriteThrough is only supported for IList vectors.", ex.Message); + } + + [Fact] + public void MemoryVector_WriteThrough_NotAllowed() + { + string schema = $@" + {MetadataHelpers.AllAttributes} + namespace ForceWriteTests; + struct VS ({MetadataKeys.ValueStruct}) {{ V : int; }} + table Table ({MetadataKeys.SerializerKind}) {{ V : [ ubyte ] ({MetadataKeys.VectorKind}:""Memory"", {MetadataKeys.WriteThrough}); }} + "; + + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + Assert.Equal("Table property 'ForceWriteTests.Table.V' declared the WriteThrough on a vector. Vector WriteThrough is only valid for value type structs.", ex.Message); + } } diff --git a/src/Tests/FlatSharpEndToEndTests/ClassLib/GeneratedSerializerWrapperTests.cs b/src/Tests/FlatSharpEndToEndTests/ClassLib/GeneratedSerializerWrapperTests.cs new file mode 100644 index 00000000..0644c7b9 --- /dev/null +++ b/src/Tests/FlatSharpEndToEndTests/ClassLib/GeneratedSerializerWrapperTests.cs @@ -0,0 +1,44 @@ +/* + * 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.ClassLib; + +public class GeneratedSerializerWrapperTests +{ + [Fact] + public void Serialize_DestinationBuffer_TooShort() + { + FlatBufferSerializerNonGenericTests.SomeTable table = new(); + table.A = 3; + + var serializer = FlatBufferSerializerNonGenericTests.SomeTable.Serializer; + + byte[] destination = new byte[7]; + Assert.Throws(() => serializer.Write(destination, table)); + } + + [Fact] + public void Serialize_RootTable_Null() + { + var serializer = FlatBufferSerializerNonGenericTests.SomeTable.Serializer; + + byte[] destination = new byte[7]; + var ex = Assert.Throws(() => serializer.Write(destination, null)); + Assert.Contains("The root table may not be null.", ex.Message); + } +} diff --git a/src/Tests/FlatSharpEndToEndTests/ClassLib/SpanComparerTests.cs b/src/Tests/FlatSharpEndToEndTests/ClassLib/SpanComparerTests.cs index 75f58bc6..6c02b3ca 100644 --- a/src/Tests/FlatSharpEndToEndTests/ClassLib/SpanComparerTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/ClassLib/SpanComparerTests.cs @@ -63,6 +63,21 @@ public void RandomFlatBufferStringComparison() } } + [Fact] + public void StringComparer_NullItems() + { + StringSpanComparer comp = default; + + var ex = Assert.Throws(() => comp.Compare(false, default, false, default)); + Assert.Equal("Strings may not be null when used as sorted vector keys.", ex.Message); + + ex = Assert.Throws(() => comp.Compare(false, default, true, default)); + Assert.Equal("Strings may not be null when used as sorted vector keys.", ex.Message); + + ex = Assert.Throws(() => comp.Compare(true, default, false, default)); + Assert.Equal("Strings may not be null when used as sorted vector keys.", ex.Message); + } + [Fact] public void TestBoolComparer() { diff --git a/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj b/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj index 891fde08..8200e605 100644 --- a/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj +++ b/src/Tests/FlatSharpEndToEndTests/FlatSharpEndToEndTests.csproj @@ -4,9 +4,10 @@ false - true + true net7.0;net6.0 - net472;net6.0;net7.0 + net472;net6.0;net7.0 + net6.0;net7.0 net7.0 false FlatSharpEndToEndTests diff --git a/src/Tests/FlatSharpEndToEndTests/Grpc/EchoServiceTestCases.cs b/src/Tests/FlatSharpEndToEndTests/Grpc/EchoServiceTestCases.cs index 0fe0ac7a..3b9df0c9 100644 --- a/src/Tests/FlatSharpEndToEndTests/Grpc/EchoServiceTestCases.cs +++ b/src/Tests/FlatSharpEndToEndTests/Grpc/EchoServiceTestCases.cs @@ -371,8 +371,7 @@ public Task EchoDuplexStreaming_Interface_Canceled_OnWrite() for (int i = 0; i < 100; ++i) { - string str = Guid.NewGuid().ToString(); - await sourceChannel.Writer.WriteAsync(new StringMessage { Value = str }); + await sourceChannel.Writer.WriteAsync(new StringMessage { Value = i.ToString() }); if (i == 50) { @@ -381,6 +380,8 @@ public Task EchoDuplexStreaming_Interface_Canceled_OnWrite() try { + await destChannel.Reader.ReadAsync(); + await destChannel.Reader.ReadAsync(); await destChannel.Reader.ReadAsync(); Assert.False(true, "Exception not thrown"); } @@ -396,14 +397,14 @@ public Task EchoDuplexStreaming_Interface_Canceled_OnWrite() else { var item = await destChannel.Reader.ReadAsync(); - Assert.Equal(str, item.Value); + Assert.Equal(i.ToString(), item.Value); } } await this.AssertCanceled(() => duplexCall); Assert.True(destChannel.Reader.Completion.IsCompleted); - Assert.True(destChannel.Reader.Completion.IsFaulted); + Assert.True(destChannel.Reader.Completion.IsCanceled || destChannel.Reader.Completion.IsFaulted); }); } @@ -439,7 +440,7 @@ private async Task AssertCanceled(Func callback) await callback(); Assert.False(true, "Exception was not thrown"); } - catch (TaskCanceledException) + catch (OperationCanceledException) { } catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled) diff --git a/src/Tests/FlatSharpEndToEndTests/Helpers.cs b/src/Tests/FlatSharpEndToEndTests/Helpers.cs index ea2cac83..cdbf76bc 100644 --- a/src/Tests/FlatSharpEndToEndTests/Helpers.cs +++ b/src/Tests/FlatSharpEndToEndTests/Helpers.cs @@ -19,6 +19,7 @@ using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; +using System.Text; using System.Threading; using Xunit.Abstractions; @@ -34,6 +35,26 @@ public static void Repeat(Action action, int times = 5) } } + public static string ToCSharpArrayString(this byte[] buffer) + { + StringBuilder sb = new(); + sb.Append("new byte[] { "); + + for (int i = 0; i < buffer.Length; ++i) + { + if (i % 4 == 0) + { + sb.AppendLine(); + } + + sb.Append(buffer[i]); + sb.Append(", "); + } + + sb.Append("}"); + return sb.ToString(); + } + public static byte[] AllocateAndSerialize(this T item) where T : class, IFlatBufferSerializable { return item.AllocateAndSerialize(item.Serializer); diff --git a/src/Tests/FlatSharpEndToEndTests/ValueStructs/ValueStructTests.cs b/src/Tests/FlatSharpEndToEndTests/ValueStructs/ValueStructTests.cs index e0f0fd85..b7678e99 100644 --- a/src/Tests/FlatSharpEndToEndTests/ValueStructs/ValueStructTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/ValueStructs/ValueStructTests.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using System; using System.Runtime.InteropServices; namespace FlatSharpEndToEndTests.ValueStructs; @@ -95,8 +96,9 @@ public void ExtensionMethod_WorksForVectors() } } - [Fact] - public void SerializeAndParse_Full() + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void SerializeAndParse_Full(FlatBufferDeserializationOption option) { ValueStruct vs = new ValueStruct { @@ -131,7 +133,7 @@ public void SerializeAndParse_Full() int maxBytes = serializer.GetMaxSize(table); byte[] buffer = new byte[maxBytes]; int written = serializer.Write(buffer, table); - RootTable parsed = serializer.Parse(buffer.AsMemory().Slice(0, written)); + RootTable parsed = serializer.Parse(buffer.AsMemory().Slice(0, written), option); Assert.NotNull(parsed.RefStruct); Assert.NotNull(parsed.ValueStruct); @@ -163,6 +165,107 @@ public void SerializeAndParse_Full() } } + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ValueStructs_UnityNative_WellAligned_Serialize(FlatBufferDeserializationOption option) + { + int count = 10; + + UnityVectors_Native source = new() + { + WellAligned = new(Enumerable.Range(0, count).Select(x => new Vec3 { X = x, Y = x, Z = x }).ToArray(), default), + }; + + byte[] data = source.AllocateAndSerialize(); + + var parsed = UnityVectors_List.Serializer.Parse(data, option); + + for (int i = 0; i < count; ++i) + { + Assert.Equal(source.WellAligned[i].X, parsed.WellAligned[i].X); + Assert.Equal(source.WellAligned[i].Y, parsed.WellAligned[i].Y); + Assert.Equal(source.WellAligned[i].Z, parsed.WellAligned[i].Z); + } + } + + [Fact] + public void ValueStructs_UnityNative_PoorlyAligned_Serialize() + { + int count = 10; + + UnityVectors_Native source = new() + { + WellAligned = new Unity.Collections.NativeArray(Array.Empty(), default), + PoorlyAligned = new(Enumerable.Range(0, count).Select(x => new PoorlyAligned { X = x, Y = 1, }).ToArray(), default), + }; + + var ex = Assert.Throws(() => source.AllocateAndSerialize()); + Assert.Equal("Type 'FlatSharpEndToEndTests.ValueStructs.PoorlyAligned' does not support Unsafe Span operations because the size (5) is not a multiple of the alignment (4).", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ValueStructs_UnityNative_WellAligned_Parse(FlatBufferDeserializationOption option) + { + int count = 10; + + UnityVectors_List source = new() + { + WellAligned = Enumerable.Range(0, count).Select(x => new Vec3 { X = x, Y = x, Z = x }).ToArray(), + }; + + byte[] data = source.AllocateAndSerialize(); + + GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + var parsed = UnityVectors_Native.Serializer.Parse(new MemoryInputBuffer(data, true), option); + + for (int i = 0; i < count; ++i) + { + Assert.Equal(source.WellAligned[i].X, parsed.WellAligned[i].X); + Assert.Equal(source.WellAligned[i].Y, parsed.WellAligned[i].Y); + Assert.Equal(source.WellAligned[i].Z, parsed.WellAligned[i].Z); + } + } + finally + { + handle.Free(); + } + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void ValueStructs_UnityNative_PoorlyAligned_Parse(FlatBufferDeserializationOption option) + { + int count = 10; + + UnityVectors_List source = new() + { + WellAligned = Array.Empty(), + PoorlyAligned = Enumerable.Range(0, count).Select(x => new PoorlyAligned { X = x, Y = 1, }).ToArray(), + }; + + byte[] data = source.AllocateAndSerialize(); + GCHandle handle = GCHandle.Alloc(data); + try + { + var ex = Assert.Throws(() => + { + var parsed = UnityVectors_Native.Serializer.Parse(new MemoryInputBuffer(data, true), option); + float f = parsed.PoorlyAligned.Value[0].X; + }); + + Assert.Equal( + "Type 'FlatSharpEndToEndTests.ValueStructs.PoorlyAligned' does not support Unsafe Span operations because the size (5) is not a multiple of the alignment (4).", + ex.Message); + } + finally + { + handle.Free(); + } + } + [Fact] public void ValueStructs_OutOfRange() { diff --git a/src/Tests/FlatSharpEndToEndTests/ValueStructs/ValueStructs.fbs b/src/Tests/FlatSharpEndToEndTests/ValueStructs/ValueStructs.fbs index 771e58e4..a632d073 100644 --- a/src/Tests/FlatSharpEndToEndTests/ValueStructs/ValueStructs.fbs +++ b/src/Tests/FlatSharpEndToEndTests/ValueStructs/ValueStructs.fbs @@ -3,6 +3,7 @@ attribute "fs_serializer"; attribute "fs_unsafeStructVector"; attribute "fs_valueStruct"; attribute "fs_writeThrough"; +attribute "fs_vector"; namespace FlatSharpEndToEndTests.ValueStructs; @@ -21,6 +22,12 @@ struct Vec3 (fs_valueStruct) Z : float; } +struct PoorlyAligned (fs_valueStruct) +{ + X : float; + Y : ubyte; +} + table RootTable (fs_serializer:"GreedyMutable") { refStruct : RefStruct; valueStruct : ValueStruct; @@ -31,6 +38,16 @@ table RootTable (fs_serializer:"GreedyMutable") { str : string; } +table UnityVectors_Native (fs_serializer) { + well_aligned : [ Vec3 ] (fs_vector:"UnityNativeArray", required); + poorly_aligned : [ PoorlyAligned ] (fs_vector:"UnityNativeArray"); +} + +table UnityVectors_List (fs_serializer) { + well_aligned : [ Vec3 ]; + poorly_aligned : [ PoorlyAligned ]; +} + struct InnerValueStruct (fs_valueStruct) { A : float; } diff --git a/src/Tests/FlatSharpEndToEndTests/Vectors/Sorted/SortedVectorTests.cs b/src/Tests/FlatSharpEndToEndTests/Vectors/Sorted/SortedVectorTests.cs index c7412dd5..e5d77418 100644 --- a/src/Tests/FlatSharpEndToEndTests/Vectors/Sorted/SortedVectorTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/Vectors/Sorted/SortedVectorTests.cs @@ -15,6 +15,7 @@ */ using FlatSharp.Internal; +using System.IO; using System.Runtime.InteropServices; namespace FlatSharpEndToEndTests.Vectors.Sorted; @@ -261,6 +262,102 @@ public class SortedVectorTests (k, v) => k.Key = v, new Utf8StringComparer()); + [Fact] + public void String_Null_Key_Is_Required() + { + RootTable root = new() + { + ListVectorOfString = new List { new() { Key = null, }, new() { Key = "a" }, new() { Key = "b" } } + }; + + Assert.True(typeof(StringKey).GetProperty("Key").GetCustomAttribute().Required); + + var ex = Assert.Throws(() => root.AllocateAndSerialize()); + Assert.Equal("Table property 'FlatSharpEndToEndTests.Vectors.Sorted.StringKey.Key' is marked as required, but was not set.", ex.Message); + } + + [Theory] + [ClassData(typeof(DeserializationOptionClassData))] + public void Int_Null_Key_OK(FlatBufferDeserializationOption option) + { + RootTable root = new() + { + ListVectorOfInt = new List { new() { }, new() { Key = 1 }, new() { Key = 2 } } + }; + + RootTable parsed = root.SerializeAndParse(option, out byte[] buffer); + + // ensure that the '5' is not written to the output. + byte[] expected = + { + 4, 0, 0, 0, + 248, 255, 255, 255, + 20, 0, 0, 0, + 16, 0, 8, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 4, 0, + 3, 0, 0, 0, + 20, 0, 0, 0, + 32, 0, 0, 0, + 4, 0, 0, 0, + 252, 255, 255, 255, + 4, 0, 4, 0, + 248, 255, 255, 255, + 1, 0, 0, 0, + 6, 0, 8, 0, + 4, 0, 0, 0, + 8, 0, 0, 0, + 2, 0, 0, 0, + }; + + Assert.True(buffer.SequenceEqual(expected)); + + Assert.NotNull(parsed.ListVectorOfInt.BinarySearchByFlatBufferKey(5)); + Assert.Null(parsed.ListVectorOfInt.BinarySearchByFlatBufferKey(6)); + } + + [Theory] + [InlineData(FlatBufferDeserializationOption.Lazy)] + [InlineData(FlatBufferDeserializationOption.Progressive)] + public void String_Null_Key_Fails_Binary_Search(FlatBufferDeserializationOption option) + { + RootTable_NonSorted root = new() + { + ListVectorOfString = new List { new() { Key = null, }, new() { Key = "m" }, new() { Key = "z" } } + }; + + byte[] data = root.AllocateAndSerialize(); + RootTable parsed = RootTable.Serializer.Parse(data, option); + + var ex = Assert.Throws(() => + { + var key = parsed.ListVectorOfString.BinarySearchByFlatBufferKey("a"); + }); + + Assert.Equal("Sorted FlatBuffer vectors may not have null-valued keys.", ex.Message); + } + + [Theory] + [InlineData(FlatBufferDeserializationOption.Greedy)] + [InlineData(FlatBufferDeserializationOption.GreedyMutable)] + public void String_Null_Key_Fails_Greedy_Parse(FlatBufferDeserializationOption option) + { + RootTable_NonSorted root = new() + { + ListVectorOfString = new List { new() { Key = null, }, new() { Key = "m" }, new() { Key = "z" } } + }; + + byte[] data = root.AllocateAndSerialize(); + + var ex = Assert.Throws(() => + { + RootTable parsed = RootTable.Serializer.Parse(data, option); + }); + + Assert.Equal("Table property 'FlatSharpEndToEndTests.Vectors.Sorted.StringKey.Key' is marked as required, but was missing from the buffer.", ex.Message); + } + private void SortedVectorStructTestReadOnly( FlatBufferDeserializationOption option, Func> getList, @@ -347,7 +444,7 @@ public class SortedVectorTests } } - private void SortedVectorTest( + private void SortedVectorTest( FlatBufferDeserializationOption option, Func createKey, Func> getList, diff --git a/src/Tests/FlatSharpEndToEndTests/Vectors/Sorted/SortedVectors.fbs b/src/Tests/FlatSharpEndToEndTests/Vectors/Sorted/SortedVectors.fbs index e1cd1c59..239359c2 100644 --- a/src/Tests/FlatSharpEndToEndTests/Vectors/Sorted/SortedVectors.fbs +++ b/src/Tests/FlatSharpEndToEndTests/Vectors/Sorted/SortedVectors.fbs @@ -30,6 +30,28 @@ table RootTable (fs_serializer) list_vector_of_string : [ StringKey ] (fs_sortedVector); } +table RootTable_NonSorted (fs_serializer) +{ + list_vector_of_bool : [ BoolKey ]; + + list_vector_of_byte : [ ByteKey ]; + list_vector_of_sbyte : [ SByteKey ]; + + list_vector_of_short : [ ShortKey ]; + list_vector_of_ushort : [ UShortKey ]; + + list_vector_of_int : [ IntKey ]; + list_vector_of_uint : [ UIntKey ]; + + list_vector_of_long : [ LongKey ]; + list_vector_of_ulong : [ ULongKey ]; + + list_vector_of_float : [ FloatKey ]; + list_vector_of_double : [ DoubleKey ]; + + list_vector_of_string : [ StringKey_NoKey ]; +} + table RootTableReadOnly (fs_serializer) { list_vector_of_bool : [ BoolKey ] (fs_sortedVector, fs_vector:"IReadOnlyList"); @@ -79,10 +101,12 @@ table ByteKey { Key : ubyte (key); } table SByteKey { Key : byte (key); } table ShortKey { Key : short (key); } table UShortKey { Key : ushort (key); } -table IntKey { Key : int (key); } +table IntKey { Key : int = 5(key); } table UIntKey { Key : uint (key); } table LongKey { Key : long (key); } table ULongKey { Key : ulong (key); } table FloatKey { Key : float (key); } table DoubleKey { Key : double (key); } -table StringKey { Key : string (key); } \ No newline at end of file +table StringKey { Key : string (key); } +table StringKey_NoKey { Key : string; } + diff --git a/src/Tests/FlatSharpEndToEndTests/Vectors/Standard/StandardVectorTests.cs b/src/Tests/FlatSharpEndToEndTests/Vectors/Standard/StandardVectorTests.cs index c929f0ca..0d2fe65c 100644 --- a/src/Tests/FlatSharpEndToEndTests/Vectors/Standard/StandardVectorTests.cs +++ b/src/Tests/FlatSharpEndToEndTests/Vectors/Standard/StandardVectorTests.cs @@ -24,6 +24,7 @@ public class StandardVectorTests { private static readonly string[] Strings = new[] { string.Empty, "a", "ab", "abc", "abcd", "abcde" }; private static readonly byte[] Bytes = new byte[] { 1, 2, 3, 4, 5, 6 }; + private static readonly int[] Ints = new int[] { 1, 2, 3, 4, 5, 6 }; #region Lazy @@ -155,6 +156,12 @@ public void Lazy_ReadOnly_Memory_Explicit() Assert.Equal(Bytes.Length, memory.Length); } + [Fact] + public void Lazy_Unity_Pinned() => this.ValidateUnity(FlatBufferDeserializationOption.Lazy, true, true); + + [Fact] + public void Lazy_Unity_NotPinned() => this.ValidateUnity_ExpectPinningError(FlatBufferDeserializationOption.Lazy); + #endregion #region Progressive @@ -285,6 +292,12 @@ public void Progressive_ReadOnly_Memory_Explicit() Assert.Equal(Bytes.Length, memory.Length); } + [Fact] + public void Progressive_Unity_Pinned() => this.ValidateUnity(FlatBufferDeserializationOption.Progressive, true, true); + + [Fact] + public void Progressive_Unity_NotPinned() => this.ValidateUnity_ExpectPinningError(FlatBufferDeserializationOption.Progressive); + #endregion #region Greedy @@ -415,10 +428,15 @@ public void Greedy_ReadOnly_Memory_Explicit() Assert.Equal(Bytes.Length, memory.Length); } - #endregion + [Fact] + public void Greedy_Unity_Pinned() => this.ValidateUnity(FlatBufferDeserializationOption.Greedy, true, false); + [Fact] + public void Greedy_Unity_NotPinned() => this.ValidateUnity(FlatBufferDeserializationOption.Greedy, false, false); - #region Greedy + #endregion + + #region GreedyMutable [Fact] public void GreedyMutable_String_IList_Implicit() @@ -546,8 +564,50 @@ public void GreedyMutable_ReadOnly_Memory_Explicit() Assert.Equal(Bytes.Length, memory.Length); } + [Fact] + public void GreedyMutable_Unity_Pinned() => this.ValidateUnity(FlatBufferDeserializationOption.GreedyMutable, true, false); + + [Fact] + public void GreedyMutable_Unity_NotPinned() => this.ValidateUnity(FlatBufferDeserializationOption.GreedyMutable, false, false); + #endregion + private void ValidateUnity(FlatBufferDeserializationOption option, bool pin, bool expectOverlap) + { + this.SerializeAndParse(option, out _, out byte[] buffer); + + GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + try + { + MemoryInputBuffer inputBuffer = new MemoryInputBuffer(buffer, isPinned: pin); + + var result = StandardVectorTable.Serializer.Parse(inputBuffer, option); + Assert.NotNull(result.UnityNative); + var nativeArray = result.UnityNative.Value; + + Assert.Equal(Ints.Length, nativeArray.Length); + + for (int i = 0; i < Ints.Length; ++i) + { + Assert.Equal(Ints[i], nativeArray[i]); + } + + Assert.Equal( + expectOverlap, + MemoryMarshal.Cast(nativeArray.AsSpan()).Overlaps(buffer.AsSpan())); + } + finally + { + handle.Free(); + } + } + + private void ValidateUnity_ExpectPinningError(FlatBufferDeserializationOption option) + { + var ex = Assert.Throws(() => this.ValidateUnity(option, false, false)); + Assert.Equal("Non-greedy parsing of a NativeArray requires a pinned buffer.", ex.Message); + } + private StandardVectorTable SerializeAndParse(FlatBufferDeserializationOption option, out IFlatBufferDeserializedObject obj, out byte[] inputBuffer) { inputBuffer = new StandardVectorTable @@ -559,6 +619,8 @@ private StandardVectorTable SerializeAndParse(FlatBufferDeserializationOption op ExplicitStringList = Strings, ImplicitStringList = Strings, ReadOnlyStringList = Strings, + + UnityNative = new Unity.Collections.NativeArray(Ints, Unity.Collections.Allocator.Temp) }.AllocateAndSerialize(); var table = StandardVectorTable.Serializer.Parse(inputBuffer, option); diff --git a/src/Tests/FlatSharpEndToEndTests/Vectors/Standard/StandardVectors.fbs b/src/Tests/FlatSharpEndToEndTests/Vectors/Standard/StandardVectors.fbs index 19150de5..1744a93f 100644 --- a/src/Tests/FlatSharpEndToEndTests/Vectors/Standard/StandardVectors.fbs +++ b/src/Tests/FlatSharpEndToEndTests/Vectors/Standard/StandardVectors.fbs @@ -17,4 +17,6 @@ table StandardVectorTable (fs_serializer) implicit_memory : [ ubyte ]; explicit_memory : [ ubyte ] (fs_vector:"Memory"); read_only_memory : [ ubyte ] (fs_vector:"ReadOnlyMemory"); + + unity_native : [ int ] (fs_vector:"UnityNativeArray"); } \ No newline at end of file diff --git a/src/Tests/FlatSharpPoolableEndToEndTests/FlatSharpPoolableEndToEndTests.csproj b/src/Tests/FlatSharpPoolableEndToEndTests/FlatSharpPoolableEndToEndTests.csproj index f5f5832b..7da0486f 100644 --- a/src/Tests/FlatSharpPoolableEndToEndTests/FlatSharpPoolableEndToEndTests.csproj +++ b/src/Tests/FlatSharpPoolableEndToEndTests/FlatSharpPoolableEndToEndTests.csproj @@ -6,7 +6,8 @@ false true net7.0;net6.0 - net472;net6.0;net7.0 + net472;net6.0;net7.0 + net6.0;net7.0 net7.0 false PoolingTests @@ -30,7 +31,6 @@ - diff --git a/src/Tests/FlatSharpTests/ClassLib/TypeModelTests.cs b/src/Tests/FlatSharpTests/ClassLib/TypeModelTests.cs deleted file mode 100644 index 17ac096e..00000000 --- a/src/Tests/FlatSharpTests/ClassLib/TypeModelTests.cs +++ /dev/null @@ -1,1540 +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.Linq; -using FlatSharp.CodeGen; -using FlatSharp.TypeModel; -using Unity.Collections; - -namespace FlatSharpTests; - -public class TypeModelTests -{ - [Fact] - public void TypeModel_Table_UnrecognizedPropertyType() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>))); - - Assert.Equal("Failed to create or find type model for type 'System.Collections.Generic.Dictionary'.", ex.Message); - } - - [Fact] - public void TypeModel_Table_DuplicateIndex() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(DuplicateNumberTable))); - - Assert.Equal("FlatBuffer Table FlatSharpTests.TypeModelTests.DuplicateNumberTable already defines a property with index 0. This may happen when unions are declared as these are double-wide members.", ex.Message); - } - - [Fact] - public void TypeModel_Table_PrivateSetter() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(PrivateSetter))); - - Assert.Equal("Property FlatSharpTests.TypeModelTests.PrivateSetter.String (Index 0) declared a set method, but it was not public/protected and virtual.", ex.Message); - } - - [Fact] - public void TypeModel_Table_InternalSetter() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(InternalSetter))); - - Assert.Equal("Property FlatSharpTests.TypeModelTests.InternalSetter.String (Index 0) declared a set method, but it was not public/protected and virtual.", ex.Message); - } - - [Fact] - public void TypeModel_Table_InterfaceImplementationVirtual() - { - RuntimeTypeModel.CreateFrom(typeof(InterfaceTableVirtual)); - } - - [Fact] - public void TypeModel_Table_NoGetter() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Table_NoGetter))); - Assert.Equal("Property FlatSharpTests.TypeModelTests.Table_NoGetter.Prop (Index 0) on did not declare a getter.", ex.Message); - } - - [Fact] - public void TypeModel_Table_Empty() - { - RuntimeTypeModel.CreateFrom(typeof(Table_Empty)); - } - - [Fact] - public void TypeModel_Table_NonPublicGetter() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Table_NonPublicGetter))); - Assert.Equal("Property FlatSharpTests.TypeModelTests.Table_NonPublicGetter.Prop (Index 0) must declare a public getter.", ex.Message); - } - - [Fact] - public void TypeModel_Table_FileIdentifiers() - { - Assert.IsType(RuntimeTypeModel.CreateFrom(typeof(Table_FileIdentifierOK))); - - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Table_FileIdentifierTooShort))); - Assert.Equal("File identifier 'abc' is invalid. FileIdentifiers must be exactly 4 ASCII characters.", ex.Message); - - ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Table_FileIdentifierTooLong))); - Assert.Equal("File identifier 'abcde' is invalid. FileIdentifiers must be exactly 4 ASCII characters.", ex.Message); - - ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Table_FileIdentifierTooFancy))); - Assert.Equal("File identifier '😍😚😙😵‍' is invalid. FileIdentifiers must be exactly 4 ASCII characters.", ex.Message); - - ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Table_FileIdentifierOutOfRange))); - Assert.Equal("File identifier 'abcµ' contains non-ASCII characters. Character 'µ' is invalid.", ex.Message); - } - - [Fact] - public void TypeModel_Table_InternalConstructor() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(InternalConstructorTable))); - Assert.Equal("Default constructor for 'FlatSharpTests.TypeModelTests.InternalConstructorTable' is not visible to subclasses outside the assembly.", ex.Message); - } - - [Fact] - public void TypeModel_Table_PrivateConstructor() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(PrivateConstructorTable))); - Assert.Equal("Default constructor for 'FlatSharpTests.TypeModelTests.PrivateConstructorTable' is not visible to subclasses outside the assembly.", ex.Message); - } - - [Fact] - public void TypeModel_Table_ForceWrite() - { - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite)); - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite>)); - - // This is a special case and is allowed since memory is a struct - // and is therefore non-null. It will be written as a 0 byte vector. - RuntimeTypeModel.CreateFrom(typeof(Table_ForceWrite>)); - - static void ValidateError() - { - var type = typeof(Table_ForceWrite); - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(type)); - - Assert.Equal( - $"Property 'Item' on table '{CSharpHelpers.GetCompilableTypeName(type)}' declares the ForceWrite option, but the type is not supported for force write.", - ex.Message); - } - - ValidateError(); - ValidateError(); - ValidateError(); - ValidateError(); - ValidateError>(); - ValidateError>(); - ValidateError>(); - ValidateError?>(); - ValidateError>(); - ValidateError?>(); - } - - [Fact] - public void TypeModel_Table_ProtectedConstructor() - { - RuntimeTypeModel.CreateFrom(typeof(ProtectedConstructorTable)); - } - - [Fact] - public void TypeModel_Table_SpecialConstructor() - { - RuntimeTypeModel.CreateFrom(typeof(SpecialConstructorTable)); - } - - [Fact] - public void TypeModel_Table_NoValidCtor() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(Table_NoCtor))); - - Assert.Equal( - "Unable to find a usable constructor for 'FlatSharpTests.TypeModelTests.Table_NoCtor'. The type must supply a default constructor or single parameter constructor accepting 'FlatBufferDeserializationContext' that is visible to subclasses outside the assembly.", - ex.Message); - } - - [Fact] - public void TypeModel_Table_Required_Scalar_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableRequiredField))); - - Assert.Equal( - "Table property 'FlatSharpTests.TypeModelTests.TableRequiredField.Value' declared the Required attribute. Required is only valid on non-scalar table fields.", - ex.Message); - } - - [Fact] - public void TypeModel_Table_Required_Enum_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableRequiredField))); - - Assert.Equal( - "Table property 'FlatSharpTests.TypeModelTests.TableRequiredField.Value' declared the Required attribute. Required is only valid on non-scalar table fields.", - ex.Message); - } - - [Fact] - public void TypeModel_Table_Required() - { - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>>)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>>)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>>)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>)); - RuntimeTypeModel.CreateFrom(typeof(TableRequiredField>)); - } - - [Fact] - public void TypeModel_Table_OnDeserialized() - { - string CreateError() => $"Type '{CSharpHelpers.GetCompilableTypeName(typeof(T))}' provides an unusable 'OnFlatSharpDeserialized' method. 'OnFlatSharpDeserialized' must be protected, have a return type of void, and accept a single parameter of type 'FlatBufferDeserializationContext'."; - - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_WrongParameter))); - Assert.Equal(CreateError(), ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_NoParameter))); - Assert.Equal(CreateError(), ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_OutParameter))); - Assert.Equal(CreateError(), ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_InParameter))); - Assert.Equal(CreateError(), ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_RefParameter))); - Assert.Equal(CreateError(), ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_NotProtected))); - Assert.Equal(CreateError(), ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_ReturnsNonVoid))); - Assert.Equal(CreateError(), ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_Multiple))); - Assert.Equal("Type 'FlatSharpTests.TypeModelTests.TableOnDeserialized_Multiple' provides more than one 'OnFlatSharpDeserialized' method.", ex.Message); - - RuntimeTypeModel.CreateFrom(typeof(TableOnDeserialized_OK)); - } - - [Fact] - public void TypeModel_Table_WriteThrough_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(TableWriteThrough_NotSupported))); - - Assert.Equal( - "Table property 'FlatSharpTests.TypeModelTests.TableWriteThrough_NotSupported.Property' declared the WriteThrough attribute. WriteThrough on tables is only supported for value type structs.", - ex.Message); - } - - [Fact] - public void TypeModel_Struct_NoGetter() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Struct_NoGetter))); - Assert.Equal("Property FlatSharpTests.TypeModelTests.Struct_NoGetter.Prop (Index 0) on did not declare a getter.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_NonPublicGetter() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Struct_NonPublicGetter))); - Assert.Equal("Property FlatSharpTests.TypeModelTests.Struct_NonPublicGetter.Prop (Index 0) must declare a public getter.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_StringNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericStruct))); - Assert.Equal("Struct 'FlatSharpTests.TypeModelTests.GenericStruct' property Value (Index 0) with type System.String cannot be part of a flatbuffer struct.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_VectorNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericStruct>))); - Assert.Equal("Struct 'FlatSharpTests.TypeModelTests.GenericStruct>' property Value (Index 0) with type System.Memory cannot be part of a flatbuffer struct.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_TableNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericStruct>))); - Assert.Equal("Struct 'FlatSharpTests.TypeModelTests.GenericStruct>' property Value (Index 0) with type FlatSharpTests.TypeModelTests.GenericTable cannot be part of a flatbuffer struct.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_UnionNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericStruct>>))); - Assert.Equal("Struct 'FlatSharpTests.TypeModelTests.GenericStruct>>' property Value (Index 0) with type FlatSharp.FlatBufferUnion> cannot be part of a flatbuffer struct.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_Misnumbered() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(MisnumberedStruct))); - Assert.Equal("FlatBuffer struct FlatSharpTests.TypeModelTests.MisnumberedStruct does not declare an item with index 1. Structs must have sequenential indexes starting at 0.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_DeprecatedNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(StructDeprecatedField))); - Assert.Equal( - "FlatBuffer struct FlatSharpTests.TypeModelTests.StructDeprecatedField may not have deprecated properties", - ex.Message); - } - - [Fact] - public void TypeModel_Struct_InaccessibleConstructor() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(InaccessibleConstructorStruct))); - Assert.Equal("Default constructor for 'FlatSharpTests.TypeModelTests.InaccessibleConstructorStruct' is not visible to subclasses outside the assembly.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_Empty() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(Struct_Empty))); - Assert.Equal("Can't create struct type model from type FlatSharpTests.TypeModelTests.Struct_Empty because it does not have any non-static [FlatBufferItem] properties. Structs cannot be empty.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_ProtectedConstructor() - { - RuntimeTypeModel.CreateFrom(typeof(ProtectedConstructorStruct)); - } - - [Fact] - public void TypeModel_Struct_SpecialConstructor() - { - RuntimeTypeModel.CreateFrom(typeof(SpecialConstructorStruct)); - } - - [Fact] - public void TypeModel_Struct_InternalAccess() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(InternalAccessStruct))); - Assert.Equal("Can't create type model from type FlatSharpTests.TypeModelTests.InternalAccessStruct because it is not public.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_Sealed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SealedStruct))); - Assert.Equal("Can't create type model from type FlatSharpTests.TypeModelTests.SealedStruct because it is sealed.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_DoesNotInheritFromObject() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(DoesNotInheritFromObjectStruct))); - Assert.Equal("Can't create type model from type FlatSharpTests.TypeModelTests.DoesNotInheritFromObjectStruct its base class is not System.Object.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_Abstract() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(AbstractStruct))); - Assert.Equal("Can't create type model from type FlatSharpTests.TypeModelTests.AbstractStruct because it is abstract.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_DuplicateNumbered() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(DuplicateNumberedStruct))); - Assert.Equal("FlatBuffer struct FlatSharpTests.TypeModelTests.DuplicateNumberedStruct does not declare an item with index 2. Structs must have sequenential indexes starting at 0.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_DefaultValue() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(StructWithDefaultValue))); - Assert.Equal("FlatBuffer struct FlatSharpTests.TypeModelTests.StructWithDefaultValue declares default value on index 0. Structs may not have default values.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_InterfaceImplementationVirtual() - { - RuntimeTypeModel.CreateFrom(typeof(InterfaceStructVirtual)); - } - - [Fact] - public void TypeModel_Struct_OptionalField_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericStruct))); - Assert.Equal("Struct 'FlatSharpTests.TypeModelTests.GenericStruct>' property Value (Index 0) with type System.Nullable cannot be part of a flatbuffer struct.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_OptionalEnum_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericStruct))); - Assert.Equal("Struct 'FlatSharpTests.TypeModelTests.GenericStruct>' property Value (Index 0) with type System.Nullable cannot be part of a flatbuffer struct.", ex.Message); - } - - [Fact] - public void TypeModel_Struct_ForceWrite_NotAllowed() - { - static void Validate() - { - var type = typeof(Struct_ForceWrite); - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(type)); - - Assert.Equal( - $"FlatBuffer struct {CSharpHelpers.GetCompilableTypeName(type)} may not have properties with the ForceWrite option set to true.", - ex.Message); - } - - Validate(); - Validate(); - Validate(); - Validate(); - Validate(); - Validate(); - Validate(); - Validate(); - Validate(); - Validate(); - Validate(); - Validate(); - Validate>(); - } - - [Fact] - public void TypeModel_Struct_WriteThrough_Virtual() - { - RuntimeTypeModel.CreateFrom(typeof(StructWriteThrough)); - } - - [Fact] - public void TypeModel_Struct_Required_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(StructRequiredField))); - - Assert.Equal( - "Struct member 'FlatSharpTests.TypeModelTests.StructRequiredField.Value' declared the Required attribute. Required is not valid inside structs.", - ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(StructRequiredField))); - - Assert.Equal( - "Struct member 'FlatSharpTests.TypeModelTests.StructRequiredField.Value' declared the Required attribute. Required is not valid inside structs.", - ex.Message); - - ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(StructRequiredField>))); - - Assert.Equal( - "Struct member 'FlatSharpTests.TypeModelTests.StructRequiredField>.Value' declared the Required attribute. Required is not valid inside structs.", - ex.Message); - } - - [Fact] - public void TypeModel_TypeCantBeTableAndStruct() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(InvalidTableAndStruct))); - Assert.Equal("Type 'FlatSharpTests.TypeModelTests.InvalidTableAndStruct' is declared as both [FlatBufferTable] and [FlatBufferStruct].", ex.Message); - } - - [Fact] - public void TypeModel_Vector_VectorNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>>))); - Assert.Equal("Type 'System.Collections.Generic.IList' is not a valid vector member.", ex.Message); - } - - [Fact] - public void TypeModel_MemoryVector_UnionNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>>>))); - Assert.Equal("Memory vectors may only be ReadOnlyMemory or Memory.", ex.Message); - } - - [Fact] - public void TypeModel_Vector_OptionalScalarNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>))); - Assert.Equal("Type 'System.Nullable' is not a valid vector member.", ex.Message); - } - - [Fact] - public void TypeModel_Vector_OptionalEnumNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>))); - Assert.Equal("Type 'System.Nullable' is not a valid vector member.", ex.Message); - } - - [Fact] - public void TypeModel_Vector_Indexed() - { - var model = RuntimeTypeModel.CreateFrom(typeof(GenericTable>>)); - Assert.Equal(FlatBufferSchemaType.Table, model.SchemaType); - - TableTypeModel tableModel = (TableTypeModel)model; - var firstMember = tableModel.IndexToMemberMap[0]; - - Assert.True(firstMember.IsSortedVector); // sorted vector is set even though it's not explicitly declared. Indexed Vectors force it to be set. - - var vectorModel = firstMember.ItemTypeModel; - Assert.Equal(FlatBufferSchemaType.Vector, vectorModel.SchemaType); - } - - [Fact] - public void TypeModel_Vector_IndexedVector_UnkeyedTable_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(IIndexedVector>))); - Assert.Equal("Indexed vector values must have a property with the key attribute defined. Table = 'FlatSharpTests.TypeModelTests.GenericTable'", ex.Message); - } - - [Fact] - public void TypeModel_Vector_IndexedVector_Struct_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(IIndexedVector>))); - Assert.Equal("Indexed vector values must be flatbuffer tables. Type = 'FlatSharpTests.TypeModelTests.GenericStruct'", ex.Message); - } - - [Fact] - public void TypeModel_Enum_UntaggedEnumNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(UntaggedEnum))); - Assert.Equal("Enum 'FlatSharpTests.TypeModelTests.UntaggedEnum' is not tagged with a [FlatBufferEnum] attribute.", ex.Message); - } - - [Fact] - public void TypeModel_Enum_NullableUntaggedEnumNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(UntaggedEnum?))); - Assert.Equal("Enum 'FlatSharpTests.TypeModelTests.UntaggedEnum' is not tagged with a [FlatBufferEnum] attribute.", ex.Message); - } - - [Fact] - public void TypeModel_Enum_TaggedEnum() - { - var model = RuntimeTypeModel.CreateFrom(typeof(TaggedEnum)); - - Assert.True(model is EnumTypeModel enumModel); - Assert.Equal(typeof(TaggedEnum), model.ClrType); - Assert.True(model.IsFixedSize); - } - - [Fact] - public void TypeModel_Enum_NullableTaggedEnum() - { - var model = RuntimeTypeModel.CreateFrom(typeof(TaggedEnum?)); - - Assert.True(model is NullableTypeModel nullableModel); - Assert.Equal(typeof(TaggedEnum?), model.ClrType); - Assert.True(model.IsFixedSize); - } - - [Fact] - public void TypeModel_Vector_Array_NotAllowed() - { - var ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(int[]))); - Assert.Equal( - "Failed to create or find type model for type 'System.Int32[]'.", - ex.Message); - } - - [Fact] - public void TypeModel_Vector_ListVectorOfStruct() - { - var model = this.VectorTest(typeof(IList<>), typeof(GenericStruct)); - Assert.IsType(model); - } - - [Fact] - public void TypeModel_Vector_ReadOnlyListOfTable() - { - var model = this.VectorTest(typeof(IReadOnlyList<>), typeof(GenericTable)); - Assert.IsType(model); - } - - [Fact] - public void TypeModel_Vector_MemoryOfByte() - { - var model = this.VectorTest(typeof(Memory<>), typeof(byte)); - Assert.IsType(model); - } - - [Fact] - public void TypeModel_Vector_NullableMemoryOfByte() - { - var model = RuntimeTypeModel.CreateFrom(typeof(Memory?)); - Assert.IsType(model); - } - - [Fact] - public void TypeModel_Vector_NullableReadOnlyMemoryOfByte() - { - var model = RuntimeTypeModel.CreateFrom(typeof(ReadOnlyMemory?)); - Assert.IsType(model); - } - - [Fact] - public void TypeModel_Vector_MemoryOfScalar_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(Memory))); - Assert.Equal("Memory vectors may only be ReadOnlyMemory or Memory.", ex.Message); - } - - [Fact] - public void TypeModel_Vector_MemoryOfEnum_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(Memory))); - Assert.Equal("Memory vectors may only be ReadOnlyMemory or Memory.", ex.Message); - } - - [Fact] - public void TypeModel_Vector_ReadOnlyMemoryOfByte() - { - var model = this.VectorTest(typeof(ReadOnlyMemory<>), typeof(byte)); - Assert.IsType(model); - } - - [Fact] - public void TypeModel_Vector_ReadOnlyMemoryOfScalar_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(ReadOnlyMemory))); - Assert.Equal("Memory vectors may only be ReadOnlyMemory or Memory.", ex.Message); - } - - [Fact] - public void TypeModel_Vector_ReadOnlyMemoryOfEnum_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(ReadOnlyMemory))); - Assert.Equal("Memory vectors may only be ReadOnlyMemory or Memory.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfStruct_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Struct member 'FlatSharpTests.TypeModelTests.SortedVectorKeyStruct.Key' declared the 'key' attribute. FlatSharp does not support keys on struct members.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfEnum_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>))); - Assert.Equal("Property 'Vector' declares a sorted vector, but the member is not a table. Type = System.Collections.Generic.IList.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfNotATable_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector))); - Assert.Equal("Property 'Vector' declares the sortedVector option, but the underlying type was not a vector.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfNullableEnum_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>))); - Assert.Equal("Type 'System.Nullable' is not a valid vector member.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfString_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>))); - Assert.Equal("Property 'Vector' declares a sorted vector, but the member is not a table. Type = System.Collections.Generic.IList.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfScalar_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>))); - Assert.Equal("Property 'Vector' declares a sorted vector, but the member is not a table. Type = System.Collections.Generic.IList.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfNullableScalar_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>))); - Assert.Equal("Type 'System.Nullable' is not a valid vector member.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithBuiltInKey() - { - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>)); - } - - [Fact] - public void TypeModel_SortedVector_DeprecatedKey_NotAllowed() - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal( - "Table FlatSharpTests.TypeModelTests.SortedVectorDeprecatedKeyTable declares a key property that is deprecated.", - ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithOptionalKey_NotAllowed() - { - var ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - - ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithStructKey_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>>))); - Assert.Equal("Struct 'FlatSharpTests.TypeModelTests.GenericStruct' property Value (Index 0) with type System.String cannot be part of a flatbuffer struct.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithTableKey_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithVectorKey_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithEnumKey_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithNullableEnumKey_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorKeyTable> declares a key property on a type that that does not support being a key in a sorted vector.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithoutKey_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Property 'Vector' declares a sorted vector, but the member does not have a key defined. Type = System.Collections.Generic.IList>.", ex.Message); - } - - [Fact] - public void TypeModel_SortedVector_OfTableWithMultipleKeys_NotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(SortedVector>>))); - Assert.Equal("Table FlatSharpTests.TypeModelTests.SortedVectorMultiKeyTable has more than one [FlatBufferItemAttribute] with Key set to true.", ex.Message); - } - - private BaseVectorTypeModel VectorTest(Type vectorDefinition, Type innerType) - { - var model = (BaseVectorTypeModel)RuntimeTypeModel.CreateFrom(vectorDefinition.MakeGenericType(innerType)); - - Assert.Equal(model.ClrType.GetGenericTypeDefinition(), vectorDefinition); - Assert.Equal(4, model.PhysicalLayout.Single().InlineSize); - Assert.Equal(4, model.PhysicalLayout.Single().Alignment); - - var innerModel = RuntimeTypeModel.CreateFrom(innerType); - Assert.Equal(innerModel.ClrType, model.ItemTypeModel.ClrType); - - return model; - } - - /// - /// This scenario actually works with flatsharp, but the flatc compiler does not support this, so it doesn't seem to be an officially - /// sanctioned feature. - /// - [Fact] - public void TypeModel_Union_VectorsNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>>))); - Assert.Equal("Unions may not store 'System.Collections.Generic.IList'.", ex.Message); - - ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>>))); - Assert.Equal("Unions may not store 'System.Collections.Generic.IReadOnlyList'.", ex.Message); - - ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>>))); - Assert.Equal("Unions may not store 'System.Memory'.", ex.Message); - - ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>>))); - Assert.Equal("Unions may not store 'System.ReadOnlyMemory'.", ex.Message); - } - - [Fact] - public void TypeModel_Union_UnionsNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>?>?>))); - Assert.Equal("Unions may not store 'System.Nullable>>'.", ex.Message); - } - - [Fact] - public void TypeModel_Union_ScalarsNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable?>))); - Assert.Equal("Unions may not store 'System.Int32'.", ex.Message); - } - - [Fact] - public void TypeModel_Union_OptionalScalarsNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable>))); - Assert.Equal("Unions may not store 'System.Nullable'.", ex.Message); - } - - [Fact] - public void TypeModel_Union_EnumsNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable?>))); - Assert.Equal("Unions may not store 'FlatSharpTests.TypeModelTests.TaggedEnum'.", ex.Message); - } - - [Fact] - public void TypeModel_Union_OptionalEnumsNotAllowed() - { - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(typeof(GenericTable?>))); - Assert.Equal("Unions may not store 'System.Nullable'.", ex.Message); - } - - [Fact] - public void FlatBufferSerializer_OnlyTablesAllowedAsRootType() - { - var ex = Assert.Throws(() => - FlatBufferSerializer.Default.Compile>()); - Assert.Equal("Can only compile [FlatBufferTable] elements as root types. Type 'FlatSharpTests.TypeModelTests.GenericStruct' is a Struct.", ex.Message); - - ex = Assert.Throws(() => - FlatBufferSerializer.Default.Compile>()); - Assert.Equal("Can only compile [FlatBufferTable] elements as root types. Type 'System.Collections.Generic.IList' is a Vector.", ex.Message); - - ex = Assert.Throws(() => - FlatBufferSerializer.Default.Compile()); - Assert.Equal("Can only compile [FlatBufferTable] elements as root types. Type 'System.String' is a String.", ex.Message); - } - - [Fact] - public void TypeModel_Union_StructsTablesStringsAllowed() - { - var tableModel = (TableTypeModel)RuntimeTypeModel.CreateFrom(typeof(GenericTable, GenericStruct>?>)); - Assert.Equal(1, tableModel.IndexToMemberMap.Count); - - var nullableModel = (NullableTypeModel)tableModel.IndexToMemberMap[0].ItemTypeModel; - var model = (UnionTypeModel)nullableModel.Children.Single(); - - Assert.Equal(3, model.UnionElementTypeModel.Length); - Assert.IsType(model.UnionElementTypeModel[0]); - Assert.IsType(model.UnionElementTypeModel[1]); - Assert.IsType(model.UnionElementTypeModel[2]); - } - - [Fact] - public void TypeModel_StructsWithPadding() - { - // inner strut is float + double + bool = 4x float + 4x padding + 8x double + 1 bool = 17. - // outer struct is inner + double + bool = 17x inner + 7x padding + 8x double + 1x bool = 33 - Type type = typeof(GenericStruct>); - var typeModel = RuntimeTypeModel.CreateFrom(type); - - Assert.Equal(typeModel.ClrType, type); - Assert.IsType(typeModel); - - var structModel = (StructTypeModel)typeModel; - Assert.Equal(3, structModel.Members.Count); - - Assert.IsType(structModel.Members[0].ItemTypeModel); - Assert.Equal(0, structModel.Members[0].Index); - Assert.Equal(0, structModel.Members[0].Offset); - Assert.Equal(8, structModel.Members[0].ItemTypeModel.PhysicalLayout.Single().Alignment); - Assert.Equal(17, structModel.Members[0].ItemTypeModel.PhysicalLayout.Single().InlineSize); - - Assert.IsAssignableFrom(structModel.Members[1].ItemTypeModel); - Assert.Equal(1, structModel.Members[1].Index); - Assert.Equal(24, structModel.Members[1].Offset); - Assert.Equal(8, structModel.Members[1].ItemTypeModel.PhysicalLayout.Single().Alignment); - Assert.Equal(8, structModel.Members[1].ItemTypeModel.PhysicalLayout.Single().InlineSize); - - Assert.IsAssignableFrom(structModel.Members[2].ItemTypeModel); - Assert.Equal(2, structModel.Members[2].Index); - Assert.Equal(32, structModel.Members[2].Offset); - Assert.Equal(1, structModel.Members[2].ItemTypeModel.PhysicalLayout.Single().Alignment); - Assert.Equal(1, structModel.Members[2].ItemTypeModel.PhysicalLayout.Single().InlineSize); - - Assert.Equal(33, structModel.PhysicalLayout.Single().InlineSize); - Assert.Equal(8, structModel.PhysicalLayout.Single().Alignment); - } - - [Fact] - public void TypeModel_Vector_NativeArrayOfUnionIsNotAllowed() - { - var ex = Assert.Throws( - () => TypeModelContainer.CreateDefault().WithUnitySupport(true).CreateTypeModel(typeof(NativeArray>))); - Assert.Equal("UnityNativeArray vectors only support scalar or struct generic arguments. Type = Unity.Collections.NativeArray>.", ex.Message); - } - - [Fact] - public void TypeModel_Vector_NativeArrayOfClassIsNotAllowed() - { - var ex = Assert.Throws( - () => TypeModelContainer.CreateDefault().WithUnitySupport(true).CreateTypeModel(typeof(NativeArray))); - Assert.Equal("UnityNativeArray vectors only support scalar or struct generic arguments. Type = Unity.Collections.NativeArray.", ex.Message); - } - - [Fact] - public void TypeModel_Vector_NativeArrayOfReferenceStruct_NotAllowed() - { - var ex = Assert.Throws( - () => TypeModelContainer.CreateDefault().WithUnitySupport(true).CreateTypeModel(typeof(NativeArray))); - - Assert.Equal("UnityNativeArray vectors only support value types. Type = Unity.Collections.NativeArray.", ex.Message); - } - - [Fact] - public void Parse_NativeArrayThrowsExceptionForNonPinnedBuffer() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 0, 0, 0, 0, // vector length - }; - - Assert.Throws(() => - { - var table = new FlatBufferSerializer( - new FlatBufferSerializerOptions() { DeserializationOption = FlatBufferDeserializationOption.Lazy }, - TypeModelContainer.CreateDefault().WithUnitySupport(true)).Parse>>(data); - - // the lazy access here should cause an exception - table.Value.GetHashCode(); - }); - } - - public enum UntaggedEnum - { - Foo, Bar - } - - [FlatBufferEnum(typeof(int))] - public enum TaggedEnum - { - Foo, Bar - } - - [FlatBufferTable, FlatBufferStruct] - public class InvalidTableAndStruct - { - [FlatBufferItem(0)] - public virtual string String { get; set; } - } - - [FlatBufferTable] - public class GenericTable : ISortableTable where T : notnull - { - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferTable] - public class DuplicateNumberTable - { - [FlatBufferItem(0)] - public virtual string Value { get; set; } - - [FlatBufferItem(0)] - public virtual double Value2 { get; set; } - } - - [FlatBufferStruct] - public class GenericStruct : ISortableTable where T : notnull - { - [FlatBufferItem(0)] - public virtual T Value { get; set; } - - [FlatBufferItem(1)] - public virtual double Double { get; protected internal set; } - - [FlatBufferItem(2)] - public virtual bool Bool { get; protected set; } - } - - [FlatBufferStruct] - public class DuplicateNumberedStruct - { - [FlatBufferItem(0)] - public virtual T Value { get; set; } - - [FlatBufferItem(1)] - public virtual double Double { get; set; } - - [FlatBufferItem(1)] - public virtual double Double2 { get; set; } - } - - [FlatBufferStruct] - public class MisnumberedStruct - { - [FlatBufferItem(0)] - public virtual T Value { get; set; } - - [FlatBufferItem(2)] - public virtual double Double { get; set; } - } - - [FlatBufferStruct] - public class InaccessibleConstructorStruct - { - internal InaccessibleConstructorStruct() { } - - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferStruct] - public class ProtectedConstructorStruct - { - protected ProtectedConstructorStruct() { } - - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferStruct] - public class SpecialConstructorStruct - { - protected SpecialConstructorStruct(FlatBufferDeserializationContext context) { } - - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferTable] - public class PrivateConstructorTable - { - internal PrivateConstructorTable() { } - - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferTable] - public class InternalConstructorTable - { - internal InternalConstructorTable() { } - - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferTable] - public class ProtectedConstructorTable - { - protected ProtectedConstructorTable() { } - - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferTable] - public class SpecialConstructorTable - { - protected SpecialConstructorTable(FlatBufferDeserializationContext context) { } - - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferStruct] - internal class InternalAccessStruct - { - [FlatBufferItem(0)] - public virtual T Value { get; set; } - } - - [FlatBufferStruct] - public abstract class AbstractStruct - { - [FlatBufferItem(0)] - public abstract T Value { get; set; } - } - - [FlatBufferStruct] - public class StructWithDefaultValue - { - [FlatBufferItem(0, DefaultValue = 100)] - public virtual int Int { get; set; } - } - - [FlatBufferStruct] - public sealed class SealedStruct - { - [FlatBufferItem(0)] - public T Value { get; set; } - } - - [FlatBufferStruct] - public class DoesNotInheritFromObjectStruct : GenericStruct - { - } - - [FlatBufferTable] - public class OverlappingUnionIndex - { - [FlatBufferItem(0)] - public virtual FlatBufferUnion> Union { get; set; } - - // Invalid -- this should be at index 2. - [FlatBufferItem(1)] - public virtual string FooBar { get; set; } - } - - [FlatBufferTable] - public class PrivateSetter - { - [FlatBufferItem(0)] - public virtual string String { get; private set; } - } - - [FlatBufferTable] - public class InternalSetter - { - [FlatBufferItem(0)] - public virtual string String { get; internal set; } - } - - public interface IInterface - { - int Foo { get; set; } - } - - [FlatBufferTable] - public class InterfaceTableVirtual : IInterface - { - [FlatBufferItem(0)] - public virtual int Foo { get; set; } - } - - [FlatBufferTable] - public class InterfaceStructVirtual : IInterface - { - [FlatBufferItem(0)] - public virtual int Foo { get; set; } - } - - [FlatBufferTable] - public class SortedVector - { - [FlatBufferItem(0, SortedVector = true)] - public virtual T Vector { get; set; } - } - - [FlatBufferTable] - public class SortedVectorKeyTable : ISortableTable where T : notnull - { - [FlatBufferItem(0, Key = true)] - public virtual T Key { get; set; } - } - - [FlatBufferTable] - public class SortedVectorDeprecatedKeyTable - { - [FlatBufferItem(0, Key = true, Deprecated = true)] - public virtual T Key { get; set; } - } - - [FlatBufferTable] - public class SortedVectorMultiKeyTable - { - [FlatBufferItem(0, Key = true)] - public virtual T Key { get; set; } - - [FlatBufferItem(1, Key = true)] - public virtual T Key2 { get; set; } - } - - [FlatBufferStruct] - public class SortedVectorKeyStruct - { - [FlatBufferItem(0, Key = true)] - public virtual T Key { get; set; } - } - - [FlatBufferTable] - public class Table_NoGetter - { - private int value; - - [FlatBufferItem(0)] - public virtual int Prop { set => this.value = value; } - } - - [FlatBufferStruct] - public class Struct_NoGetter - { - private int value; - - [FlatBufferItem(0)] - public virtual int Prop { set => this.value = value; } - } - - [FlatBufferTable] - public class Table_NonPublicGetter - { - [FlatBufferItem(0)] - public virtual int Prop { protected get; set; } - } - - [FlatBufferStruct] - public class Struct_NonPublicGetter - { - [FlatBufferItem(0)] - public virtual int Prop { protected get; set; } - } - - [FlatBufferTable] - public class Table_ForceWrite - { - [FlatBufferItem(0, ForceWrite = true)] - public virtual T Item { get; set; } - } - - [FlatBufferStruct] - public class Struct_ForceWrite - { - [FlatBufferItem(0, ForceWrite = true)] - public virtual T Item { get; set; } - } - - [FlatBufferTable(FileIdentifier = "abc")] - public class Table_FileIdentifierTooShort - { - [FlatBufferItem(0)] - public virtual int Prop { get; set; } - } - - [FlatBufferTable(FileIdentifier = "abcd")] - public class Table_FileIdentifierOK - { - [FlatBufferItem(0)] - public virtual int Prop { get; set; } - } - - [FlatBufferTable(FileIdentifier = "abcde")] - public class Table_FileIdentifierTooLong - { - [FlatBufferItem(0)] - public virtual int Prop { get; set; } - } - - [FlatBufferTable(FileIdentifier = "😍😚😙😵‍")] - public class Table_FileIdentifierTooFancy - { - [FlatBufferItem(0)] - public virtual int Prop { get; set; } - } - - [FlatBufferTable(FileIdentifier = "abcµ")] - public class Table_FileIdentifierOutOfRange - { - [FlatBufferItem(0)] - public virtual int Prop { get; set; } - } - - [FlatBufferTable] - public class Table_Empty - { - } - - [FlatBufferStruct] - public class Struct_Empty - { - } - - [FlatBufferTable] - public class TableOnDeserialized_OK - { - protected void OnFlatSharpDeserialized(FlatBufferDeserializationContext context) - { - } - } - - [FlatBufferTable] - public class TableOnDeserialized_NotProtected - { - public void OnFlatSharpDeserialized(FlatBufferDeserializationContext context) - { - } - } - - [FlatBufferTable] - public class TableOnDeserialized_WrongParameter - { - protected void OnFlatSharpDeserialized(string s) - { - } - } - - [FlatBufferTable] - public class TableOnDeserialized_NoParameter - { - protected void OnFlatSharpDeserialized() - { - } - } - - [FlatBufferTable] - public class TableOnDeserialized_OutParameter - { - protected void OnFlatSharpDeserialized(out FlatBufferDeserializationContext context) - { - context = default; - } - } - - [FlatBufferTable] - public class TableOnDeserialized_InParameter - { - protected void OnFlatSharpDeserialized(in FlatBufferDeserializationContext context) - { - } - } - - [FlatBufferTable] - public class TableOnDeserialized_RefParameter - { - protected void OnFlatSharpDeserialized(ref FlatBufferDeserializationContext context) - { - } - } - - [FlatBufferTable] - public class TableOnDeserialized_Multiple - { - protected void OnFlatSharpDeserialized(FlatBufferDeserializationContext context) - { - } - - protected void OnFlatSharpDeserialized(string s) - { - } - } - - [FlatBufferTable] - public class TableOnDeserialized_ReturnsNonVoid - { - protected string OnFlatSharpDeserialized(FlatBufferDeserializationContext context) - { - return "foo"; - } - } - - [FlatBufferTable] - public class TableWriteThrough_NotSupported - { - [FlatBufferItem(0, WriteThrough = true)] - public virtual int Property { get; set; } - } - - [FlatBufferStruct] - public class StructWriteThrough - { - [FlatBufferItem(0, WriteThrough = true)] - public virtual int Property { get; set; } - - [FlatBufferItem(1, WriteThrough = false)] - public virtual int Property2 { get; set; } - } - - [FlatBufferTable] - public class Table_NoCtor - { - public Table_NoCtor(string foo) - { - } - - [FlatBufferItem(0)] public virtual int Value { get; set; } - } - - [FlatBufferStruct] - public class StructDeprecatedField - { - [FlatBufferItem(0)] - public virtual int A { get; set; } - - [FlatBufferItem(1, Deprecated = true)] - public virtual int B { get; set; } - } - - [FlatBufferTable] - public class TableRequiredField - { - [FlatBufferItem(0, Required = true)] - public virtual T Value { get; set; } - } - - [FlatBufferStruct] - public class StructRequiredField - { - [FlatBufferItem(0, Required = true)] - public virtual T Value { get; set; } - } -} diff --git a/src/Tests/FlatSharpTests/FlatSharpTests.csproj b/src/Tests/FlatSharpTests/FlatSharpTests.csproj deleted file mode 100644 index bf9de002..00000000 --- a/src/Tests/FlatSharpTests/FlatSharpTests.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - net472;net6.0;net7.0 - net47;net6.0;net7.0 - false - FlatSharpTests - x64 - FlatSharpTests - annotations - - - - - - - - - - - - - - - - - - UnionTypes.cs - TextTemplatingFileGenerator - - - - - - - diff --git a/src/Tests/FlatSharpTests/GlobalUsings.cs b/src/Tests/FlatSharpTests/GlobalUsings.cs deleted file mode 100644 index 3f252f44..00000000 --- a/src/Tests/FlatSharpTests/GlobalUsings.cs +++ /dev/null @@ -1,31 +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. - */ - -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.Reflection; -global using System.Runtime.CompilerServices; -global using FlatSharp; -global using FlatSharp.Attributes; -global using FlatSharp.Internal; -global using Xunit; diff --git a/src/Tests/FlatSharpTests/ModuleInitializer.cs b/src/Tests/FlatSharpTests/ModuleInitializer.cs deleted file mode 100644 index 1d2d4833..00000000 --- a/src/Tests/FlatSharpTests/ModuleInitializer.cs +++ /dev/null @@ -1,42 +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. - */ - -namespace FlatSharpTests -{ - using System.Runtime.CompilerServices; - using FlatSharp.CodeGen; - - public partial class ModuleInitializer - { - [ModuleInitializer] - public static void AssemblyInitialize() - { -#if NETCOREAPP - RoslynSerializerGenerator.EnableStrictValidation = true; -#endif - } - } -} - -#if !NET5_0_OR_GREATER -namespace System.Runtime.CompilerServices -{ - [AttributeUsage(AttributeTargets.Method)] - public class ModuleInitializerAttribute : Attribute - { - } -} -#endif diff --git a/src/Tests/FlatSharpTests/SerializationTests/ScalarVectorTests.cs b/src/Tests/FlatSharpTests/SerializationTests/ScalarVectorTests.cs deleted file mode 100644 index b92d6d8c..00000000 --- a/src/Tests/FlatSharpTests/SerializationTests/ScalarVectorTests.cs +++ /dev/null @@ -1,248 +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.Linq; - -namespace FlatSharpTests; - -/// -/// Tests various types of vectors (List/ReadOnlyList/Memory/ReadOnlyMemory/Array) for primitive types. -/// -public class ScalarVectorTests -{ - private static readonly Random r = new Random(); - - [Fact] - public void BoolVector() - { - this.TestType(() => r.Next() % 2 == 0, 0); - this.TestType(() => r.Next() % 2 == 0, 1); - this.TestType(() => r.Next() % 2 == 0, 10); - this.TestType(() => r.Next() % 2 == 0, 500); - } - - [Fact] - public void ByteVector() - { - this.TestType(() => (byte)r.Next(), 0); - this.TestType(() => (byte)r.Next(), 1); - this.TestType(() => (byte)r.Next(), 10); - this.TestType(() => (byte)r.Next(), 500); - } - - [Fact] - public void SByteVector() - { - this.TestType(() => (sbyte)r.Next(), 0); - this.TestType(() => (sbyte)r.Next(), 1); - this.TestType(() => (sbyte)r.Next(), 10); - this.TestType(() => (sbyte)r.Next(), 500); - } - - [Fact] - public void UShortVector() - { - this.TestType(() => (ushort)r.Next(), 0); - this.TestType(() => (ushort)r.Next(), 1); - this.TestType(() => (ushort)r.Next(), 10); - this.TestType(() => (ushort)r.Next(), 500); - } - - [Fact] - public void ShortVector() - { - this.TestType(() => (short)r.Next(), 0); - this.TestType(() => (short)r.Next(), 1); - this.TestType(() => (short)r.Next(), 10); - this.TestType(() => (short)r.Next(), 500); - } - - [Fact] - public void UintVector() - { - this.TestType(() => (uint)r.Next(), 0); - this.TestType(() => (uint)r.Next(), 1); - this.TestType(() => (uint)r.Next(), 10); - this.TestType(() => (uint)r.Next(), 500); - } - - [Fact] - public void IntVector() - { - this.TestType(() => r.Next(), 0); - this.TestType(() => r.Next(), 1); - this.TestType(() => r.Next(), 10); - this.TestType(() => r.Next(), 500); - } - - [Fact] - public void ULongVector() - { - this.TestType(() => (ulong)r.Next(), 0); - this.TestType(() => (ulong)r.Next(), 1); - this.TestType(() => (ulong)r.Next(), 10); - this.TestType(() => (ulong)r.Next(), 500); - } - - [Fact] - public void LongVector() - { - this.TestType(() => (long)r.Next(), 0); - this.TestType(() => (long)r.Next(), 1); - this.TestType(() => (long)r.Next(), 10); - this.TestType(() => (long)r.Next(), 500); - } - - [Fact] - public void FloatVector() - { - this.TestType(() => (float)r.NextDouble(), 0); - this.TestType(() => (float)r.NextDouble(), 1); - this.TestType(() => (float)r.NextDouble(), 10); - this.TestType(() => (float)r.NextDouble(), 500); - } - - [Fact] - public void DoubleVector() - { - this.TestType(() => r.NextDouble(), 0); - this.TestType(() => r.NextDouble(), 1); - this.TestType(() => r.NextDouble(), 10); - this.TestType(() => r.NextDouble(), 500); - } - - [Fact] - public void EnumVector() - { - this.TestType(() => SimpleEnum.Bar, 0); - this.TestType(() => SimpleEnum.Foo, 1); - this.TestType(() => SimpleEnum.Bar, 10); - this.TestType(() => SimpleEnum.Foo, 500); - } - - private void TestType(Func generator, int length) - { - if (typeof(T) == typeof(byte)) - { - var memoryTable = new RootTable> - { - Vector = Enumerable.Range(0, length).Select(i => generator()).ToArray(), - Inner = new InnerTable> - { - Vector = Enumerable.Range(0, length).Select(i => generator()).ToArray(), - } - }; - - Span memory = new byte[10240]; - int offset = FlatBufferSerializer.Default.Serialize(memoryTable, memory); - var memoryTableResult = FlatBufferSerializer.Default.Parse>>(memory.Slice(0, offset).ToArray()); - var resultVector = memoryTableResult.Vector; - Assert.Equal(length, resultVector.Length); - for (int i = 0; i < memoryTableResult.Vector.Length; ++i) - { - Assert.Equal(memoryTable.Vector.Span[i], resultVector.Span[i]); - } - } - - if (typeof(T) == typeof(byte)) - { - var memoryTable = new RootTable> - { - Vector = Enumerable.Range(0, length).Select(i => generator()).ToArray(), - Inner = new InnerTable> - { - Vector = Enumerable.Range(0, length).Select(i => generator()).ToArray(), - } - }; - - Span memory = new byte[10240]; - int offset = FlatBufferSerializer.Default.Serialize(memoryTable, memory); - var memoryTableResult = FlatBufferSerializer.Default.Parse>>(memory.Slice(0, offset).ToArray()); - var resultVector = memoryTableResult.Vector; - Assert.Equal(length, resultVector.Length); - for (int i = 0; i < memoryTableResult.Vector.Length; ++i) - { - Assert.Equal(memoryTable.Vector.Span[i], resultVector.Span[i]); - } - } - - { - var memoryTable = new RootTable> - { - Vector = Enumerable.Range(0, length).Select(i => generator()).ToArray(), - Inner = new InnerTable> - { - Vector = Enumerable.Range(0, length).Select(i => generator()).ToArray(), - } - }; - - Span memory = new byte[10240]; - int offset = FlatBufferSerializer.Default.Serialize(memoryTable, memory); - var memoryTableResult = FlatBufferSerializer.Default.Parse>>(memory.Slice(0, offset).ToArray()); - var resultVector = memoryTableResult.Vector; - Assert.Equal(length, resultVector.Count); - for (int i = 0; i < memoryTableResult.Vector.Count; ++i) - { - Assert.Equal(memoryTable.Vector[i], resultVector[i]); - } - } - - { - var memoryTable = new RootTable> - { - Vector = Enumerable.Range(0, length).Select(i => generator()).ToArray(), - Inner = new InnerTable> - { - Vector = Enumerable.Range(0, length).Select(i => generator()).ToArray(), - } - }; - - Span memory = new byte[10240]; - int offset = FlatBufferSerializer.Default.Serialize(memoryTable, memory); - var memoryTableResult = FlatBufferSerializer.Default.Parse>>(memory.Slice(0, offset).ToArray()); - var resultVector = memoryTableResult.Vector; - Assert.Equal(length, resultVector.Count); - for (int i = 0; i < memoryTableResult.Vector.Count; ++i) - { - Assert.Equal(memoryTable.Vector[i], resultVector[i]); - } - } - } - - [FlatBufferEnum(typeof(long))] - public enum SimpleEnum : long - { - Foo = 0, - Bar = 1, - } - - [FlatBufferTable] - public class RootTable - { - [FlatBufferItem(0)] - public virtual TVector? Vector { get; set; } - - [FlatBufferItem(1)] - public virtual InnerTable? Inner { get; set; } - } - - [FlatBufferTable] - public class InnerTable - { - [FlatBufferItem(0)] - public virtual TVector? Vector { get; set; } - } -} diff --git a/src/Tests/FlatSharpTests/SerializationTests/TableSerializationTests.cs b/src/Tests/FlatSharpTests/SerializationTests/TableSerializationTests.cs deleted file mode 100644 index 8929cd08..00000000 --- a/src/Tests/FlatSharpTests/SerializationTests/TableSerializationTests.cs +++ /dev/null @@ -1,149 +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.Linq; -using System.Runtime; - -namespace FlatSharpTests; - -/// -/// Verifies expected binary formats for test data. -/// - -public class TableSerializationTests -{ - [Fact] - public void RootTableNull() - { - Assert.Throws(() => FlatBufferSerializer.Default.Serialize(null, new byte[1024])); - } - - [Fact] - public void TableParse_NotMutable() - { - var table = this.SerializeAndParse(FlatBufferDeserializationOption.Lazy, out _); - - Assert.Throws(() => table.String = null); - Assert.Throws(() => table.Struct = null); - Assert.Throws(() => table.StructVector = new List()); - Assert.Throws(() => table.StructVector.Add(null)); - - //Assert.Contains("FlatBufferVectorBase", table.StructVector.GetType().FullName); - } - - [Fact] - public void TableParse_Progressive() - { - var table = this.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out _); - - Assert.Throws(() => table.String = string.Empty); - Assert.Throws(() => table.Struct.Long = 0); - Assert.Throws(() => table.Struct = new()); - - //Assert.Contains("FlatBufferProgressiveVector", table.StructVector.GetType().FullName); - } - - [Fact] - public void TableParse_GreedyImmutable() - { - var table = this.SerializeAndParse(FlatBufferDeserializationOption.Greedy, out var buffer); - - bool reaped = false; - for (int i = 0; i < 5; ++i) - { - GC.Collect(2, GCCollectionMode.Forced); - GCSettings.LatencyMode = GCLatencyMode.Batch; - if (!buffer.TryGetTarget(out _)) - { - reaped = true; - break; - } - } - - Assert.True(reaped, "GC did not reclaim underlying byte buffer."); - - // The buffer has been collected. Now verify that we can read all the data as - // we expect. This demonstrates that we've copied, as well as that we've - // released references. - Assert.NotNull(table.String); - Assert.NotNull(table.Struct); - Assert.NotNull(table.StructVector); - - Assert.Equal("hi", table.String); - Assert.Equal(1, table.Struct.Byte); - Assert.Equal(2, table.Struct.Long); - Assert.Equal(3u, table.Struct.Uint); - - Assert.Equal(4, table.StructVector[0].Byte); - Assert.Equal(5, table.StructVector[0].Long); - Assert.Equal(6u, table.StructVector[0].Uint); - } - - private SimpleTable SerializeAndParse(FlatBufferDeserializationOption option, out WeakReference buffer) - { - SimpleTable table = new SimpleTable - { - String = "hi", - Struct = new SimpleStruct { Byte = 1, Long = 2, Uint = 3 }, - StructVector = new List { new SimpleStruct { Byte = 4, Long = 5, Uint = 6 } }, - }; - - var serializer = FlatBufferSerializer.CompileFor(table).WithSettings(s => s.UseDeserializationMode(option)); - - var rawBuffer = new byte[1024]; - serializer.Write(rawBuffer, table); - buffer = new WeakReference(rawBuffer); - - return serializer.Parse(rawBuffer); - } - - [FlatBufferTable] - public class SimpleTable - { - [FlatBufferItem(0)] - public virtual string? String { get; set; } - - [FlatBufferItem(1)] - public virtual SimpleStruct? Struct { get; set; } - - [FlatBufferItem(2)] - public virtual IList? StructVector { get; set; } - - [FlatBufferItem(4)] - public virtual SimpleTable? InnerTable { get; set; } - - [FlatBufferItem(5, DefaultValue = 0L)] - public virtual long NoSetter { get; } - } - - [FlatBufferStruct] - public class SimpleStruct - { - [FlatBufferItem(0)] - public virtual long Long { get; set; } - - [FlatBufferItem(1)] - public virtual byte Byte { get; set; } - - [FlatBufferItem(2)] - public virtual uint Uint { get; set; } - } - - [FlatBufferTable] - public class EmptyTable - { - } -} diff --git a/src/Tests/FlatSharpTests/SerializationTests/UnionTests.cs b/src/Tests/FlatSharpTests/SerializationTests/UnionTests.cs deleted file mode 100644 index 77522bd8..00000000 --- a/src/Tests/FlatSharpTests/SerializationTests/UnionTests.cs +++ /dev/null @@ -1,198 +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.Linq; - -namespace FlatSharpTests; - -/// -/// Verifies expected binary formats for test data. -/// -public class UnionTests -{ - [Fact] - public void Union_Default_Fails() - { - UnionTable table = new() - { - Item = default(FlatBufferUnion) - }; - - Assert.Throws(() => FlatBufferSerializer.Default.GetMaxSize(table)); - Assert.Throws(() => FlatBufferSerializer.Default.Serialize(table, new byte[1024])); - } - - [Fact] - public void Union_2Items_TableAndStruct_RoundTrip() - { - var expectedStruct = new SimpleStruct { Long = 123 }; - var expectedTable = new SimpleTable { Int = 456 }; - - var testStruct = CreateAndDeserialize1(expectedStruct, expectedTable); - Assert.Equal(123, testStruct.Long); - - var testTable = CreateAndDeserialize2(expectedStruct, expectedTable); - Assert.Equal(456, testTable.Int); - } - - [Fact] - public void Union_2Items_Struct_SerializationTest() - { - byte[] expectedData = - { - 4, 0, 0, 0, // offset to table - 246, 255, 255, 255, // soffset to vtable - 16, 0, 0, 0, // uoffset_t to struct data - 2, 0, // discriminator (1 byte), padding - 8, 0, // vtable length - 9, 0, // table length - 8, 0, // discriminator offset - 4, 0, // value offset - - 0, 0, // padding - - // struct data - 123, 0, 0, 0, 0, 0, 0, 0 - }; - - var union = new UnionTable - { - Item = new FlatBufferUnion(new SimpleStruct { Long = 123 }) - }; - - Span data = new byte[100]; - int count = FlatBufferSerializer.Default.Serialize(union, data); - - Assert.True(expectedData.AsSpan().SequenceEqual(data.Slice(0, count))); - } - - [Fact] - public void Union_2Items_Table_SerializationTest() - { - byte[] expectedData = - { - 4, 0, 0, 0, // offset to table - 246, 255, 255, 255, // soffset to vtable - 16, 0, 0, 0, // uoffset_t to table data - 1, 0, // discriminator (1 byte), padding - 8, 0, // vtable length - 9, 0, // table length - 8, 0, // discriminator offset - 4, 0, // value offset - 0, 0, // padding - - // table data - 248, 255, 255, 255, // soffset to vtable - 123, 0, 0, 0, // value of index 0 - 6, 0, // vtable length - 8, 0, // table length - 4, 0 // offset of index 0 - }; - - var union = new UnionTable - { - Item = new FlatBufferUnion(new SimpleTable { Int = 123 }) - }; - - Span data = new byte[100]; - int count = FlatBufferSerializer.Default.Serialize(union, data); - - Assert.True(expectedData.AsSpan().SequenceEqual(data.Slice(0, count))); - } - - [Fact] - public void Union_2Items_StringAndTable_RoundTrip() - { - var expectedString = "foobar"; - var expectedInt = 123; - var expectedTable = new SimpleTable { Int = expectedInt }; - - Assert.Equal(expectedString, CreateAndDeserialize1(expectedString, expectedTable)); - Assert.Equal(expectedInt, CreateAndDeserialize2(expectedString, expectedTable).Int); - } - - [Fact] - public void Union_2Items_MultipleStructs_RoundTrip() - { - var simpleStruct1 = new SimpleStruct { Long = 3 }; - var simpleStruct2 = new SimpleStruct2 { Long = 4 }; - - Assert.Equal(simpleStruct1.Long, CreateAndDeserialize1(simpleStruct1, simpleStruct2).Long); - Assert.Equal(simpleStruct2.Long, CreateAndDeserialize2(simpleStruct1, simpleStruct2).Long); - } - - private static T1 CreateAndDeserialize1(T1 one, T2 two) - { - var table = new UnionTable - { - Item = new FlatBufferUnion(one) - }; - - byte[] buffer = new byte[1024]; - - FlatBufferSerializer.Default.Serialize(table, buffer); - - var parseResult = FlatBufferSerializer.Default.Parse>(buffer); - Assert.Equal(1, parseResult.Item.Value.Discriminator); - - return parseResult.Item.Value.Item1; - } - - private static T2 CreateAndDeserialize2(T1 one, T2 two) - { - var table = new UnionTable - { - Item = new FlatBufferUnion(two) - }; - - byte[] buffer = new byte[1024]; - - FlatBufferSerializer.Default.Serialize(table, buffer); - - var parseResult = FlatBufferSerializer.Default.Parse>(buffer); - Assert.Equal(2, parseResult.Item.Value.Discriminator); - - return parseResult.Item.Value.Item2; - } - - [FlatBufferTable] - public class UnionTable - { - [FlatBufferItem(0)] - public virtual FlatBufferUnion? Item { get; set; } - } - - [FlatBufferTable] - public class SimpleTable - { - [FlatBufferItem(0)] - public virtual int Int { get; set; } - } - - [FlatBufferStruct] - public class SimpleStruct - { - [FlatBufferItem(0)] - public virtual long Long { get; set; } - } - - [FlatBufferStruct] - public class SimpleStruct2 - { - [FlatBufferItem(0)] - public virtual long Long { get; set; } - } -} diff --git a/src/Tests/FlatSharpTests/SerializationTests/ValueStructTests.cs b/src/Tests/FlatSharpTests/SerializationTests/ValueStructTests.cs deleted file mode 100644 index 6f0d4f68..00000000 --- a/src/Tests/FlatSharpTests/SerializationTests/ValueStructTests.cs +++ /dev/null @@ -1,871 +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. - */ - -using System.Linq; -using System.Runtime.InteropServices; -using FlatSharp.CodeGen; -using FlatSharp.TypeModel; - -namespace FlatSharpTests; - -/// -/// Tests for ByValue structs. -/// -public class ValueStructTests -{ - private static readonly FlatBufferSerializer memoryMarshalEnabled = new FlatBufferSerializer(new FlatBufferSerializerOptions { EnableValueStructMemoryMarshalDeserialization = true }); - private static readonly FlatBufferSerializer memoryMarshalDisabled = new FlatBufferSerializer(new FlatBufferSerializerOptions { EnableValueStructMemoryMarshalDeserialization = false }); - - public class TypeModel - { - [Theory] - [InlineData(typeof(ValidStruct_Marshallable), true, true)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), true, true)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), false, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), false, false)] - public void ValidStructs(Type type, bool marshalSerialize, bool marshalParse) - { - ITypeModel typeModel = RuntimeTypeModel.CreateFrom(type); - var tm = Assert.IsType(typeModel); - - var children = tm.Children.ToArray(); - Assert.Equal(typeof(byte), children[0].ClrType); - Assert.Equal(typeof(short), children[1].ClrType); - Assert.Equal(typeof(int), children[2].ClrType); - Assert.Equal(typeof(long), children[3].ClrType); - Assert.Equal(typeof(ValidStruct_Inner), children[4].ClrType); - Assert.Equal(marshalSerialize, tm.CanMarshalOnSerialize); - Assert.Equal(marshalParse, tm.CanMarshalOnParse); - } - - [Theory] - [InlineData(typeof(MarshalAlways), true, true, true)] - [InlineData(typeof(MarshalParse), true, false, true)] - [InlineData(typeof(MarshalSerialize), true, true, false)] - [InlineData(typeof(MarshalNever), true, false, false)] - [InlineData(typeof(MarshalDefault_Simple), true, false, false)] // simple structs are not marshalled. - [InlineData(typeof(MarshalDefault_Complex_DueToInnerStruct), true, true, true)] - [InlineData(typeof(MarshalDefault_Complex_DueToLotsOfFields), true, true, true)] - [InlineData(typeof(MarshalAlways), false, true, true)] - [InlineData(typeof(MarshalParse), false, false, true)] - [InlineData(typeof(MarshalSerialize), false, true, false)] - [InlineData(typeof(MarshalNever), false, false, false)] - [InlineData(typeof(MarshalDefault_Simple), false, false, false)] // simple structs are not marshalled. - [InlineData(typeof(MarshalDefault_Complex_DueToInnerStruct), false, true, true)] - [InlineData(typeof(MarshalDefault_Complex_DueToLotsOfFields), false, true, true)] - public void MarshalConfigTests(Type type, bool marshalEnable, bool marshalSerialize, bool marshalParse) - { - ITypeModel typeModel = RuntimeTypeModel.CreateFrom(type); - var tm = Assert.IsType(typeModel); - - Assert.Equal(marshalSerialize, tm.CanMarshalOnSerialize); - Assert.Equal(marshalParse, tm.CanMarshalOnParse); - - var options = new FlatBufferSerializerOptions { EnableValueStructMemoryMarshalDeserialization = marshalEnable }; - - CodeGeneratedMethod serializeMethod = tm.CreateSerializeMethodBody(ContextHelpers.CreateSerializeContext(options)); - CodeGeneratedMethod parseMethod = tm.CreateParseMethodBody(ContextHelpers.CreateParserContext(options)); - - Assert.Equal(parseMethod.MethodBody.Contains("MemoryMarshal.Read"), marshalEnable && marshalParse); - Assert.Equal(serializeMethod.MethodBody.Contains("MemoryMarshal.Write"), marshalEnable && marshalSerialize); - } - - [Theory] - [InlineData(typeof(InvalidStruct_Sequential), "Value struct 'FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_Sequential' must have [StructLayout(LayoutKind.Explicit)] specified.")] - [InlineData(typeof(InvalidStruct_Auto), "Value struct 'FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_Auto' must have [StructLayout(LayoutKind.Explicit)] specified.")] - [InlineData(typeof(InvalidStruct_None), "Value struct 'FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_None' must have [StructLayout(LayoutKind.Explicit)] specified.")] - [InlineData(typeof(InvalidStruct_Empty), "Value struct 'FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_Empty' is empty or has no public fields.")] - [InlineData(typeof(InvalidStruct_NoPublicFields), "Struct 'FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_NoPublicFields' field Foo is not public and does not declare a custom accessor. Non-public fields must also specify a custom accessor.")] - [InlineData(typeof(InvalidStruct_ContainsReferenceType), "Struct 'FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_ContainsReferenceType' field Foo must be a value type if the struct is a value type.")] - [InlineData(typeof(InvalidStruct_WrongFieldOffset), "Struct 'FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_WrongFieldOffset' property 'B' defines invalid [FieldOffset] attribute. Expected: [FieldOffset(8)].")] - [InlineData(typeof(InvalidStruct_NotValidStructMember), "Struct 'FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_NotValidStructMember' field A cannot be part of a flatbuffer struct. Structs may only contain scalars and other structs.")] - [InlineData(typeof(InvalidStruct_NotPublic), "Can't create type model from type FlatSharpTests.ValueStructTests.TypeModel.InvalidStruct_NotPublic because it is not public.")] - public void InvalidStructs(Type structType, string expectedError) - { - var ex = Assert.Throws(() => RuntimeTypeModel.CreateFrom(structType)); - Assert.Equal(expectedError, ex.Message); - } - - [Theory] - [InlineData(typeof(ValidStruct_Marshallable), 20, sizeof(ulong))] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), 20, sizeof(ulong))] - [InlineData(typeof(FiveByteStruct), 5, sizeof(byte))] - [InlineData(typeof(NineByteStruct), 9, sizeof(ulong))] - [InlineData(typeof(SixteenByteStruct), 16, sizeof(ulong))] - public void LayoutTests(Type structType, int expectedSize, int expectedAlignment) - { - ValueStructTypeModel model = (ValueStructTypeModel)RuntimeTypeModel.CreateFrom(structType); - - var layout = Assert.Single(model.PhysicalLayout); - - Assert.False(model.SerializeMethodRequiresContext); - Assert.True(model.SerializesInline); - Assert.True(model.IsValidStructMember); - Assert.True(model.IsValidTableMember); - Assert.True(model.IsValidUnionMember); - Assert.True(model.IsValidVectorMember); - Assert.False(model.IsValidSortedVectorKey); - Assert.True(model.IsFixedSize); - - Assert.Equal(FlatBufferSchemaType.Struct, model.SchemaType); - Assert.Equal( - SerializationHelpers.GetMaxPadding(expectedAlignment) + expectedSize, - model.MaxInlineSize); - - Assert.Equal(expectedSize, layout.InlineSize); - Assert.Equal(expectedAlignment, layout.Alignment); - Assert.Equal(Marshal.SizeOf(structType), layout.InlineSize); - } - - [FlatBufferStruct, StructLayout(LayoutKind.Sequential)] - public struct InvalidStruct_Sequential { public byte A; } - - [FlatBufferStruct, StructLayout(LayoutKind.Auto)] - public struct InvalidStruct_Auto { public byte A; } - - [FlatBufferStruct] - public struct InvalidStruct_None { public byte A; } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - public struct InvalidStruct_Empty { } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - public struct InvalidStruct_NoPublicFields {[FieldOffset(0)] private int Foo; } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - public struct InvalidStruct_ContainsReferenceType {[FieldOffset(0)] public ReferenceStruct Foo; } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - public struct InvalidStruct_WrongFieldOffset {[FieldOffset(0)] public long A;[FieldOffset(1)] public byte B; } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - public struct InvalidStruct_NotValidStructMember {[FieldOffset(0)] public string A; } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - internal struct InvalidStruct_NotPublic {[FieldOffset(0)] public int Foo; } - } - - public class SerializationTests - { - [Theory] - [InlineData(typeof(ValidStruct_Marshallable), true, true)] - [InlineData(typeof(ValidStruct_Marshallable), false, false)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), true, true)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), false, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), true, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), false, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), true, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), false, false)] - public void Serialize_Nullable(Type type, bool enableMarshal, bool expectMarshal) - { - Type tableType = typeof(SimpleTableNullable<>).MakeGenericType(type); - - ISimpleTable simpleTable = (ISimpleTable)Activator.CreateInstance(tableType); - - IValidStruct s = (IValidStruct)Activator.CreateInstance(type); - s.IA = 1; - s.IB = 2; - s.IC = 3; - s.ID = 4; - s.IInner = new ValidStruct_Inner { Test = 5 }; - - simpleTable.Item = s; - - var fbs = GetSerializer(enableMarshal); - - ISerializer serializer = fbs.Compile(simpleTable); - byte[] data = new byte[1024]; - int bytesWritten = serializer.Write(data, simpleTable); - - byte[] expectedData = - { - 4, 0, 0, 0, - 232, 255, 255, 255, - 1, 0, - 2, 0, - 3, 0, 0, 0, - 4, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, - 6, 0, - 24, 0, - 4, 0, - }; - - Assert.Equal(expectedData.Length, bytesWritten); - Assert.True(expectedData.AsSpan().SequenceEqual(data.AsSpan().Slice(0, bytesWritten))); - - Assert.Equal( - expectMarshal, - serializer.GetCSharp().Contains($"MemoryMarshal.Write")); - } - - [Theory] - [InlineData(typeof(ValidStruct_Marshallable), true, true)] - [InlineData(typeof(ValidStruct_Marshallable), false, false)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), true, true)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), false, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), true, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), false, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), true, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), false, false)] - public void Serialize_NonNullable(Type type, bool enableMarshal, bool expectMarshal) - { - Type tableType = typeof(SimpleTableNonNullable<>).MakeGenericType(type); - - ISimpleTable simpleTable = (ISimpleTable)Activator.CreateInstance(tableType); - - IValidStruct s = (IValidStruct)Activator.CreateInstance(type); - s.IA = 1; - s.IB = 2; - s.IC = 3; - s.ID = 4; - s.IInner = new ValidStruct_Inner { Test = 5 }; - - simpleTable.Item = s; - - var fbs = GetSerializer(enableMarshal); - - ISerializer serializer = fbs.Compile(simpleTable); - byte[] data = new byte[1024]; - int bytesWritten = serializer.Write(data, simpleTable); - - byte[] expectedData = - { - 4, 0, 0, 0, - 232, 255, 255, 255, - 1, 0, - 2, 0, - 3, 0, 0, 0, - 4, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, - 6, 0, - 24, 0, - 4, 0, - }; - - Assert.Equal(expectedData.Length, bytesWritten); - Assert.True(expectedData.AsSpan().SequenceEqual(data.AsSpan().Slice(0, bytesWritten))); - - Assert.Equal( - expectMarshal, - serializer.GetCSharp().Contains($"MemoryMarshal.Write")); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Serialize_NineByte(bool marshal) - { - byte[] data = - { - 4, 0, 0, 0, - 242, 255, 255, 255, - 1, 0, 0, 0, 0, 0, 0, 0, - 2, - 0, - 6, 0, - 13, 0, - 4, 0, - }; - - var table = new SimpleTableAnything - { - Item = new NineByteStruct - { - A = 1, - B = 2, - } - }; - - byte[] buffer = new byte[1024]; - - var serializer = GetSerializer(marshal); - int bytesWritten = serializer.Serialize(table, buffer); - - Assert.True(data.AsSpan().SequenceEqual(buffer.AsSpan().Slice(0, bytesWritten))); - Assert.Equal(marshal, serializer.Compile(table.GetType()).GetCSharp().Contains("MemoryMarshal.Write")); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Serialize_FiveByte(bool marshal) - { - byte[] data = - { - 4, 0, 0, 0, - 246, 255, 255, 255, - 1, 2, 3, 4, 5, 0, - 6, 0, - 9, 0, - 4, 0, - }; - - SimpleTableAnything source = new() - { - Item = new FiveByteStruct - { - A = 1, - B = 2, - C = 3, - D = 4, - E = 5, - } - }; - - byte[] buffer = new byte[1024]; - - var serializer = GetSerializer(marshal); - var written = serializer.Serialize(source, buffer); - - Assert.True(data.AsSpan().SequenceEqual(buffer.AsSpan().Slice(0, written))); - Assert.Equal(marshal, serializer.Compile(source.GetType()).GetCSharp().Contains("MemoryMarshal.Write")); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Serialize_SixteenByte(bool marshal) - { - byte[] data = - { - 4, 0, 0, 0, - 236, 255, 255, 255, - 1, 0, 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 0, 0, 0, - 6, 0, - 20, 0, - 4, 0, - }; - - SimpleTableAnything source = new() - { - Item = new SixteenByteStruct - { - A = 1, - B = 2ul, - } - }; - - byte[] buffer = new byte[1024]; - - var serializer = GetSerializer(marshal); - var written = serializer.Serialize(source, buffer); - - Assert.True(data.AsSpan().SequenceEqual(buffer.AsSpan().Slice(0, written))); - Assert.Equal(marshal, serializer.Compile(source.GetType()).GetCSharp().Contains("MemoryMarshal.Write")); - } - } - - public class ParseTests - { - [Theory] - [InlineData(typeof(ValidStruct_Marshallable), true, true, FlatBufferDeserializationOption.Greedy)] - [InlineData(typeof(ValidStruct_Marshallable), false, false, FlatBufferDeserializationOption.Lazy)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), true, true, FlatBufferDeserializationOption.Progressive)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), false, false, FlatBufferDeserializationOption.GreedyMutable)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), true, false, FlatBufferDeserializationOption.Progressive)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), false, false, FlatBufferDeserializationOption.Lazy)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), true, false, FlatBufferDeserializationOption.Progressive)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), false, false, FlatBufferDeserializationOption.GreedyMutable)] - public void Parse_Nullable(Type type, bool enableMarshal, bool expectMarshal, FlatBufferDeserializationOption option) - { - byte[] data = - { - 4, 0, 0, 0, - 232, 255, 255, 255, - 1, 0, - 2, 0, - 3, 0, 0, 0, - 4, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, - 6, 0, - 24, 0, - 4, 0, - }; - - Type tableType = typeof(SimpleTableNullable<>).MakeGenericType(type); - var fbs = GetSerializer(enableMarshal); - - ISerializer serializer = fbs.Compile(tableType).WithSettings(s => s.UseDeserializationMode(option)); - ISimpleTable table = (ISimpleTable)serializer.Parse(data); - - Assert.Equal( - expectMarshal, - serializer.GetCSharp().Contains($"MemoryMarshal.Read<{type.GetGlobalCompilableTypeName()}>")); - - Assert.Equal(1, table.Item.IA); - Assert.Equal(2, table.Item.IB); - Assert.Equal(3, table.Item.IC); - Assert.Equal(4, table.Item.ID); - Assert.Equal(5, table.Item.IInner.Test); - } - - [Theory] - [InlineData(typeof(ValidStruct_Marshallable), true, true)] - [InlineData(typeof(ValidStruct_Marshallable), false, false)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), true, true)] - [InlineData(typeof(ValidStruct_Marshallable_OutOfOrder), false, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), true, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_DueToSize), false, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), true, false)] - [InlineData(typeof(ValidStruct_NotMarshallable_MissingSizeHint), false, false)] - public void Parse_NonNullable(Type type, bool enableMarshal, bool expectMarshal) - { - byte[] data = - { - 4, 0, 0, 0, - 232, 255, 255, 255, - 1, 0, - 2, 0, - 3, 0, 0, 0, - 4, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, - 6, 0, - 24, 0, - 4, 0, - }; - - Type tableType = typeof(SimpleTableNonNullable<>).MakeGenericType(type); - var fbs = GetSerializer(enableMarshal); - - ISerializer serializer = fbs.Compile(tableType); - ISimpleTable table = (ISimpleTable)serializer.Parse(data); - - Assert.Equal( - expectMarshal, - serializer.GetCSharp().Contains($"MemoryMarshal.Read<{type.GetGlobalCompilableTypeName()}>")); - - Assert.Equal(1, table.Item.IA); - Assert.Equal(2, table.Item.IB); - Assert.Equal(3, table.Item.IC); - Assert.Equal(4, table.Item.ID); - Assert.Equal(5, table.Item.IInner.Test); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Parse_NineByte(bool marshal) - { - byte[] data = - { - 4, 0, 0, 0, - 242, 255, 255, 255, - 1, 0, 0, 0, 0, 0, 0, 0, - 2, - 0, - 6, 0, - 13, 0, - 4, 0, - }; - - var serializer = GetSerializer(marshal); - var parsed = serializer.Parse>(data); - - Assert.NotNull(parsed.Item); - Assert.Equal(1ul, parsed.Item.Value.A); - Assert.Equal(2, parsed.Item.Value.B); - - Assert.Equal(marshal, serializer.Compile>().GetCSharp().Contains("MemoryMarshal.Read")); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Parse_FiveByte(bool marshal) - { - byte[] data = - { - 4, 0, 0, 0, - 246, 255, 255, 255, - 1, 2, 3, 4, 5, 0, - 6, 0, - 9, 0, - 4, 0, - }; - - var serializer = GetSerializer(marshal); - var parsed = serializer.Parse>(data); - - Assert.NotNull(parsed.Item); - Assert.Equal(1, parsed.Item.Value.A); - Assert.Equal(2, parsed.Item.Value.B); - Assert.Equal(3, parsed.Item.Value.C); - Assert.Equal(4, parsed.Item.Value.D); - Assert.Equal(5, parsed.Item.Value.E); - - Assert.Equal(marshal, serializer.Compile>().GetCSharp().Contains("MemoryMarshal.Read")); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Parse_SixteenByte(bool marshal) - { - byte[] data = - { - 4, 0, 0, 0, - 236, 255, 255, 255, - 1, 0, 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 0, 0, 0, - 6, 0, - 20, 0, - 4, 0, - }; - - var serializer = GetSerializer(marshal); - var parsed = serializer.Parse>(data); - - Assert.NotNull(parsed); - Assert.NotNull(parsed.Item); - Assert.Equal(1, parsed.Item.Value.A); - Assert.Equal(2ul, parsed.Item.Value.B); - - Assert.Equal(marshal, serializer.Compile>().GetCSharp().Contains("MemoryMarshal.Read")); - } - } - - public class FormattingTests - { - [Fact] - public void Bool() => RoundTrip(true, (ref ValueStruct_Bool s) => ref s.Value); - - [Fact] - public void Byte() => RoundTrip(byte.MaxValue, (ref ValueStruct_Byte s) => ref s.Value); - - [Fact] - public void SByte() => RoundTrip(sbyte.MinValue, (ref ValueStruct_SByte s) => ref s.Value); - - [Fact] - public void UShort() => RoundTrip(ushort.MaxValue, (ref ValueStruct_UShort s) => ref s.Value); - - [Fact] - public void Short() => RoundTrip(short.MinValue, (ref ValueStruct_Short s) => ref s.Value); - - [Fact] - public void UInt() => RoundTrip(uint.MaxValue, (ref ValueStruct_UInt s) => ref s.Value); - - [Fact] - public void Int() => RoundTrip(int.MinValue, (ref ValueStruct_Int s) => ref s.Value); - - [Fact] - public void ULong() => RoundTrip(ulong.MaxValue, (ref ValueStruct_ULong s) => ref s.Value); - - [Fact] - public void Long() => RoundTrip(long.MinValue, (ref ValueStruct_Long s) => ref s.Value); - - [Fact] - public void Double() => RoundTrip(Math.PI, (ref ValueStruct_Double s) => ref s.Value); - - [Fact] - public void Float() => RoundTrip((float)Math.E, (ref ValueStruct_Float s) => ref s.Value); - - private static void RoundTrip(TValue value, GetValue getter) - where TValue : struct - where TStruct : struct - { - SimpleTableAnything> refTable = new() - { - Item = new() { Item = value } - }; - - TStruct valueItem = default; - getter(ref valueItem) = value; - - SimpleTableAnything valueTable = new() - { - Item = valueItem - }; - - ValueStructTypeModel vstm = (ValueStructTypeModel)RuntimeTypeModel.CreateFrom(typeof(TStruct)); - Assert.True(vstm.CanMarshalOnSerialize); - Assert.True(vstm.CanMarshalOnParse); - - byte[] referenceBuffer = new byte[1024]; - byte[] referenceBufferNull = new byte[1024]; - byte[] valueMarshalBufferNull = new byte[1024]; - byte[] valueNonMarshalBufferNull = new byte[1024]; - byte[] valueMarhalBuffer = new byte[1024]; - byte[] valueNonMarhalBuffer = new byte[1024]; - - var marshalSerializer = GetSerializer(true); - var nonMarshalSerializer = GetSerializer(false); - - int refNullBytesWritten = FlatBufferSerializer.Default.Serialize(new SimpleTableAnything>(), referenceBufferNull); - int valueNullMarshalBytesWritten = marshalSerializer.Serialize(new SimpleTableAnything(), valueMarshalBufferNull); - int valueNullNonMarshalBytesWritten = nonMarshalSerializer.Serialize(new SimpleTableAnything(), valueNonMarshalBufferNull); - - int refBytesWritten = FlatBufferSerializer.Default.Serialize(refTable, referenceBuffer); - int valueMarshalBytesWritten = marshalSerializer.Serialize(valueTable, valueMarhalBuffer); - int valueNonMarshalBytesWritten = nonMarshalSerializer.Serialize(valueTable, valueNonMarhalBuffer); - - Assert.Equal(refBytesWritten, valueMarshalBytesWritten); - Assert.Equal(refBytesWritten, valueNonMarshalBytesWritten); - - Assert.True(referenceBuffer.SequenceEqual(valueMarhalBuffer)); - Assert.True(referenceBuffer.SequenceEqual(valueNonMarhalBuffer)); - - Assert.Equal(refNullBytesWritten, valueNullMarshalBytesWritten); - Assert.Equal(refNullBytesWritten, valueNullNonMarshalBytesWritten); - - Assert.True(referenceBufferNull.SequenceEqual(valueMarshalBufferNull)); - Assert.True(referenceBufferNull.SequenceEqual(valueNonMarshalBufferNull)); - - var parsed = FlatBufferSerializer.Default.Parse>(valueMarhalBuffer); - TStruct temp = parsed.Item; - Assert.Equal(value, getter(ref temp)); - - parsed = FlatBufferSerializer.Default.Parse>(referenceBufferNull); - temp = parsed.Item; - Assert.Equal(default(TValue), getter(ref temp)); - } - - public delegate ref TValue GetValue(ref TStruct @struct) where TStruct : struct; - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_Bool {[FieldOffset(0)] public bool Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_Byte {[FieldOffset(0)] public byte Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_SByte {[FieldOffset(0)] public sbyte Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_UShort {[FieldOffset(0)] public ushort Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_Short {[FieldOffset(0)] public short Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_UInt {[FieldOffset(0)] public uint Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_Int {[FieldOffset(0)] public int Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_ULong {[FieldOffset(0)] public ulong Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_Long {[FieldOffset(0)] public long Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_Float {[FieldOffset(0)] public float Value; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct ValueStruct_Double {[FieldOffset(0)] public double Value; } - - [FlatBufferStruct] - public class GenericReferenceStruct where T : struct - { - [FlatBufferItem(0)] - public virtual T Item { get; set; } - } - } - - private static FlatBufferSerializer GetSerializer(bool enableMemoryMarshal) - { - return enableMemoryMarshal ? memoryMarshalEnabled : memoryMarshalDisabled; - } - - - public interface IValidStruct - { - byte IA { get; set; } - short IB { get; set; } - int IC { get; set; } - long ID { get; set; } - ValidStruct_Inner IInner { get; set; } - } - - public interface ISimpleTable - { - IValidStruct? Item { get; set; } - } - - [FlatBufferTable] - public class SimpleTableAnything - { - [FlatBufferItem(0)] - public virtual T? Item { get; set; } - } - - [FlatBufferTable] - public class SimpleTableNonNullable : ISimpleTable - where T : struct, IValidStruct - { - [FlatBufferItem(0)] - public virtual T Item { get; set; } - - IValidStruct? ISimpleTable.Item { get => this.Item; set => this.Item = (T)value; } - } - - [FlatBufferTable] - public class SimpleTableNullable : ISimpleTable - where T : struct, IValidStruct - { - [FlatBufferItem(0)] - public virtual T? Item { get; set; } - - IValidStruct? ISimpleTable.Item { get => this.Item; set => this.Item = (T?)value; } - } - - [FlatBufferStruct] - public class ReferenceStruct - { - [FlatBufferItem(0)] - public virtual int Foo { get; set; } - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - public struct ValidStruct_Inner - { - [FieldOffset(0)] public int Test; - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit, Size = 20)] - public struct ValidStruct_Marshallable : IValidStruct - { - [FieldOffset(0)] public byte A; - [FieldOffset(2)] public short B; - [FieldOffset(4)] public int C; - [FieldOffset(8)] public long D; - [FieldOffset(16)] public ValidStruct_Inner Inner; - - public byte IA { get => this.A; set => this.A = value; } - public short IB { get => this.B; set => this.B = value; } - public int IC { get => this.C; set => this.C = value; } - public long ID { get => this.D; set => this.D = value; } - public ValidStruct_Inner IInner { get => this.Inner; set => this.Inner = value; } - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit, Size = 20)] - public struct ValidStruct_Marshallable_OutOfOrder : IValidStruct - { - [FieldOffset(16)] public ValidStruct_Inner Inner; - [FieldOffset(4)] public int C; - [FieldOffset(8)] public long D; - [FieldOffset(2)] public short B; - [FieldOffset(0)] public byte A; - - public byte IA { get => this.A; set => this.A = value; } - public short IB { get => this.B; set => this.B = value; } - public int IC { get => this.C; set => this.C = value; } - public long ID { get => this.D; set => this.D = value; } - public ValidStruct_Inner IInner { get => this.Inner; set => this.Inner = value; } - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - public struct ValidStruct_NotMarshallable_MissingSizeHint : IValidStruct - { - [FieldOffset(0)] public byte A; - [FieldOffset(2)] public short B; - [FieldOffset(4)] public int C; - [FieldOffset(8)] public long D; - [FieldOffset(16)] public ValidStruct_Inner Inner; - - public byte IA { get => this.A; set => this.A = value; } - public short IB { get => this.B; set => this.B = value; } - public int IC { get => this.C; set => this.C = value; } - public long ID { get => this.D; set => this.D = value; } - public ValidStruct_Inner IInner { get => this.Inner; set => this.Inner = value; } - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit, Size = 100)] - public struct ValidStruct_NotMarshallable_DueToSize : IValidStruct - { - [FieldOffset(0)] public byte A; - [FieldOffset(2)] public short B; - [FieldOffset(4)] public int C; - [FieldOffset(8)] public long D; - [FieldOffset(16)] public ValidStruct_Inner Inner; - - public byte IA { get => this.A; set => this.A = value; } - public short IB { get => this.B; set => this.B = value; } - public int IC { get => this.C; set => this.C = value; } - public long ID { get => this.D; set => this.D = value; } - public ValidStruct_Inner IInner { get => this.Inner; set => this.Inner = value; } - } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit, Size = 16)] - public struct SixteenByteStruct - { - [FieldOffset(0)] public byte A; - [FieldOffset(8)] public ulong B; - } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit, Size = 9)] - public struct NineByteStruct - { - [FieldOffset(0)] public ulong A; - [FieldOffset(8)] public byte B; - } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit, Size = 5)] - public struct FiveByteStruct - { - [FieldOffset(0)] public byte A; - [FieldOffset(1)] public byte B; - [FieldOffset(2)] public byte C; - [FieldOffset(3)] public byte D; - [FieldOffset(4)] public byte E; - } - - // Small struct that shouldn't marshal (but we are making it). - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Always), StructLayout(LayoutKind.Explicit)] - public struct MarshalAlways {[FieldOffset(0)] public byte A; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Serialize), StructLayout(LayoutKind.Explicit)] - public struct MarshalSerialize {[FieldOffset(0)] public byte A; } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Parse), StructLayout(LayoutKind.Explicit)] - public struct MarshalParse {[FieldOffset(0)] public byte A; } - - // Complex struct that should marshal (but we are not letting it). - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Never), StructLayout(LayoutKind.Explicit)] - public struct MarshalNever - { - [FieldOffset(0)] public byte A; - [FieldOffset(1)] public byte B; - [FieldOffset(2)] public byte C; - [FieldOffset(3)] public byte D; - [FieldOffset(4)] public byte E; - } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Default), StructLayout(LayoutKind.Explicit)] - public struct MarshalDefault_Simple - { - [FieldOffset(0)] public byte A; - [FieldOffset(1)] public byte B; - [FieldOffset(2)] public byte C; - } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Default), StructLayout(LayoutKind.Explicit)] - public struct MarshalDefault_Complex_DueToInnerStruct - { - [FieldOffset(0)] public FiveByteStruct A; - } - - [FlatBufferStruct(MemoryMarshalBehavior = MemoryMarshalBehavior.Default), StructLayout(LayoutKind.Explicit)] - public struct MarshalDefault_Complex_DueToLotsOfFields - { - [FieldOffset(0)] public byte A; - [FieldOffset(1)] public byte B; - [FieldOffset(2)] public byte C; - [FieldOffset(3)] public byte D; - } -} diff --git a/src/Tests/FlatSharpTests/SerializationTests/VectorDeserializationTests.cs b/src/Tests/FlatSharpTests/SerializationTests/VectorDeserializationTests.cs deleted file mode 100644 index 86952243..00000000 --- a/src/Tests/FlatSharpTests/SerializationTests/VectorDeserializationTests.cs +++ /dev/null @@ -1,650 +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.Collections.ObjectModel; -using System.Drawing; -using System.Linq; -using System.Runtime.InteropServices; -using FlatSharp.TypeModel; -using Unity.Collections; - -namespace FlatSharpTests; - -/// -/// Binary format testing for vector deserialization. -/// -public class VectorDeserializationTests -{ - [Fact] - public void NullListVector() - { - byte[] data = - { - 8, 0, 0, 0, // offset to table start - 4, 0, // vtable length - 4, 0, // table length - 4, 0, 0, 0 // soffset to vtable - }; - - var item = FlatBufferSerializer.Default.Parse>>(data); - Assert.Null(item.Vector); - } - - [Fact] - public void EmptyListVector() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 0, 0, 0, 0, // vector length - }; - - var item = FlatBufferSerializer.Default.Parse>>(data); - Assert.Equal(0, item.Vector.Count); - } - - [Fact] - public void SimpleListVector() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 3, 0, 0, 0, // vector length, - - // vector data - 1, 0, - 2, 0, - 3, 0 - }; - - var item = FlatBufferSerializer.Default.Parse>>(data); - - Assert.Equal(3, item.Vector.Count); - Assert.Equal(1, item.Vector[0]); - Assert.Equal(2, item.Vector[1]); - Assert.Equal(3, item.Vector[2]); - } - - [Fact] - public void EmptyMemoryVector() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 0, 0, 0, 0, // vector length - }; - - var item = FlatBufferSerializer.Default.Parse>>(data); - Assert.True(item.Vector.IsEmpty); - } - - [Fact] - public void SimpleMemoryVector() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 3, 0, 0, 0, // vector length - 1, 2, 3, // True false true - }; - - var item = FlatBufferSerializer.Default.Parse>>(data); - Assert.Equal(3, item.Vector.Length); - Assert.Equal((byte)1, item.Vector.Span[0]); - Assert.Equal((byte)2, item.Vector.Span[1]); - Assert.Equal((byte)3, item.Vector.Span[2]); - } - - [Fact] - public void EmptyNativeArrayVector() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 0, 0, 0, 0, // vector length - }; - - var item = FlatBufferSerializer.DefaultWithUnitySupport.Parse>>(data); - Assert.True(item.Vector.Length == 0); - } - - [Fact] - public void SimpleNativeArrayVector() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 3, 0, 0, 0, // vector length - 1, 2, 3, // True false true - }; - - var item = FlatBufferSerializer.DefaultWithUnitySupport.Parse>>(data); - Assert.Equal(3, item.Vector.Length); - Assert.Equal((byte)1, item.Vector[0]); - Assert.Equal((byte)2, item.Vector[1]); - Assert.Equal((byte)3, item.Vector[2]); - } - - [Fact] - public void SimpleNativeArrayVectorOfInt() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 3, 0, 0, 0, // vector length - 1, 0, 0, 0, // vector data - 2, 0, 0, 0, - 3, 0, 0, 0, - }; - - var item = FlatBufferSerializer.DefaultWithUnitySupport.Parse>>(data); - Assert.Equal(3, item.Vector.Length); - Assert.Equal(1, item.Vector[0]); - Assert.Equal(2, item.Vector[1]); - Assert.Equal(3, item.Vector[2]); - } - - [Fact] - public void NullString() - { - byte[] data = - { - 8, 0, 0, 0, // offset to table start - 4, 0, // vtable length - 4, 0, // table length - 4, 0, 0, 0 // soffset to vtable - }; - - var item = FlatBufferSerializer.Default.Parse>(data); - Assert.Null(item.Vector); - } - - [Fact] - public void EmptyString() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 0, 0, 0, 0, // vector length - 0, // null terminator (special case for string vectors). - }; - - var item = FlatBufferSerializer.Default.Parse>(data); - Assert.Equal(string.Empty, item.Vector); - } - - [Fact] - public void SimpleString() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 3, 0, 0, 0, // vector length - 1, 2, 3, 0, // data + null terminator (special case for string vectors). - }; - - var item = FlatBufferSerializer.Default.Parse>(data); - Assert.Equal(3, item.Vector.Length); - Assert.Equal(new string(new char[] { (char)1, (char)2, (char)3 }), item.Vector); - } - - [Fact] - public void NullArray() - { - byte[] data = - { - 8, 0, 0, 0, // offset to table start - 4, 0, // vtable length - 4, 0, // table length - 4, 0, 0, 0 // soffset to vtable - }; - - var item = FlatBufferSerializer.Default.Parse>>(data); - Assert.Null(item.Vector); - } - - [Fact] - public void EmptyArray() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 0, 0, 0, 0, // vector length - }; - - // we parse non-scalar array and scalar arrays differently, so test for both. - var item = FlatBufferSerializer.Default.Parse>>(data); - Assert.Empty(item.Vector); - - var stringItem = FlatBufferSerializer.Default.Parse>>(data); - Assert.Empty(stringItem.Vector); - } - - [Fact] - public void SimpleArray() - { - byte[] data = - { - 12, 0, 0, 0, // offset to table start - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 8, 0, 0, 0, // soffset to vtable - 4, 0, 0, 0, // uoffset_t to vector - 3, 0, 0, 0, // vector length - - // vector data - 1, 0, 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, 0, 0, - }; - - var item = FlatBufferSerializer.Default.Parse>>(data); - Assert.Equal(3, item.Vector.Count); - Assert.Equal(1L, item.Vector[0]); - Assert.Equal(2L, item.Vector[1]); - Assert.Equal(3L, item.Vector[2]); - - var item2 = FlatBufferSerializer.Default.Parse>>(data); - Assert.Equal(3, item2.Vector.Count); - Assert.Equal(1L, item2.Vector[0]); - Assert.Equal(2L, item2.Vector[1]); - Assert.Equal(3L, item2.Vector[2]); - } - - [Fact] - public void StringVector_GreedyDeserialize_Mutable() - { - RootTable> root = new RootTable> - { - Vector = new List { "one", "two", "three" } - }; - - var serializer = FlatBufferSerializer.CompileFor(root).WithSettings(s => s.UseGreedyMutableDeserialization()); - - byte[] buffer = new byte[100]; - serializer.Write(buffer, root); - - var parsed = serializer.Parse>>(buffer); - - // Assert.Equal(typeof(PoolableList), parsed.Vector.GetType()); - Assert.False(parsed.Vector.IsReadOnly); - - // Shouldn't throw. - parsed.Vector.Add("four"); - } - - [Fact] - public void StringVector_GreedyDeserialize_NotMutable() - { - RootTable> root = new RootTable> - { - Vector = new List { "one", "two", "three" } - }; - - var serializer = FlatBufferSerializer.CompileFor(root).WithSettings(s => s.UseGreedyDeserialization()); - - byte[] buffer = new byte[100]; - serializer.Write(buffer, root); - - var parsed = serializer.Parse>>(buffer); - - //Assert.Equal(typeof(ImmutableList), parsed.Vector.GetType()); - Assert.True(parsed.Vector.IsReadOnly); - - Assert.Throws(() => parsed.Vector.Add("four")); - } - - [Fact] - public void MemoryVector_GreedyDeserialize() - { - RootTable> root = new RootTable>() - { - Vector = new Memory(new byte[100]) - }; - - root.Vector.Span.Fill(1); - - byte[] buffer = new byte[1024]; - - var serializer = FlatBufferSerializer.CompileFor(root).WithSettings(s => s.UseGreedyDeserialization()); - - serializer.Write(buffer, root); - - var parsed1 = serializer.Parse>>(buffer); - var parsed2 = serializer.Parse>>(buffer); - - Assert.Equal((byte)1, parsed1.Vector.Span[0]); - Assert.Equal((byte)1, parsed2.Vector.Span[0]); - - // Assert that this change affects only the 'parsed1' object. - parsed1.Vector.Span[0] = 2; - - Assert.Equal((byte)2, parsed1.Vector.Span[0]); - Assert.Equal((byte)1, parsed2.Vector.Span[0]); - } - - /// - /// Asserts that when greedy parsing is off, a change to the memory of the parsed object represents a change in the buffer. - /// - [Fact] - public void MemoryVector_LazyDeserialize() - { - RootTable> root = new RootTable>() - { - Vector = new Memory(new byte[100]) - }; - - root.Vector.Span.Fill(1); - - byte[] buffer = new byte[1024]; - var serializer = FlatBufferSerializer.CompileFor(root).WithSettings(s => s.UseLazyDeserialization()); - - serializer.Write(buffer.AsSpan(), root); - - var parsed1 = serializer.Parse>>(buffer); - var parsed2 = serializer.Parse>>(buffer); - - Assert.Equal((byte)1, parsed1.Vector.Span[0]); - Assert.Equal((byte)1, parsed2.Vector.Span[0]); - - // Asser that this change affects both objects. - parsed1.Vector.Span[0] = 2; - - Assert.Equal((byte)2, parsed1.Vector.Span[0]); - Assert.Equal((byte)2, parsed2.Vector.Span[0]); - } - - /// - /// Asserts that when greedy parsing is off, a change to the memory of the parsed object represents a change in the buffer. - /// - [Fact] - public void MemoryVector_ProgressiveDeserialize() - { - RootTable> root = new RootTable>() - { - Vector = new Memory(new byte[100]) - }; - - root.Vector.Span.Fill(1); - - byte[] buffer = new byte[1024]; - var serializer = FlatBufferSerializer.CompileFor(root).WithSettings(s => s.UseProgressiveDeserialization()); - - serializer.Write(buffer.AsSpan(), root); - - var parsed1 = serializer.Parse>>(buffer); - var parsed2 = serializer.Parse>>(buffer); - - Assert.Equal((byte)1, parsed1.Vector.Span[0]); - Assert.Equal((byte)1, parsed2.Vector.Span[0]); - - // Asser that this change affects both objects. - parsed1.Vector.Span[0] = 2; - - Assert.Equal((byte)2, parsed1.Vector.Span[0]); - Assert.Equal((byte)2, parsed2.Vector.Span[0]); - } - - [Fact] - public void NativeArrayVector_GreedyDeserialize() - { - RootTable> root = new RootTable>() - { - Vector = new NativeArray(new byte[100], Allocator.Temp) - }; - - root.Vector.AsSpan().Fill(1); - - byte[] buffer = new byte[1024]; - var options = new FlatBufferSerializerOptions(FlatBufferDeserializationOption.Greedy); - var serializer = new FlatBufferSerializer(options, TypeModelContainer.CreateDefault().WithUnitySupport(true)); - - serializer.Serialize(root, buffer.AsSpan()); - - var parsed1 = serializer.Parse>>(buffer); - var parsed2 = serializer.Parse>>(buffer); - - Assert.Equal((byte)1, parsed1.Vector[0]); - Assert.Equal((byte)1, parsed2.Vector[0]); - - // Assert that this change affects only the 'parsed1' object. - parsed1.Vector.AsSpan()[0] = 2; - - - Assert.Equal((byte)2, parsed1.Vector[0]); - Assert.Equal((byte)1, parsed2.Vector[0]); - } - - [Fact] - public void NativeArrayVector_LazyDeserialize() - { - RootTable> root = new RootTable>() - { - Vector = new NativeArray(new byte[100], Allocator.Temp) - }; - - root.Vector.AsSpan().Fill(1); - - byte[] buffer = new byte[1024]; - var options = new FlatBufferSerializerOptions(FlatBufferDeserializationOption.Lazy); - var serializer = new FlatBufferSerializer(options, TypeModelContainer.CreateDefault().WithUnitySupport(true)); - - serializer.Serialize(root, buffer.AsSpan()); - - var memory = buffer.AsMemory(); - using (var handle = memory.Pin()) - { - var parsed1 = - serializer.Parse>, MemoryInputBuffer>(new MemoryInputBuffer(memory, true)); - var parsed2 = - serializer.Parse>, MemoryInputBuffer>(new MemoryInputBuffer(memory, true)); - - Assert.Equal((byte)1, parsed1.Vector[0]); - Assert.Equal((byte)1, parsed2.Vector[0]); - - // Asser that this change affects both objects. - parsed1.Vector.AsSpan()[0] = 2; - - Assert.Equal((byte)2, parsed1.Vector[0]); - Assert.Equal((byte)2, parsed2.Vector[0]); - } - } - - [Fact] - public void UnalignedStruct_Value5Byte() - { - byte[] data = - { - 4, 0, 0, 0, // offset to table start - 248, 255, 255, 255, // soffset to vtable (-8) - 12, 0, 0, 0, // uoffset_t to vector - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 3, 0, 0, 0, // vector length - 1, 0, 0, 0, // index 0.Int - 1, // index 0.Byte - 0, 0, 0, // padding - 2, 0, 0, 0, // index 1.Int - 2, // index 1.Byte - 0, 0, 0, // padding - 3, 0, 0, 0, // index2.Int - 3, // Index2.byte - 0, 0, 0, // padding - }; - - var item = FlatBufferSerializer.Default.Parse>>(data); - - Assert.Equal(3, item.Vector.Count); - for (int i = 0; i < 3; ++i) - { - Assert.Equal(i + 1, item.Vector[i].Int); - Assert.Equal((byte)(i + 1), item.Vector[i].Byte); - } - } - - [Fact] - public void VectorOfUnion_List() => this.VectorOfUnionTest>>>>( - FlatBufferDeserializationOption.Progressive, - v => v.Vector.ToArray()); - - [Fact] - public void VectorOfUnion_ReadOnlyList() => this.VectorOfUnionTest>>>>( - FlatBufferDeserializationOption.Lazy, - v => v.Vector.ToArray()); - - private void VectorOfUnionTest( - FlatBufferDeserializationOption option, - Func>[]> getItems) - where V : class, new() - { - byte[] data = - { - 4, 0, 0, 0, - 244, 255, 255, 255, - 16, 0, 0, 0, // uoffset to discriminator vector - 20, 0, 0, 0, // uoffset to offset vector - 8, 0, // vtable - 12, 0, - 4, 0, - 8, 0, - 3, 0, 0, 0, // discriminator vector length - 1, 2, 3, 0, // values + 1 byte padding - 3, 0, 0, 0, // offset vector length - 12, 0, 0, 0, // value 0 - 16, 0, 0, 0, // value 1 - 16, 0, 0, 0, // value 2 - 3, 0, 0, 0, // string length - 102, 111, 111, 0, // foo + null terminator - 3, 0, 0, 0, // struct value ('3') - 248, 255, 255, 255, // table vtable offset - 1, 0, 0, 0, // value of 'key' - 8, 0, // table vtable start - 8, 0, - 0, 0, - 4, 0, - }; - - var serializer = FlatBufferSerializer.Default.Compile().WithSettings(s => s.UseDeserializationMode(option)); - V parsed = serializer.Parse(data); - var items = getItems(parsed); - - Assert.True(items[0].TryGet(out string str)); - Assert.Equal("foo", str); - - Assert.True(items[1].TryGet(out Struct @struct)); - Assert.Equal(3, @struct.Integer); - - Assert.True(items[2].TryGet(out TableWithKey table)); - Assert.Equal(1, table.Key); - Assert.Null(table.Value); - } - - [FlatBufferTable] - public class RootTable - { - [FlatBufferItem(0)] - public virtual TVector? Vector { get; set; } - } - - [FlatBufferStruct] - public class Struct - { - [FlatBufferItem(0)] - public virtual int Integer { get; set; } - } - - [FlatBufferTable] - public class TableWithKey - { - [FlatBufferItem(0)] - public virtual string? Value { get; set; } - - [FlatBufferItem(1, Key = true)] - public virtual TKey? Key { get; set; } - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit, Size = 5)] - public struct ValueFiveByteStruct - { - [FieldOffset(0)] public int Int; - - [FieldOffset(4)] public byte Byte; - } -} diff --git a/src/Tests/FlatSharpTests/SerializationTests/VectorSerializationTests.cs b/src/Tests/FlatSharpTests/SerializationTests/VectorSerializationTests.cs deleted file mode 100644 index 69e92684..00000000 --- a/src/Tests/FlatSharpTests/SerializationTests/VectorSerializationTests.cs +++ /dev/null @@ -1,721 +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.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using FlatSharp.TypeModel; -using Unity.Collections; - -namespace FlatSharpTests; - -/// -/// Binary format testing for vector serialization. -/// - -public class VectorSerializationTests -{ - public class SimpleTests - { - - [Fact] - public void Simple_Scalar_Vectors() - { - static void Test(Func factory) - { - byte[] expectedResult = - { - 4, 0, 0, 0, // offset to table start - 248, 255, 255, 255, // soffset to vtable (-8) - 12, 0, 0, 0, // uoffset_t to vector - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 3, 0, 0, 0, // vector length - - // vector data - 1, 2, 3, - }; - - var root = new RootTable - { - Vector = factory(new byte[] { 1, 2, 3 }) - }; - - Span target = new byte[1024]; - int offset = FlatBufferSerializer.DefaultWithUnitySupport.Serialize(root, target); - string csharp = FlatBufferSerializer.DefaultWithUnitySupport.Compile(root).GetCSharp(); - - target = target.Slice(0, offset); - - Assert.True(expectedResult.AsSpan().SequenceEqual(target)); - } - - Test>(a => a.ToList()); - Test>(a => a.ToList()); - Test>(a => a.AsMemory()); - Test>(a => a.AsMemory()); - Test>(a => new NativeArray(a, Allocator.Temp)); - Test?>(a => a.AsMemory()); - Test?>(a => a.AsMemory()); - Test?>(a => new NativeArray(a, Allocator.Temp)); - } - - [Fact] - public void Empty_Vectors() - { - static void Test(T instance) - { - byte[] expectedResult = - { - 4, 0, 0, 0, // offset to table start - 248, 255, 255, 255, // soffset to vtable (-8) - 12, 0, 0, 0, // uoffset_t to vector - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 0, 0, 0, 0, // vector length - }; - - var root = new RootTable - { - Vector = instance - }; - - Span target = new byte[1024]; - int offset = FlatBufferSerializer.DefaultWithUnitySupport.Serialize(root, target); - string csharp = FlatBufferSerializer.DefaultWithUnitySupport.Compile(root).GetCSharp(); - - target = target.Slice(0, offset); - - Assert.True(expectedResult.AsSpan().SequenceEqual(target)); - } - - Test>(new List()); - Test>(new List()); - Test>(new Memory(new byte[0])); - Test>(new ReadOnlyMemory(new byte[0])); - Test>(new NativeArray(new byte[0], Allocator.Temp)); - Test?>(new Memory(new byte[0])); - Test?>(new ReadOnlyMemory(new byte[0])); - Test?>(new NativeArray(new byte[0], Allocator.Temp)); - Test>>(new IndexedVector>()); - } - - [Fact] - public void Null_Vectors() - { - static void Test() - { - byte[] expectedResult = - { - 4, 0, 0, 0, // offset to table start - 252,255,255,255, // soffset to vtable (-4) - 4, 0, // vtable length - 4, 0, // table length - }; - - var root = new RootTable - { - Vector = default(T) - }; - - Span target = new byte[1024]; - int offset = FlatBufferSerializer.DefaultWithUnitySupport.Serialize(root, target); - string csharp = FlatBufferSerializer.DefaultWithUnitySupport.Compile(root).GetCSharp(); - - target = target.Slice(0, offset); - - Assert.True(expectedResult.AsSpan().SequenceEqual(target)); - } - - Test>(); - Test>(); - Test?>(); - Test?>(); - Test?>(); - Test>>(); - - Test>>(); - Test>>(); - - Test(); - } - - [Fact] - public void UnalignedStruct_5Byte() - { - var root = new RootTable> - { - Vector = new[] - { - new FiveByteStruct { Byte = 1, Int = 1 }, - new FiveByteStruct { Byte = 2, Int = 2 }, - new FiveByteStruct { Byte = 3, Int = 3 }, - }, - }; - - Span target = new byte[10240]; - int offset = FlatBufferSerializer.Default.Serialize(root, target); - target = target.Slice(0, offset); - - byte[] expectedResult = - { - 4, 0, 0, 0, // offset to table start - 248, 255, 255, 255, // soffset to vtable (-8) - 12, 0, 0, 0, // uoffset_t to vector - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 3, 0, 0, 0, // vector length - 1, 0, 0, 0, // index 0.Int - 1, // index 0.Byte - 0, 0, 0, // padding - 2, 0, 0, 0, // index 1.Int - 2, // index 1.Byte - 0, 0, 0, // padding - 3, 0, 0, 0, // index2.Int - 3, // Index2.byte - 0, 0, 0, // padding - }; - - Assert.True(expectedResult.AsSpan().SequenceEqual(target)); - } - - [Fact] - public void UnalignedStruct_Value5Byte() - { - var root = new RootTable> - { - Vector = new[] - { - new ValueFiveByteStruct { Byte = 1, Int = 1 }, - new ValueFiveByteStruct { Byte = 2, Int = 2 }, - new ValueFiveByteStruct { Byte = 3, Int = 3 }, - }, - }; - - Span target = new byte[10240]; - int offset = FlatBufferSerializer.Default.Serialize(root, target); - target = target.Slice(0, offset); - - byte[] expectedResult = - { - 4, 0, 0, 0, // offset to table start - 248, 255, 255, 255, // soffset to vtable (-8) - 12, 0, 0, 0, // uoffset_t to vector - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 3, 0, 0, 0, // vector length - 1, 0, 0, 0, // index 0.Int - 1, // index 0.Byte - 0, 0, 0, // padding - 2, 0, 0, 0, // index 1.Int - 2, // index 1.Byte - 0, 0, 0, // padding - 3, 0, 0, 0, // index2.Int - 3, // Index2.byte - 0, 0, 0, // padding - }; - - Assert.True(expectedResult.AsSpan().SequenceEqual(target)); - } - - [Fact] - public void UnalignedStruct_Value5Byte_NativeArray() - { - var root = new RootTable> - { - Vector = new NativeArray(new[] - { - new ValueFiveByteStruct { Byte = 1, Int = 1 }, - new ValueFiveByteStruct { Byte = 2, Int = 2 }, - new ValueFiveByteStruct { Byte = 3, Int = 3 }, - }, Allocator.Temp) - }; - - Assert.Throws(() => - { - Span target = new byte[10240]; - FlatBufferSerializer.DefaultWithUnitySupport.Serialize(root, target); - }); - } - - [Fact] - public void AlignedStruct_Value5Byte_NativeArray() - { - var root = new RootTable> - { - Vector = new NativeArray(new[] - { - new ValueFiveByteStructWithPadding { Byte = 1, Int = 1 }, - new ValueFiveByteStructWithPadding { Byte = 2, Int = 2 }, - new ValueFiveByteStructWithPadding { Byte = 3, Int = 3 }, - }, Allocator.Temp) - }; - - Span target = new byte[10240]; - int offset = FlatBufferSerializer.DefaultWithUnitySupport.Serialize(root, target); - target = target.Slice(0, offset); - - byte[] expectedResult = - { - 4, 0, 0, 0, // offset to table start - 248, 255, 255, 255, // soffset to vtable (-8) - 12, 0, 0, 0, // uoffset_t to vector - 6, 0, // vtable length - 8, 0, // table length - 4, 0, // offset of index 0 field - 0, 0, // padding to 4-byte alignment - 3, 0, 0, 0, // vector length - 1, 0, 0, 0, // index 0.Int - 1, // index 0.Byte - 0, 0, 0, // padding - 2, 0, 0, 0, // index 1.Int - 2, // index 1.Byte - 0, 0, 0, // padding - 3, 0, 0, 0, // index2.Int - 3, // Index2.byte - 0, 0, 0, // padding - }; - - Assert.True(expectedResult.AsSpan().SequenceEqual(target)); - } - - [Fact] - public void UnalignedStruct_9Byte() - { - var root = new RootTable2> - { - Vector = new[] - { - new NineByteStruct { Byte = 1, Long = 1 }, - new NineByteStruct { Byte = 2, Long = 2 }, - }, - }; - - Span target = new byte[10240]; - int offset = FlatBufferSerializer.Default.Serialize(root, target); - target = target.Slice(0, offset); - - byte[] expectedResult = - { - 4, 0, 0, 0, // offset to table start - 246, 255, 255, 255, // soffset to vtable (-10) - 20, 0, 0, 0, // uoffset_t to vector - 0, // alignment imp - 0, // padding - 8, 0, // vtable length - 9, 0, // table length - 8, 0, // offset to index 0 field - 4, 0, // offset of index 1 field - - 0, 0, 0, 0, 0, 0, // padding to 8 byte alignment for struct. - 2, 0, 0, 0, // vector length - 1, 0, 0, 0, 0, 0, 0, 0, // index 0.Long - 1, // index 0.Byte - 0, 0, 0, 0, 0, 0, 0, // padding - 2, 0, 0, 0, 0, 0, 0, 0, // index 1.Long - 2, // index 1.Byte - 0, 0, 0, 0, 0, 0, 0, // padding - }; - - Assert.True(expectedResult.AsSpan().SequenceEqual(target)); - } - - [Fact] - public void UnalignedStruct_Value9Byte() - { - var root = new RootTable2> - { - Vector = new[] - { - new ValueNineByteStruct { Byte = 1, Long = 1 }, - new ValueNineByteStruct { Byte = 2, Long = 2 }, - }, - }; - - Span target = new byte[10240]; - int offset = FlatBufferSerializer.Default.Serialize(root, target); - target = target.Slice(0, offset); - - byte[] expectedResult = - { - 4, 0, 0, 0, // offset to table start - 246, 255, 255, 255, // soffset to vtable (-10) - 20, 0, 0, 0, // uoffset_t to vector - 0, // alignment imp - 0, // padding - 8, 0, // vtable length - 9, 0, // table length - 8, 0, // offset to index 0 field - 4, 0, // offset of index 1 field - - 0, 0, 0, 0, 0, 0, // padding to 8 byte alignment for struct. - 2, 0, 0, 0, // vector length - 1, 0, 0, 0, 0, 0, 0, 0, // index 0.Long - 1, // index 0.Byte - 0, 0, 0, 0, 0, 0, 0, // padding - 2, 0, 0, 0, 0, 0, 0, 0, // index 1.Long - 2, // index 1.Byte - 0, 0, 0, 0, 0, 0, 0, // padding - }; - - Assert.True(expectedResult.AsSpan().SequenceEqual(target)); - } - - [Fact] - public void NullStringInVector() - { - var root = new RootTable> - { - Vector = new string[] { "foobar", "banana", null, "two" }, - }; - - var serializer = FlatBufferSerializer.Default.Compile>>(); - - byte[] target = new byte[10240]; - Assert.Throws(() => FlatBufferSerializer.Default.Serialize(root, target)); - } - - [Fact] - public void NullStructInVector() - { - var root = new RootTable> - { - Vector = new[] { new Struct { Integer = 1, }, null, new Struct { Integer = 3 } }, - }; - - var serializer = FlatBufferSerializer.Default.Compile>>(); - - byte[] target = new byte[10240]; - Assert.Throws(() => FlatBufferSerializer.Default.Serialize(root, target)); - } - - [Fact] - public void AlignedStructVectorMaxSize() - { - var root = new RootTable>(); - - // Empty table max size (vector not included here). - var baselineMaxSize = FlatBufferSerializer.Default.GetMaxSize(root); - - root.Vector = new[] { new Struct { Integer = 1 }, new Struct { Integer = 2 } }; - - var maxSize = FlatBufferSerializer.Default.GetMaxSize(root); - - // padding + length + padding + 2 * itemLength - Assert.Equal(3 + 4 + 3 + (2 * 4), maxSize - baselineMaxSize); - } - - [Fact] - public void UnalignedStruct_5Byte_VectorMaxSize() - { - var root = new RootTable>(); - - // Empty table max size (vector not included here). - var baselineMaxSize = FlatBufferSerializer.Default.GetMaxSize(root); - - root.Vector = new[] { new FiveByteStruct { Int = 1 }, new FiveByteStruct { Int = 2 } }; - - var maxSize = FlatBufferSerializer.Default.GetMaxSize(root); - - // padding + length + padding to 4 byte alignment + (2 * (padding + itemLength)) - Assert.Equal(3 + 4 + 3 + (2 * (3 + 5)), maxSize - baselineMaxSize); - } - - [Fact] - public void UnalignedStruct_9Byte_VectorMaxSize() - { - var root = new RootTable>(); - - // Empty table max size (vector not included here). - var baselineMaxSize = FlatBufferSerializer.Default.GetMaxSize(root); - - root.Vector = new[] { new NineByteStruct { Long = 1 }, new NineByteStruct { Long = 2 } }; - - var maxSize = FlatBufferSerializer.Default.GetMaxSize(root); - - // padding + length + padding to 8 byte alignment + (2 * (padding + itemLength)) - Assert.Equal(3 + 4 + 7 + (2 * (7 + 9)), maxSize - baselineMaxSize); - } - - [Fact] - public void SortedVector_StringKey() - { - var root = new RootTableSorted>>(); - - root.Vector = new List> - { - new TableWithKey { Key = "d", Value = "0" }, - new TableWithKey { Key = "c", Value = "1" }, - new TableWithKey { Key = "b", Value = "2" }, - new TableWithKey { Key = "a", Value = "3" }, - new TableWithKey { Key = "", Value = "4" }, - }; - - byte[] data = new byte[1024]; - FlatBufferSerializer.Default.Serialize(root, data); - - var parsed = FlatBufferSerializer.Default.Parse>>>(data); - - Assert.Equal("", parsed.Vector[0].Key); - Assert.Equal("a", parsed.Vector[1].Key); - Assert.Equal("b", parsed.Vector[2].Key); - Assert.Equal("c", parsed.Vector[3].Key); - Assert.Equal("d", parsed.Vector[4].Key); - } - - [Fact] - public void SortedVector_BinarySearch_ErrorCases() - { - IList> testList = new List> - { - new TableWithKey { Key = null, Value = "0" }, - new TableWithKey { Key = "notnull", Value = "3" }, - new TableWithKey { Key = "alsonotnull", Value = "3" }, - }; - - var root = new RootTable>>() - { - Vector = testList - }; - - byte[] data = new byte[1024]; - - // Serialize succeeds here because the "root" is unsorted. - FlatBufferSerializer.Default.Serialize(root, data); - - // Fail to serialize sorted vector due to null key. - // Fail to search through greedy sorted vector due to null key. - { - var rootSorted = new RootTableSorted>>() - { - Vector = testList - }; - - var parsed_greedy = FlatBufferSerializer.Default.Parse>>>(data); - Assert.Throws(() => FlatBufferSerializer.Default.Serialize(rootSorted, new byte[1024])); - Assert.Throws(() => SortedVectorHelpers.BinarySearchByFlatBufferKey(parsed_greedy.Vector, "AAA")); - Assert.Throws(() => SortedVectorHelpers.BinarySearchByFlatBufferKey(parsed_greedy.Vector, (string)null)); - } - - // Fail to use a table with uninitialized keys. - { - var rootSorted = new RootTableSorted>> - { - Vector = testList.Select(x => new TableWithUninitializedKey { Key = x.Key, Value = x.Value }).ToList(), - }; - - var parsed_greedy = FlatBufferSerializer.Default.Parse>>>(data); - Assert.Throws(() => FlatBufferSerializer.Default.Serialize(rootSorted, new byte[1024])); - Assert.Throws(() => SortedVectorHelpers.BinarySearchByFlatBufferKey(parsed_greedy.Vector, "AAA")); - Assert.Throws(() => SortedVectorHelpers.BinarySearchByFlatBufferKey(parsed_greedy.Vector, (string)null)); - } - - // Fail to binary search through lazy sorted vector with null key. - { - // Serialize succeeds here because the "root" is unsorted. - FlatBufferSerializer.Default.Serialize(root, data); - var lazyCopy = FlatBufferSerializer.Default.Compile>>>().WithSettings(s => s.UseLazyDeserialization()).Parse(data); - - Assert.Throws(() => SortedVectorHelpers.BinarySearchByFlatBufferKey(lazyCopy.Vector, "AAA")); - Assert.Throws(() => SortedVectorHelpers.BinarySearchByFlatBufferKey(lazyCopy.Vector, (string)null)); - } - - { - var parsed = FlatBufferSerializer.Default.Compile>>>().WithSettings(s => s.UseLazyDeserialization()).Parse(data); - var ex = Assert.Throws(() => SortedVectorHelpers.BinarySearchByFlatBufferKey(parsed.Vector, "foo")); - } - } - } - - public class VectorOfUnionTests - { - [Fact] - public void VectorOfUnion_List() => this.VectorOfUnionTest>>>>( - (l, v) => v.Vector = l.ToList()); - - [Fact] - public void VectorOfUnion_ReadOnlyList() => this.VectorOfUnionTest>>>>( - (l, v) => v.Vector = l.ToList()); - - private void VectorOfUnionTest(Action>[], V> setValue) - where V : class, new() - { - var items = new[] - { - new FlatBufferUnion>("foo"), - new FlatBufferUnion>(new Struct { Integer = 3 }), - new FlatBufferUnion>(new TableWithKey { Key = 1 }), - }; - - V value = new V(); - setValue(items, value); - - byte[] expectedData = - { - 4, 0, 0, 0, - 244, 255, 255, 255, - 16, 0, 0, 0, // uoffset to discriminator vector - 20, 0, 0, 0, // uoffset to offset vector - 8, 0, // vtable - 12, 0, - 4, 0, - 8, 0, - 3, 0, 0, 0, // discriminator vector length - 1, 2, 3, 0, // values + 1 byte padding - 3, 0, 0, 0, // offset vector length - 12, 0, 0, 0, // value 0 - 16, 0, 0, 0, // value 1 - 16, 0, 0, 0, // value 2 - 3, 0, 0, 0, // string length - 102, 111, 111, 0, // foo + null terminator - 3, 0, 0, 0, // struct value ('3') - 248, 255, 255, 255, // table vtable offset - 1, 0, 0, 0, // value of 'key' - 8, 0, // table vtable start - 8, 0, - 0, 0, - 4, 0, - }; - - byte[] data = new byte[1024]; - int written = FlatBufferSerializer.Default.Serialize(value, data); - data = data.AsSpan().Slice(0, written).ToArray(); - - Assert.True(data.SequenceEqual(expectedData)); - } - } - - [FlatBufferTable] - public class RootTable - { - [FlatBufferItem(0)] - public virtual TVector? Vector { get; set; } - } - - [FlatBufferTable] - public class RootTableSorted - { - [FlatBufferItem(0, SortedVector = true)] - public virtual TVector? Vector { get; set; } - } - - [FlatBufferTable] - public class TableWithKey : ISortableTable - { - static TableWithKey() - { - SortedVectorHelpers.RegisterKeyLookup, TKey>(x => x.Key, 1); - } - - [FlatBufferItem(0)] - public virtual string? Value { get; set; } - - [FlatBufferItem(1, Key = true)] - public virtual TKey? Key { get; set; } - } - - [FlatBufferTable] - public class TableWithUninitializedKey : ISortableTable - { - [FlatBufferItem(0)] - public virtual string? Value { get; set; } - - [FlatBufferItem(1, Key = true)] - public virtual TKey? Key { get; set; } - } - - // Should be impossible since FlatSharp compiler only adds this to types that are sortable. However - // a user could technically add this themselves... - [FlatBufferTable] - public class TableWithNoKey : ISortableTable - { - [FlatBufferItem(0)] - public virtual string? Value { get; set; } - - [FlatBufferItem(1)] - public virtual TKey? Key { get; set; } - } - - [FlatBufferTable] - public class RootTable2 - { - [FlatBufferItem(0, DefaultValue = (byte)201)] - public virtual byte AlignmentImp { get; set; } - - [FlatBufferItem(1)] - public virtual TVector? Vector { get; set; } - } - - [FlatBufferStruct] - public class Struct - { - [FlatBufferItem(0)] - public virtual int Integer { get; set; } - } - - [FlatBufferStruct] - public class NineByteStruct - { - [FlatBufferItem(0)] - public virtual long Long { get; set; } - - [FlatBufferItem(1)] - public virtual byte Byte { get; set; } - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit, Size = 9)] - public struct ValueNineByteStruct - { - [FieldOffset(0)] public long Long; - - [FieldOffset(8)] public byte Byte; - } - - [FlatBufferStruct] - public class FiveByteStruct - { - [FlatBufferItem(0)] - public virtual int Int { get; set; } - - [FlatBufferItem(1)] - public virtual byte Byte { get; set; } - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit, Size = 5)] - public struct ValueFiveByteStruct - { - [FieldOffset(0)] public int Int; - - [FieldOffset(4)] public byte Byte; - } - - [FlatBufferStruct, StructLayout(LayoutKind.Explicit)] - public struct ValueFiveByteStructWithPadding - { - [FieldOffset(0)] public int Int; - - [FieldOffset(4)] public byte Byte; - } -} \ No newline at end of file diff --git a/src/Tests/FlatSharpTests/SerializationTests/WriteThroughTests.cs b/src/Tests/FlatSharpTests/SerializationTests/WriteThroughTests.cs deleted file mode 100644 index 6c54dd99..00000000 --- a/src/Tests/FlatSharpTests/SerializationTests/WriteThroughTests.cs +++ /dev/null @@ -1,493 +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. - */ - -using System.Collections.ObjectModel; -using System.Runtime.InteropServices; - -using FlatSharp.CodeGen; -using FlatSharp.TypeModel; -using Microsoft.CodeAnalysis.Options; - -namespace FlatSharpTests; - -/// -/// Tests various types of vectors (List/ReadOnlyList/Memory/ReadOnlyMemory/Array) for primitive types. -/// -public class WriteThroughTests -{ - // Type model tests for write through scenarios. - public class TypeModel - { - [Theory] - [InlineData(typeof(ValueStruct))] - [InlineData(typeof(ValueStruct?))] - public void WriteThrough_Table_RequiredStructField(Type type) - { - type = typeof(WriteThroughTable_Required<>).MakeGenericType(type); - - var typeModel = RuntimeTypeModel.CreateFrom(type); - Assert.True(((TableTypeModel)typeModel).IndexToMemberMap[0].IsWriteThrough); - Assert.True(((TableTypeModel)typeModel).IndexToMemberMap[0].Attribute.WriteThrough); - } - - [Theory] - [InlineData(typeof(ValueStruct))] - [InlineData(typeof(ValueStruct?))] - public void WriteThrough_Table_NotRequiredStructField(Type type) - { - type = typeof(WriteThroughTable_NotRequired<>).MakeGenericType(type); - - var ex = Assert.Throws(() => - RuntimeTypeModel.CreateFrom(type)); - - Assert.Equal( - $"Table property '{type.GetCompilableTypeName()}.Item' declared the WriteThrough attribute, but the field is not marked as required. WriteThrough fields must also be required.", - ex.Message); - } - - [Theory] - [InlineData(typeof(WriteThroughTable_NotRequired>))] - [InlineData(typeof(WriteThroughTable_NotRequired>))] - [InlineData(typeof(WriteThroughTable_NotRequired>))] - [InlineData(typeof(WriteThroughTable_NotRequired>>))] - [InlineData(typeof(WriteThroughTable_NotRequired>>))] - public void WriteThrough_Table_VectorOfDisallowed(Type type) - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(type)); - - Assert.Equal( - $"Table property '{type.GetCompilableTypeName()}.Item' declared the WriteThrough on a vector. Vector WriteThrough is only valid for value type structs.", - ex.Message); - } - - [Theory] - [InlineData(typeof(WriteThroughTable_NotRequired>))] - public void WriteThrough_Table_VectorReadOnly(Type type) - { - var ex = Assert.Throws( - () => FlatBufferSerializer.Default.Compile(type)); - - Assert.Equal( - $"Field '{type.GetCompilableTypeName()}.Item' declares the WriteThrough option. WriteThrough is only supported for IList vectors.", - ex.Message); - } - - [Theory] - [InlineData(typeof(WriteThroughTable_Required))] - [InlineData(typeof(WriteThroughTable_Required>))] - [InlineData(typeof(WriteThroughTable_Required?>))] - [InlineData(typeof(WriteThroughTable_Required))] - public void WriteThrough_Table_DisallowedTypes(Type type) - { - var ex = Assert.Throws( - () => RuntimeTypeModel.CreateFrom(type)); - - Assert.Equal( - $"Table property '{type.GetCompilableTypeName()}.Item' declared the WriteThrough attribute. WriteThrough on tables is only supported for value type structs.", - ex.Message); - } - - [Theory] - [InlineData(typeof(WriteThroughTable_Required>), false)] - [InlineData(typeof(WriteThroughTable_NotRequired>), false)] - [InlineData(typeof(WriteThroughTable_Required), true)] - public void WriteThrough_Table_ValidCases(Type innerType, bool expectWriteThrough) - { - var typeModel = RuntimeTypeModel.CreateFrom(innerType); - - Assert.Equal( - expectWriteThrough, - ((TableTypeModel)typeModel).IndexToMemberMap[0].IsWriteThrough); - - Assert.True( - ((TableTypeModel)typeModel).IndexToMemberMap[0].Attribute.WriteThrough); - } - } - - // Tests for writethrough inside of reference structs. - public class ReferenceStructInteriorWriteThrough - { - [Fact] - public void WriteThrough_SimpleInt_InReferenceStruct() - { - static void Test(FlatBufferDeserializationOption option) - { - var table = new Table> - { - Struct = new WriteThroughStruct - { - Value = 5 - } - }; - - var serializer = FlatBufferSerializer.CompileFor(table).WithSettings(s => s.UseDeserializationMode(option)); - - byte[] buffer = new byte[1024]; - serializer.Write(buffer, table); - - // parse - var parsed1 = serializer.Parse>>(buffer); - - // mutate - parsed1.Struct.Value = 300; - Assert.Equal(300, parsed1.Struct.Value); - - // verify - var parsed2 = serializer.Parse>>(buffer); - Assert.Equal(300, parsed2.Struct.Value); - } - - Test(FlatBufferDeserializationOption.Progressive); - Test(FlatBufferDeserializationOption.Lazy); - } - - [Fact] - public void WriteThrough_NestedStruct_InReferenceStruct() - { - static void Test(FlatBufferDeserializationOption option) - { - var table = new Table - { - Struct = new WriteThroughStruct - { - Value = new OtherStruct { Prop1 = 10, Prop2 = 10 }, - ValueStruct = new() { Value = 3, } - } - }; - - var serializer = FlatBufferSerializer.CompileFor(table).WithSettings(s => s.UseDeserializationMode(option)); - - byte[] buffer = new byte[1024]; - serializer.Write(buffer, table); - - // parse - var parsed1 = serializer.Parse>(buffer); - - // mutate - Assert.Equal(10, parsed1.Struct.Value.Prop1); - Assert.Equal(10, parsed1.Struct.Value.Prop2); - Assert.Equal(3, parsed1.Struct.ValueStruct.Value); - parsed1.Struct.Value = new OtherStruct { Prop1 = 300, Prop2 = 300 }; - parsed1.Struct.ValueStruct = new() { Value = -1 }; - Assert.Equal(300, parsed1.Struct.Value.Prop1); - Assert.Equal(300, parsed1.Struct.Value.Prop2); - Assert.Equal(-1, parsed1.Struct.ValueStruct.Value); - - // verify, set to null - var parsed2 = serializer.Parse>(buffer); - Assert.Equal(300, parsed2.Struct.Value.Prop1); - Assert.Equal(300, parsed2.Struct.Value.Prop2); - Assert.Equal(-1, parsed2.Struct.ValueStruct.Value); - parsed2.Struct.Value = null!; - - if (option == FlatBufferDeserializationOption.Progressive) - { - // we are null temporarily until we re-parse. - Assert.Null(parsed2.Struct.Value); - } - else if (option == FlatBufferDeserializationOption.Lazy) - { - // lazy write through clears it out. - Assert.Equal(0, parsed2.Struct.Value.Prop1); - Assert.Equal(0, parsed2.Struct.Value.Prop2); - } - else - { - Assert.False(true); - } - - // verify, set to null - var parsed3 = serializer.Parse>(buffer); - Assert.Equal(0, parsed3.Struct.Value.Prop1); - Assert.Equal(0, parsed3.Struct.Value.Prop2); - } - - Test(FlatBufferDeserializationOption.Progressive); - Test(FlatBufferDeserializationOption.Lazy); - } - - [Fact] - public void WriteThrough_Vector_List() - { - static void Test(FlatBufferDeserializationOption option) - { - var table = new Table>> - { - Struct = new List> - { - new WriteThroughStruct { Value = 5 } - } - }; - - var serializer = FlatBufferSerializer.CompileFor(table).WithSettings(s => s.UseDeserializationMode(option)); - - byte[] buffer = new byte[1024]; - serializer.Write(buffer, table); - - // parse - var parsed1 = serializer.Parse>>>(buffer); - - // mutate - parsed1.Struct[0].Value = 300; - Assert.Equal(300, parsed1.Struct[0].Value); - - // verify - var parsed2 = serializer.Parse>>>(buffer); - Assert.Equal(300, parsed2.Struct[0].Value); - } - - Test(FlatBufferDeserializationOption.Progressive); - Test(FlatBufferDeserializationOption.Lazy); - } - - [Fact] - public void WriteThrough_Lazy_ThrowsForOtherProperties() - { - var table = new Table - { - Struct = new OtherStruct { Prop1 = 1, Prop2 = 2 } - }; - - var serializer = FlatBufferSerializer.CompileFor(table).WithSettings(s => s.UseLazyDeserialization()); - - byte[] buffer = new byte[1024]; - serializer.Write(buffer, table); - var parsed = serializer.Parse(buffer); - - Assert.Throws(() => parsed.Struct.Prop2 = 4); - } - } - - public class TableField - { - [Fact] - public void Greedy_Throws() - { - WriteThroughTable_Required table = new() - { - Item = new ValueStruct { Value = 1 } - }; - - var serializer = FlatBufferSerializer.CompileFor(table).WithSettings(s => s.UseGreedyDeserialization()); - - byte[] result = new byte[1024]; - - serializer.Write(result, table); - - var parsed = serializer.Parse>(result); - Assert.Equal(1, parsed.Item.Value.Value); - - Assert.Throws(() => parsed.Item = new ValueStruct { Value = 4 }); - - // Re-read and verify the in-struct writethrough didn't do anything. - parsed = serializer.Parse>(result); - Assert.Equal(1, parsed.Item.Value.Value); - } - - // Tests write through within a table. - [Theory] - [InlineData(FlatBufferDeserializationOption.Lazy)] - [InlineData(FlatBufferDeserializationOption.Progressive)] - public void Success_Nullable(FlatBufferDeserializationOption option) - { - WriteThroughTable_Required table = new() - { - Item = new ValueStruct { Value = 1 } - }; - - var serializer = FlatBufferSerializer.CompileFor(table).WithSettings(s => s.UseDeserializationMode(option)); - - byte[] result = new byte[1024]; - - serializer.Write(result, table); - - var parsed = serializer.Parse>(result); - Assert.Equal(1, parsed.Item.Value.Value); - - parsed.Item = new ValueStruct { Value = 4 }; - - // Re-read and verify the in-struct writethrough succeeded. - parsed = serializer.Parse>(result); - Assert.Equal(4, parsed.Item.Value.Value); - - var ex = Assert.Throws(() => parsed.Item = null); - Assert.Equal( - "Nullable object must have a value.", - ex.Message); - } - - [Theory] - [InlineData(FlatBufferDeserializationOption.Lazy)] - [InlineData(FlatBufferDeserializationOption.Progressive)] - public void Success_NonNullable(FlatBufferDeserializationOption option) - { - WriteThroughTable_Required table = new() - { - Item = new ValueStruct { Value = 1 } - }; - - var serializer = FlatBufferSerializer.CompileFor(table).WithSettings(s => s.UseDeserializationMode(option)); - - byte[] result = new byte[1024]; - - serializer.Write(result, table); - - var parsed = serializer.Parse>(result); - Assert.Equal(1, parsed.Item.Value); - - parsed.Item = new ValueStruct { Value = 4 }; - - // Re-read and verify the in-struct writethrough succeeded. - parsed = serializer.Parse>(result); - Assert.Equal(4, parsed.Item.Value); - } - - [Theory] - [InlineData(FlatBufferDeserializationOption.GreedyMutable)] - public void Failure_GreedyMutable_Runtime(FlatBufferDeserializationOption option) - { - var serializer = FlatBufferSerializer.Default.Compile>().WithSettings(s => s.UseDeserializationMode(option)); - - var item = new WriteThroughTable_Required() - { - Item = new ValueStruct { Value = 3 } - }; - - byte[] buffer = new byte[1024]; - serializer.Write(buffer, item); - var parsed = serializer.Parse>(buffer); - - var ex = Assert.Throws(() => parsed.Item = new ValueStruct()); - - Assert.Equal( - "WriteThrough fields are implemented as readonly when using 'GreedyMutable' serializers.", - ex.Message); - } - } - - public class Vector - { - // Tests write through within a struct and write through of a whole struct. - [Theory] - [InlineData(FlatBufferDeserializationOption.Lazy)] - [InlineData(FlatBufferDeserializationOption.Progressive)] - public void Success(FlatBufferDeserializationOption option) - { - var serializer = FlatBufferSerializer.Default.Compile>>().WithSettings(s => s.UseDeserializationMode(option)); - - WriteThroughTable_NotRequired> table = new() - { - Item = new List - { - new() { Value = 1 } - } - }; - - byte[] result = new byte[1024]; - serializer.Write(result, table); - - var parsed = serializer.Parse>>(result); - Assert.Equal(1, parsed.Item[0].Value); - - parsed.Item[0] = new ValueStruct { Value = 4 }; - - // Re-read and verify the in-struct writethrough succeeded. - parsed = serializer.Parse>>(result); - Assert.Equal(4, parsed.Item[0].Value); - } - - [Theory] - [InlineData(FlatBufferDeserializationOption.Greedy)] - [InlineData(FlatBufferDeserializationOption.GreedyMutable)] - public void Failures_IList_AtRuntime(FlatBufferDeserializationOption option) - { - var item = new WriteThroughTable_NotRequired> - { - Item = new List() - { - new ValueStruct { Value = 1 }, - new ValueStruct { Value = 2 }, - new ValueStruct { Value = 3 }, - } - }; - - var serializer = FlatBufferSerializer.CompileFor(item).WithSettings(s => s.UseDeserializationMode(option)); - - byte[] buffer = new byte[1024]; - serializer.Write(buffer, item); - var parsed = serializer.Parse>>(buffer); - - //Assert.IsType>(parsed.Item); - Assert.Throws(() => parsed.Item[0] = new ValueStruct { Value = 4 }); - } - } - - [FlatBufferTable] - public class Table - { - [FlatBufferItem(0)] - public virtual T? Struct { get; set; } - } - - [FlatBufferTable] - public class WriteThroughTable_Required - { - [FlatBufferItem(0, WriteThrough = true, Required = true)] - public virtual T? Item { get; set; } - } - - [FlatBufferTable] - public class WriteThroughTable_NotRequired - { - [FlatBufferItem(0, WriteThrough = true)] - public virtual T? Item { get; set; } - } - - [FlatBufferStruct] - public class WriteThroughStruct - { - [FlatBufferItem(0, WriteThrough = true)] - public virtual T Value { get; set; } - } - - [FlatBufferStruct] - public class WriteThroughStruct - { - [FlatBufferItem(0, WriteThrough = true)] - public virtual OtherStruct Value { get; set; } - - [FlatBufferItem(1, WriteThrough = true)] - public virtual ValueStruct ValueStruct { get; set; } - } - - [FlatBufferStruct] - public class OtherStruct - { - [FlatBufferItem(0, WriteThrough = true)] - public virtual int Prop1 { get; set; } - - [FlatBufferItem(1, WriteThrough = false)] - public virtual int Prop2 { get; set; } - } - - [FlatBufferStruct] - [StructLayout(LayoutKind.Explicit, Size = 4)] - public struct ValueStruct - { - [FieldOffset(0)] public int Value; - } -} diff --git a/src/Tests/FlatSharpTests/Tests.bfbs b/src/Tests/FlatSharpTests/Tests.bfbs deleted file mode 100644 index b5b896fcba3fe4fbf61ed26d4334c2b5c446d003..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4632 zcmbuCU5J!b7{{M+XFuKDFC@Ir@NG`3aRU9k{z+#PjuoROV(MZ*gT z5s?TlBqAcZSi%blFGReE5a~ifBEky^=_(>b%4{q9+UfT{XU@*M!YOF$<`f_l#a2FEm!Fng5dt%YVAI?S%M znH^7=O|_e?0!v!V&Uc&jvDr8!L(GIOxO!GX6KK=!c-*}D*?W48dgDJ2Z ztOAdN`dmi}wLDhOtv&WI(+m{t|Ylmr(>|G z?5Q)7?$7H#xOH$7LpH@evXSPsw=e*n0rl-MRGf%%W7~71QE_6(X*!(Ulg|-xp0OEW z;+~{6yEsfFk}i&A_uIq{O&8&2-a=+8e*QlkUMNm-d?OB4;b1Wi1s@BYaSrDjaOi{Q z!|kx85IwirQ$XXT&U3PZEU(AgK^&A58r9k1nelvD<9yanm&=k3evzNoffHZ{SPmY} zOG!uG;^PJE6msFyU*&`Dl;TOg@)k6^fn0)-AopEGj#A;|ytm=>i`FM+<+^|K`w^l} zFJ}#YXYM;+C!~kYk}lFgIj|eZwoicudPEbGqmhrZk{!Z732H^KUnjdMzQjy>KhlvT z_suqEOZ!=4y_}tbO0>&$W*@$wsozDlh<^1oh9|IvM4ZfEWpxGsH^*t+c=Pd6_;< ztMYjxSi@PtueU2)uUb2FYgc& z@w}LEcIsXvyTo%g?c%-4ZTTNAFWjA@8tv-cYY|17yAj!1eu}dlpmAT|TxR;5F>Vt2R67 zBcC?Dt7fxXh0uk@=Hi%(_3Jg~dY@XV)~BDabQWEE0&Vk#+e~sPy6{+ z)hOz?weXI`&xHK1I;(tA{;1Z`)DI%nyGMs>sseS>yXd(g0uAXjuz&(`}w5irRYWaO=uA25BvEx&5wZ_^cTcx z=sU6Xx*VqIC#cS0{Utx&qV;>gyVxqseHB}qWp0r7K$=9Ff3l-=R4Y3l1IqjVV7E}R z?G$mPyFHxyqKh+Y5+`v!M}Ln0{k*#u#CZT-VeLsj=j@fWZoCoRsC($SCy}P9<$r{+ z0U)0*1NCFG`i8q6-^Mx5_;VME^Q&Md^Ks70d@Q*6SC|(oeIvE;J8;j$WMs=@#I)WO zB%^(0=LpEgXFxBQpXcUc-j@nnhhN1v%^IDjH||cdwv{o}b-hdJ9N8#n8S;4rd9FGb zc;5CryX61=>;ad5J6w)cYr|=ubF+I6@imdpg`J5N=F_YXx@j-v-6}8zN$ zJ`?OSxHX|c;C9NzXm?U~scM6lTy1xdi~H+BUAbqi2b;%rz3JuJWut*2UqtSkfp;`F zo+Q3PP0^c~tf{BFl}}XFo*?ezB^&hBUA_S*Z|3pic;P^9Y+$?;4N~(F#b=AzL$TxE z1I0DXc-5z>YQd+LQct!W%pFnau(JAvqa4s|u-#Q(x95D4=0OM=W|7bDgrnW4%>6*1OKmIax$US{^LI!k{}{rB^2;xeMkjOQ|A@tA+Z z%Pf#grdZ5N<`!e|c>lo5G&?T)`TRvD#6mol0>()); - } - - public static SerializationCodeGenContext CreateSerializeContext(FlatBufferSerializerOptions? options = null) - { - return new SerializationCodeGenContext( - "a", - "b", - "c", - "d", - "e", - "f", - false, - new DefaultMethodNameResolver(), - TypeModelContainer.CreateDefault(), - options ?? new FlatBufferSerializerOptions(), - new Dictionary>()); - } -} diff --git a/src/Tests/FlatSharpTests/Util/ExtensionMethods.cs b/src/Tests/FlatSharpTests/Util/ExtensionMethods.cs deleted file mode 100644 index 9e53f03e..00000000 --- a/src/Tests/FlatSharpTests/Util/ExtensionMethods.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2020 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 FlatSharpTests; - -internal static class Extensions -{ - internal static string? GetCSharp(this ISerializer serializer) where T : class - { - return ((GeneratedSerializerWrapper)serializer).CSharp; - } - - internal static string? GetCSharp(this ISerializer serializer) - { - var prop = typeof(GeneratedSerializerWrapper<>) - .MakeGenericType(serializer.RootType) - .GetProperty("CSharp"); - - return (string?)prop.GetValue(serializer); - } -} \ No newline at end of file diff --git a/src/Tests/FlatSharpTests/Util/Utf8StringComparer.cs b/src/Tests/FlatSharpTests/Util/Utf8StringComparer.cs deleted file mode 100644 index 80fe56a0..00000000 --- a/src/Tests/FlatSharpTests/Util/Utf8StringComparer.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2020 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 FlatSharpTests; - -public class Utf8StringComparer : IComparer -{ - public int Compare(string x, string y) - { - return StringSpanComparer.Instance.Compare(x != null, SerializationHelpers.Encoding.GetBytes(x), y != null, SerializationHelpers.Encoding.GetBytes(y)); - } -} diff --git a/src/Tests/FlatSharpTests/regenerateOracle.cmd b/src/Tests/FlatSharpTests/regenerateOracle.cmd deleted file mode 100644 index 9380ef99..00000000 --- a/src/Tests/FlatSharpTests/regenerateOracle.cmd +++ /dev/null @@ -1,4 +0,0 @@ - -rmdir /S /Q .\Generated\FlatSharpTests -..\..\ext\flatc\windows\flatc.exe --csharp --gen-object-api -o .\OracleTests\Generated\ .\Tests.fbs -..\..\ext\flatc\windows\flatc.exe -b -o . .\Tests.fbs \ No newline at end of file diff --git a/src/Tests/FlatSharpTests/stryker-flatsharp.cmd b/src/Tests/FlatSharpTests/stryker-flatsharp.cmd deleted file mode 100644 index 5a6eaf62..00000000 --- a/src/Tests/FlatSharpTests/stryker-flatsharp.cmd +++ /dev/null @@ -1 +0,0 @@ -dotnet stryker --excluded-mutations "['checked', 'string']" --project-file=FlatSharp.csproj \ No newline at end of file diff --git a/src/Tests/FlatSharpTests/stryker-runtime.cmd b/src/Tests/FlatSharpTests/stryker-runtime.cmd deleted file mode 100644 index 193420a6..00000000 --- a/src/Tests/FlatSharpTests/stryker-runtime.cmd +++ /dev/null @@ -1 +0,0 @@ -dotnet stryker --mutate "['!*UnionTypes.cs']" --excluded-mutations "['checked', 'string']" --project-file=FlatSharp.Runtime.csproj \ No newline at end of file diff --git a/src/Tests/unittest.props b/src/Tests/unittest.props index aacc1847..b1a2c579 100644 --- a/src/Tests/unittest.props +++ b/src/Tests/unittest.props @@ -1,4 +1,8 @@ + + + + all