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

Improve Stryker Coverage #365

Merged
merged 1 commit into from
Jan 31, 2023
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
6 changes: 6 additions & 0 deletions src/FlatSharp.Runtime/Vectors/VectorsCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ namespace FlatSharp.Internal;
/// </summary>
public static class VectorsCommon
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains<T>(IList<T> vector, T? item)
{
return IndexOf<T>(vector, item) >= 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf<T>(IList<T> vector, T? item)
{
Expand Down
4 changes: 3 additions & 1 deletion src/FlatSharp/FlatBufferVectorHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public static string CreateCommonReadOnlyVectorMethods(
string nullableReference = GetNullableReferenceAnnotation(itemTypeModel);

return $$"""
public bool Contains({{baseTypeName}}{{nullableReference}} item) => this.IndexOf(item) >= 0;
public bool Contains({{baseTypeName}}{{nullableReference}} item)
=> {{typeof(VectorsCommon).GetGlobalCompilableTypeName()}}.Contains(this, item);

public int IndexOf({{baseTypeName}}{{nullableReference}} item)
=> {{typeof(VectorsCommon).GetGlobalCompilableTypeName()}}.IndexOf(this, item);
Expand All @@ -84,6 +85,7 @@ private static string GetEfficientMultiply(
string indexVariableName)
{
FlatSharpInternal.Assert(inlineSize != 0, "invalid inline size");

bool isPowerOf2 = (inlineSize & (inlineSize - 1)) == 0;
if (!isPowerOf2)
{
Expand Down
6 changes: 5 additions & 1 deletion src/FlatSharp/FlatBufferVectorHelpers_LazyUnion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ internal sealed class {{className}}<TInputBuffer>
public {{baseTypeName}} this[int index]
{
get => this.SafeParseItem(index);
set => this.WriteThrough(index, value);
set
{
{{nameof(VectorUtilities)}}.{{nameof(VectorUtilities.CheckIndex)}}(index, this.count);
this.WriteThrough(index, value);
}
}

public int Count => this.count;
Expand Down
3 changes: 3 additions & 0 deletions src/FlatSharp/FlatBufferVectorHelpers_ProgressiveUnion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ private static void GetAddress(uint index, out uint rowIndex, out uint colIndex)
int absoluteStartIndex = (int)({{GetEfficientMultiply(chunkSize, "rowIndex")}});
int copyCount = {{chunkSize}};
int remainingItems = this.count - absoluteStartIndex;

{{StrykerSuppressor.SuppressNextLine("equality")}}
if (remainingItems < {{chunkSize}})
{
copyCount = remainingItems;
Expand Down Expand Up @@ -235,6 +237,7 @@ private static void GetAddress(uint index, out uint rowIndex, out uint colIndex)

private void ProgressiveSet(int index, {{baseTypeName}} value)
{
{{nameof(VectorUtilities)}}.{{nameof(VectorUtilities.CheckIndex)}}(index, this.count);
{{nameof(VectorUtilities)}}.{{nameof(VectorUtilities.ThrowInlineNotMutableException)}}();
}

Expand Down
86 changes: 63 additions & 23 deletions src/Tests/FlatSharpEndToEndTests/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

using FlatSharp.Internal;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using Xunit.Abstractions;

namespace FlatSharpEndToEndTests;

Expand Down Expand Up @@ -97,7 +99,8 @@ public static void AssertMutationWorks<TSource, TProperty>(
TSource parent,
bool isWriteThrough,
Expression<Func<TSource, TProperty>> propertyLambda,
TProperty newValue)
TProperty newValue,
Action<TProperty, TProperty>? assertEqual = null)
{
Assert.True(parent is IFlatBufferDeserializedObject);

Expand All @@ -124,33 +127,26 @@ public static void AssertMutationWorks<TSource, TProperty>(

MemberExpression member = propertyLambda.Body as MemberExpression;
PropertyInfo propInfo = member.Member as PropertyInfo;
Action action = () => propInfo.SetMethod.Invoke(parent, new object[] { newValue });

Func<TProperty> get = () => (TProperty)propInfo.GetMethod.Invoke(parent, null);
Action set = () => propInfo.SetMethod.Invoke(parent, new object[] { newValue });

// should be equal to itself to start with.
AssertEquality(option, get(), get(), assertEqual);

switch (option)
{
case FlatBufferDeserializationOption.Lazy when isWriteThrough:
case FlatBufferDeserializationOption.Progressive when isWriteThrough:
case FlatBufferDeserializationOption.GreedyMutable when isWriteThrough is false:
action();

// For value types, validate that they are the same.
if (typeof(TProperty).IsValueType)
{
TProperty readValue = (TProperty)propInfo.GetMethod.Invoke(parent, null);
Assert.Equal<TProperty>(newValue, readValue);
}
else if (option != FlatBufferDeserializationOption.Lazy)
{
TProperty readValue = (TProperty)propInfo.GetMethod.Invoke(parent, null);
Assert.True(object.ReferenceEquals(newValue, readValue));
}

set();
AssertEquality(option, newValue, get(), assertEqual);
return;

default:
var ex = Assert.Throws<NotMutableException>(new Action(() =>
{
var ex = Assert.Throws<TargetInvocationException>(action).InnerException;
var ex = Assert.Throws<TargetInvocationException>(set).InnerException;
throw ex;
}));

Expand All @@ -169,12 +165,8 @@ public static void ValidateListVector<T>(
IList<T> items,
T newValue)
{
// This can be lots of things: NotMutable, ArgumentOfRange, IndexOutOfRange, etc.
Assert.ThrowsAny<Exception>(() => items[-1]);
Assert.ThrowsAny<Exception>(() => items[items.Count]);
Assert.ThrowsAny<Exception>(() => items[-1] = default);
Assert.ThrowsAny<Exception>(() => items[items.Count] = default);

CheckRangeExceptions(option, isWriteThrough, items);

if (items is IFlatBufferDeserializedVector vec)
{
Assert.ThrowsAny<IndexOutOfRangeException>(() => vec.OffsetOf(-1));
Expand Down Expand Up @@ -419,4 +411,52 @@ IEnumerator IEnumerable.GetEnumerator()
return ((IEnumerable)list).GetEnumerator();
}
}

private static void AssertEquality<T>(FlatBufferDeserializationOption option, T a, T b, Action<T, T>? assertEqual)
{
if (assertEqual is null)
{
if (typeof(T).IsValueType && (typeof(T).IsPrimitive || typeof(T).IsEnum))
{
assertEqual = (a, b) => Assert.Equal(a, b);
}
else if (typeof(T) == typeof(string))
{
assertEqual = (a, b) => Assert.Equal(a, b);
}
}

if (!typeof(T).IsValueType && option != FlatBufferDeserializationOption.Lazy)
{
Assert.True(object.ReferenceEquals(a, b));
}

assertEqual?.Invoke(a, b);
}

private static void CheckRangeExceptions<T>(FlatBufferDeserializationOption option, bool isWriteThrough, IList<T> list)
{
if (option == FlatBufferDeserializationOption.Lazy || option == FlatBufferDeserializationOption.Progressive)
{
Assert.Throws<IndexOutOfRangeException>(() => list[-1]);
Assert.Throws<IndexOutOfRangeException>(() => list[list.Count]);
Assert.Throws<IndexOutOfRangeException>(() => list[-1] = default);
Assert.Throws<IndexOutOfRangeException>(() => list[list.Count] = default);
}
else if (option == FlatBufferDeserializationOption.Greedy
|| (isWriteThrough && option == FlatBufferDeserializationOption.GreedyMutable))
{
Assert.Throws<ArgumentOutOfRangeException>(() => list[-1]);
Assert.Throws<ArgumentOutOfRangeException>(() => list[list.Count]);
Assert.Throws<NotMutableException>(() => list[-1] = default);
Assert.Throws<NotMutableException>(() => list[list.Count] = default);
}
else
{
Assert.Throws<ArgumentOutOfRangeException>(() => list[-1]);
Assert.Throws<ArgumentOutOfRangeException>(() => list[list.Count]);
Assert.Throws<ArgumentOutOfRangeException>(() => list[-1] = default);
Assert.Throws<ArgumentOutOfRangeException>(() => list[list.Count] = default);
}
}
}
24 changes: 23 additions & 1 deletion src/Tests/Stryker/Tests/FullTreeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,39 @@ public void RootMutations(FlatBufferDeserializationOption option)
[ClassData(typeof(DeserializationOptionClassData))]
public void VectorFieldMutations(FlatBufferDeserializationOption option)
{
static void AssertMemoryEqual(Memory<byte>? a, Memory<byte>? b)
{
Assert.Equal(a is null, b is null);
if (a is null)
{
return;
}

Helpers.AssertSequenceEqual(a.Value.Span, b.Value.Span);
}

Vectors vectors = this.CreateRoot().SerializeAndParse(option).Vectors;

Helpers.AssertMutationWorks(option, vectors, false, r => r.Indexed, null);
Helpers.AssertMutationWorks(option, vectors, false, r => r.Memory, null);
Helpers.AssertMutationWorks(option, vectors, false, r => r.Memory, null, AssertMemoryEqual);
Helpers.AssertMutationWorks(option, vectors, false, r => r.RefStruct, null);
Helpers.AssertMutationWorks(option, vectors, false, r => r.Str, null);
Helpers.AssertMutationWorks(option, vectors, false, r => r.Table, null);
Helpers.AssertMutationWorks(option, vectors, false, r => r.Union, null);
Helpers.AssertMutationWorks(option, vectors, false, r => r.ValueStruct, null);
}

[Fact]
public void VectorFieldTests_ProgressiveClear()
{
Vectors vectors = this.CreateRoot().SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] data).Vectors;
Helpers.AssertSequenceEqual(new byte[] { 1, 2, 3, 4, }, vectors.Memory.Value.Span);

data.AsSpan().Clear();

Helpers.AssertSequenceEqual(new byte[] { 0, 0, 0, 0, }, vectors.Memory.Value.Span);
}

[Fact]
public void GetMaxSize()
{
Expand Down
51 changes: 51 additions & 0 deletions src/Tests/Stryker/Tests/ScalarFieldTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using FlatSharp.Internal;
using System.Linq.Expressions;

namespace FlatSharpStrykerTests;

public class ScalarFieldTests
{
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void ValueStructTableField(FlatBufferDeserializationOption option)
{
Root r = CreateTableWithScalarField(out byte[] buffer);
Root parsed = r.SerializeAndParse(option, out byte[] actual);

Assert.Equal(r.Fields.Memory, parsed.Fields.Memory);
Helpers.AssertSequenceEqual(buffer, actual);
}

private static Root CreateTableWithScalarField(out byte[] expectedBuffer)
{
Root root = new Root
{
Fields = new()
{
Memory = 3,
}
};

expectedBuffer = new byte[]
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable.
12, 0, 0, 0, // uoffset to field 0 (fields table)
6, 0, // vtable length
8, 0, // table length
4, 0, // offset of field 0
0, 0, // padding

250, 255, 255, 255, // soffset to vtable
3, 0,

10, 0,
5, 0,
0, 0,
0, 0,
4, 0,
};

return root;
}
}
40 changes: 38 additions & 2 deletions src/Tests/Stryker/Tests/StructFieldTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,16 @@ public void ValueStructTableField(FlatBufferDeserializationOption option)
};

Helpers.AssertSequenceEqual(expectedBytes, buffer);
Helpers.AssertMutationWorks(option, fields, false, p => p.ValueStruct, default);
Helpers.AssertMutationWorks(option, fields, false, p => p.ValueStruct, new ValueStruct(), (a, b) =>
{
var av = a.Value;
var bv = b.Value;

Assert.Equal(av.A, bv.A);
Assert.Equal(av.B, bv.B);
Assert.Equal(av.C(0), bv.C(0));
Assert.Equal(av.C(1), bv.C(1));
});
}
}
finally
Expand All @@ -82,6 +91,27 @@ public void ValueStructTableField(FlatBufferDeserializationOption option)
}
}

[Fact]
public void ValueStructTableField_ProgressiveClear()
{
Root root = new Root() { Fields = new() { ValueStruct = new ValueStruct { A = 5 } } }.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer);

var fields = root.Fields;
Assert.Equal(5, fields.ValueStruct.Value.A);
buffer.AsSpan().Clear();
Assert.Equal(5, fields.ValueStruct.Value.A);
}

[Fact]
public void ValueStructStructField_ProgressiveClear()
{
Root root = new Root() { Fields = new() { RefStruct = new() { E = new ValueStruct { A = 5 } } } }.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer);

var fields = root.Fields.RefStruct;
Assert.Equal(5, fields.E.A);
buffer.AsSpan().Clear();
Assert.Equal(5, fields.E.A);
}

[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
Expand Down Expand Up @@ -130,7 +160,13 @@ public void ReferenceStructWriteThrough(FlatBufferDeserializationOption option)
Helpers.AssertMutationWorks(option, rsp, true, rsp => rsp.__flatsharp__C_1, (sbyte)6);
Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.__flatsharp__D_0, (sbyte)3);
Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.__flatsharp__D_1, (sbyte)6);
Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.E, new ValueStruct());
Helpers.AssertMutationWorks(option, rsp, false, rsp => rsp.E, new ValueStruct(), (a, b) =>
{
Assert.Equal(a.A, b.A);
Assert.Equal(a.B, b.B);
Assert.Equal(a.C(0), b.C(0));
Assert.Equal(a.C(1), b.C(1));
});

var parsed2 = Root.Serializer.Parse(buffer, option);

Expand Down
21 changes: 20 additions & 1 deletion src/Tests/Stryker/Tests/UnionFieldTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ public void InvalidGetters()
Assert.Throws<InvalidOperationException>(() => a.RefStruct);
}

[Fact]
public void ProgressiveClear()
{
Root parsed = new Root { Fields = new() { Union = new FunUnion("hi") } }.SerializeAndParse(FlatBufferDeserializationOption.Progressive, out byte[] buffer);

Fields f = parsed.Fields;
Assert.Equal("hi", f.Union.Value.str);

buffer.AsSpan().Clear();

Assert.Equal("hi", f.Union.Value.str);
}

[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void StringMember(FlatBufferDeserializationOption option)
Expand All @@ -44,7 +57,13 @@ public void StringMember(FlatBufferDeserializationOption option)
Assert.True(union.TryGet(out string str));
Assert.Equal("hello", str);

Helpers.AssertMutationWorks(option, parsed.Fields, false, f => f.Union, default);
Helpers.AssertMutationWorks(option, parsed.Fields, false, f => f.Union, new FunUnion(string.Empty), (a, b) =>
{
var av = a.Value;
var bv = b.Value;
Assert.Equal(av.Discriminator, bv.Discriminator);
Assert.Equal(av.str, bv.str);
});
}

[Fact]
Expand Down
Loading