Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move more tests! #351

Merged
merged 7 commits into from
Nov 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
namespace FlatSharpEndToEndTests.ClassLib.FlatBufferVectorOfUnionTests;

/// <summary>
/// Tests for the FlatBufferVector class that implements IList.
/// Tests for FlatSharp's IList of Union.
/// </summary>

public class FlatBufferVectorOfUnionTests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
namespace FlatSharpEndToEndTests.ClassLib.FlatBufferVectorTests;

/// <summary>
/// Tests for the FlatBufferVector class that implements IList.
/// Tests for FlatSharp's IList implementations.
/// </summary>
public class FlatBufferVectorTests
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/*
* 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.Internal;
using System.IO;

namespace FlatSharpEndToEndTests.ClassLib.SerializerConfigurationTests;

public class SerializerConfigurationTests
{
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void UseDeserializationMode(FlatBufferDeserializationOption option)
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };

ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseDeserializationMode(option));

Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(option, obj.DeserializationContext.DeserializationOption);
}

[Fact]
public void UseLazyDeserialization()
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };

ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseLazyDeserialization());

Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(FlatBufferDeserializationOption.Lazy, obj.DeserializationContext.DeserializationOption);
}

[Fact]
public void UseProgressiveDeserialization()
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };

ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseProgressiveDeserialization());

Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(FlatBufferDeserializationOption.Progressive, obj.DeserializationContext.DeserializationOption);
}

[Fact]
public void UseGreedyDeserialization()
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };

ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseGreedyDeserialization());

Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(FlatBufferDeserializationOption.Greedy, obj.DeserializationContext.DeserializationOption);
}

[Fact]
public void UseGreedyMutableDeserialization()
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };

ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseGreedyMutableDeserialization());

Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(FlatBufferDeserializationOption.GreedyMutable, obj.DeserializationContext.DeserializationOption);
}

[Theory]
[InlineData(FlatBufferDeserializationOption.Lazy, false, false)]
[InlineData(FlatBufferDeserializationOption.Lazy, true, true)]
[InlineData(FlatBufferDeserializationOption.Progressive, false, false)]
[InlineData(FlatBufferDeserializationOption.Progressive, true, true)]
[InlineData(FlatBufferDeserializationOption.Greedy, false, false)]
[InlineData(FlatBufferDeserializationOption.Greedy, true, false)]
[InlineData(FlatBufferDeserializationOption.GreedyMutable, false, false)]
[InlineData(FlatBufferDeserializationOption.GreedyMutable, true, false)]
public void UseMemoryCopySerialization(FlatBufferDeserializationOption option, bool enableMemCopy, bool expectMemCopy)
{
Root t = new() { StringVector = new[] { "A", "b", "c", } };

var compiled = Root.Serializer.WithSettings(s => s.UseMemoryCopySerialization(enableMemCopy).UseDeserializationMode(option));

// overallocate
byte[] data = new byte[1024];

int maxBytes = compiled.GetMaxSize(t);
Assert.Equal(88, maxBytes);
int actualBytes = compiled.Write(data, t);
Assert.Equal(58, actualBytes);

// First test: Parse the array but don't trim the buffer. This causes the underlying
// buffer to be much larger than the actual data.
var parsed = compiled.Parse(data);
byte[] data2 = new byte[2048];
int bytesWritten = compiled.Write(data2, parsed);

if (expectMemCopy)
{
Assert.Equal(1024, bytesWritten); // We use mem copy serialization here, and we gave it 1024 bytes when we parsed. So we get 1024 bytes back out.
Assert.Equal(1024, compiled.GetMaxSize(parsed));
Assert.Throws<BufferTooSmallException>(() => compiled.Write(new byte[maxBytes], parsed));

// Repeat, but now using the trimmed array.
parsed = compiled.Parse(data.AsMemory().Slice(0, actualBytes));
bytesWritten = compiled.Write(data2, parsed);
Assert.Equal(58, bytesWritten);
Assert.Equal(58, compiled.GetMaxSize(parsed));

// Default still returns 88.
Assert.Equal(88, Root.Serializer.GetMaxSize(parsed));
}
else
{
Assert.Equal(58, bytesWritten);
Assert.Equal(88, compiled.GetMaxSize(parsed));
compiled.Write(new byte[maxBytes], parsed);
}
}

[Fact]
public void SharedStringWriters()
{
ISerializer<Root> noSharedStrings = Root.Serializer.WithSettings(opt => opt.DisableSharedStrings());
ISerializer<Root> smallHashSharedStrings = Root.Serializer.WithSettings(opt => opt.UseDefaultSharedStringWriter(1));
ISerializer<Root> largeHashSharedStrings = Root.Serializer.WithSettings(opt => opt.UseDefaultSharedStringWriter());
ISerializer<Root> perfectSharedStrings = Root.Serializer.WithSettings(opt => opt.UseSharedStringWriter<PerfectSharedStringWriter>());

{
Root root = new Root { StringVector = new[] { "a", "b", "a", "b", "a", "b", "a", } };

byte[] notShared = root.AllocateAndSerialize(noSharedStrings);
byte[] smallShared = root.AllocateAndSerialize(smallHashSharedStrings);
byte[] largeShared = root.AllocateAndSerialize(largeHashSharedStrings);
byte[] perfectShared = root.AllocateAndSerialize(perfectSharedStrings);

// Small shared doesn't accomplish anything since it alternates.
Assert.Equal(notShared.Length, smallShared.Length);
Assert.True(largeShared.Length < smallShared.Length);
Assert.Equal(largeShared.Length, perfectShared.Length);
}

{
Root root = new Root { StringVector = new[] { "a", "a", "a", "a", "b", "b", "b", "b" } };

byte[] notShared = root.AllocateAndSerialize(noSharedStrings);
byte[] smallShared = root.AllocateAndSerialize(smallHashSharedStrings);
byte[] largeShared = root.AllocateAndSerialize(largeHashSharedStrings);
byte[] perfectShared = root.AllocateAndSerialize(perfectSharedStrings);

// Small shared doesn't accomplish anything since it alternates.
Assert.True(smallShared.Length < notShared.Length);
Assert.Equal(largeShared.Length, smallShared.Length);
Assert.Equal(largeShared.Length, perfectShared.Length);
}
}

private class PerfectSharedStringWriter : ISharedStringWriter
{
private readonly Dictionary<string, List<int>> offsets = new();

public PerfectSharedStringWriter()
{
}

public bool IsDirty => this.offsets.Count > 0;

public void FlushWrites<TSpanWriter>(TSpanWriter writer, Span<byte> data, SerializationContext context) where TSpanWriter : ISpanWriter
{
foreach (var kvp in this.offsets)
{
string value = kvp.Key;

int stringOffset = writer.WriteAndProvisionString(data, value, context);

for (int i = 0; i < kvp.Value.Count; ++i)
{
writer.WriteUOffset(data, kvp.Value[i], stringOffset);
}
}

this.offsets.Clear();
}

public void Reset()
{
this.offsets.Clear();
}

public void WriteSharedString<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> data,
int offset,
string value,
SerializationContext context) where TSpanWriter : ISpanWriter
{
if (!this.offsets.TryGetValue(value, out var list))
{
list = new();
this.offsets[value] = list;
}

list.Add(offset);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

attribute "fs_serializer";
attribute "fs_unsafeStructVector";
attribute "fs_valueStruct";
attribute "fs_writeThrough";
attribute "fs_vector";
attribute "fs_sharedString";

namespace FlatSharpEndToEndTests.ClassLib.SerializerConfigurationTests;

table Root (fs_serializer)
{
StringVector : [ string ] (fs_sharedString);
}
80 changes: 79 additions & 1 deletion src/Tests/FlatSharpEndToEndTests/ClassLib/VTableTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
*/

using FlatSharp.Internal;
using System.IO;

namespace FlatSharpEndToEndTests.VTables;
namespace FlatSharpEndToEndTests.ClassLib.VTables;

public class VTableTests
{
Expand All @@ -43,6 +44,83 @@ public class VTableTests
[Fact]
public void Test_VTableGeneric() => this.RunTests<VTableGeneric>(255, VTableGeneric.Create);

[Fact]
public void InitializeVTable_NormalTable()
{
byte[] buffer =
{
8, 0, // vtable length
12, 0, // table length
4, 0, // index 0 offset
8, 0, // index 1 offset
8, 0, 0, 0, // soffset to vtable.
1, 0, 0, 0,
2, 0, 0, 0,
};

new ArrayInputBuffer(buffer).InitializeVTable(
8,
out int vtableOffset,
out nuint fieldCount,
out ReadOnlySpan<byte> fieldData);

Assert.Equal(0, vtableOffset);
Assert.Equal((nuint)2, fieldCount);
Assert.Equal(4, fieldData.Length);
}

[Fact]
public void InitializeVTable_EmptyTable()
{
byte[] buffer =
{
4, 0, // vtable length
4, 0, // table length
4, 0, 0, 0 // soffset to vtable.
};

new ArrayInputBuffer(buffer).InitializeVTable(
4,
out int vtableOffset,
out nuint fieldCount,
out ReadOnlySpan<byte> fieldData);

Assert.Equal(0, vtableOffset);
Assert.Equal((nuint)0, fieldCount);
Assert.Equal(0, fieldData.Length);
}

[Fact]
public void InitializeVTable_InvalidLength()
{
byte[] buffer =
{
3, 0, // vtable length
4, 0, // table length
4, 0, 0, 0 // soffset to vtable.
};

var ex = Assert.Throws<InvalidDataException>(() =>
new ArrayInputBuffer(buffer).InitializeVTable(4, out _, out _, out _));

Assert.Equal(
"FlatBuffer was in an invalid format: VTable was not long enough to be valid.",
ex.Message);
}

[Fact]
public void ReadUOffset()
{
byte[] buffer = { 4, 0, 0, 0 };
Assert.Equal(4, new ArrayInputBuffer(buffer).ReadUOffset(0));

buffer = new byte[] { 3, 0, 0, 0 };
var ex = Assert.Throws<InvalidDataException>(() => new ArrayInputBuffer(buffer).ReadUOffset(0));
Assert.Equal(
"FlatBuffer was in an invalid format: Decoded uoffset_t had value less than 4. Value = 3",
ex.Message);
}

private void RunTests<TVTable>(int expectedMaxIndex, CreateCallback<TVTable> callback)
where TVTable : struct, IVTable
{
Expand Down
26 changes: 23 additions & 3 deletions src/Tests/FlatSharpEndToEndTests/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,37 @@ public static IEnumerable<FlatBufferDeserializationOption> AllOptions

public static byte[] AllocateAndSerialize<T>(this T item) where T : class, IFlatBufferSerializable<T>
{
byte[] data = new byte[item.Serializer.GetMaxSize(item)];
int bytesWritten = item.Serializer.Write(data, item);
return item.AllocateAndSerialize(item.Serializer);
}

public static byte[] AllocateAndSerialize<T>(this T item, ISerializer<T> serializer) where T : class, IFlatBufferSerializable<T>
{
byte[] data = new byte[serializer.GetMaxSize(item)];
int bytesWritten = serializer.Write(data, item);
return data.AsMemory().Slice(0, bytesWritten).ToArray();
}

public static T SerializeAndParse<T>(this T item) where T : class, IFlatBufferSerializable<T>
{
return SerializeAndParse<T>(item, item.Serializer);
}

public static T SerializeAndParse<T>(
this T item,
FlatBufferDeserializationOption? option = null) where T : class, IFlatBufferSerializable<T>
FlatBufferDeserializationOption? option) where T : class, IFlatBufferSerializable<T>
{
byte[] buffer = item.AllocateAndSerialize();
return item.Serializer.Parse(buffer, option);
}

public static T SerializeAndParse<T>(
this T item,
ISerializer<T>? serializer) where T : class, IFlatBufferSerializable<T>
{
serializer ??= item.Serializer;

byte[] buffer = item.AllocateAndSerialize();
return serializer.Parse(buffer);
}
}

Loading