Skip to content

Commit

Permalink
Improve Stryker Coverage (#365)
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescourtney authored Jan 31, 2023
1 parent 4163907 commit c237644
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 29 deletions.
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

0 comments on commit c237644

Please sign in to comment.