blob: f2aca9cda1234fc95d7729cb394dd408e170f959 [file] [log] [blame]
// Source: https://github.com/dotnet/runtime/blob/v9.0.1/src/libraries/System.Memory/tests/TestHelpers.cs
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace Lucene.Net
{
public static class TestHelpers
{
public static void Validate<T>(this Span<T> span, params T[] expected) where T : struct, IEquatable<T>
{
Assert.True(span.SequenceEqual(expected));
}
public static void ValidateReferenceType<T>(this Span<T> span, params T[] expected) where T : class
{
Assert.AreEqual(span.Length, expected.Length);
for (int i = 0; i < expected.Length; i++)
{
T actual = span[i];
Assert.AreSame(expected[i], actual);
}
T ignore;
AssertThrows<IndexOutOfRangeException, T>(span, (_span) => ignore = _span[expected.Length]);
}
public static unsafe void ValidateNonNullEmpty<T>(this Span<T> span)
{
Assert.True(span.IsEmpty);
// Validate that empty Span is not normalized to null
Assert.True(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)) != null);
}
public delegate void AssertThrowsAction<T>(Span<T> span);
// Cannot use standard Assert.Throws() when testing Span - Span and closures don't get along.
public static void AssertThrows<E, T>(Span<T> span, AssertThrowsAction<T> action) where E : Exception
{
try
{
action(span);
Assert.False(true, "Expected exception: " + typeof(E).GetType());
}
catch (E)
{
}
catch (Exception wrongException)
{
Assert.False(true, "Wrong exception thrown: Expected " + typeof(E).GetType() + ": Actual: " + wrongException.GetType());
}
}
//
// The innocent looking construct:
//
// Assert.Throws<E>( () => new Span() );
//
// generates a hidden box of the Span as the return value of the lambda. This makes the IL illegal and unloadable on
// runtimes that enforce the actual Span rules (never mind that we expect never to reach the box instruction...)
//
// The workaround is to code it like this:
//
// Assert.Throws<E>( () => new Span().DontBox() );
//
// which turns the lambda return type back to "void" and eliminates the troublesome box instruction.
//
public static void DontBox<T>(this Span<T> span)
{
// This space intentionally left blank.
}
public static void Validate<T>(this ReadOnlySpan<T> span, params T[] expected) where T : struct, IEquatable<T>
{
Assert.True(span.SequenceEqual(expected));
}
public static void ValidateReferenceType<T>(this ReadOnlySpan<T> span, params T[] expected) where T : class
{
Assert.AreEqual(span.Length, expected.Length);
for (int i = 0; i < expected.Length; i++)
{
T actual = span[i];
Assert.AreSame(expected[i], actual);
}
T ignore;
AssertThrows<IndexOutOfRangeException, T>(span, (_span) => ignore = _span[expected.Length]);
}
public static unsafe void ValidateNonNullEmpty<T>(this ReadOnlySpan<T> span)
{
Assert.True(span.IsEmpty);
// Validate that empty Span is not normalized to null
Assert.True(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)) != null);
}
public delegate void AssertThrowsActionReadOnly<T>(ReadOnlySpan<T> span);
// Cannot use standard Assert.Throws() when testing Span - Span and closures don't get along.
public static void AssertThrows<E, T>(ReadOnlySpan<T> span, AssertThrowsActionReadOnly<T> action) where E : Exception
{
try
{
action(span);
Assert.False(true, "Expected exception: " + typeof(E).GetType());
}
catch (E)
{
}
catch (Exception wrongException)
{
Assert.False(true, "Wrong exception thrown: Expected " + typeof(E).GetType() + ": Actual: " + wrongException.GetType());
}
}
//
// The innocent looking construct:
//
// Assert.Throws<E>( () => new Span() );
//
// generates a hidden box of the Span as the return value of the lambda. This makes the IL illegal and unloadable on
// runtimes that enforce the actual Span rules (never mind that we expect never to reach the box instruction...)
//
// The workaround is to code it like this:
//
// Assert.Throws<E>( () => new Span().DontBox() );
//
// which turns the lambda return type back to "void" and eliminates the troublesome box instruction.
//
public static void DontBox<T>(this ReadOnlySpan<T> span)
{
// This space intentionally left blank.
}
public static void Validate<T>(this Memory<T> memory, params T[] expected) where T : IEquatable<T>
{
Assert.True(memory.Span.SequenceEqual(expected));
}
public static void ValidateReferenceType<T>(this Memory<T> memory, params T[] expected) where T : class
{
T[] bufferArray = memory.ToArray();
Assert.AreEqual(memory.Length, expected.Length);
for (int i = 0; i < expected.Length; i++)
{
T actual = bufferArray[i];
Assert.AreSame(expected[i], actual);
}
}
public static void Validate<T>(this ReadOnlyMemory<T> memory, params T[] expected) where T : IEquatable<T>
{
Assert.True(memory.Span.SequenceEqual(expected));
}
public static void ValidateReferenceType<T>(this ReadOnlyMemory<T> memory, params T[] expected) where T : class
{
T[] bufferArray = memory.ToArray();
Assert.AreEqual(memory.Length, expected.Length);
for (int i = 0; i < expected.Length; i++)
{
T actual = bufferArray[i];
Assert.AreSame(expected[i], actual);
}
}
public static void Validate<T>(Span<byte> span, T value) where T : struct
{
T read = MemoryMarshal.Read<T>(span);
Assert.AreEqual(value, read);
span.Clear();
}
public static TestStructExplicit s_testExplicitStruct = new TestStructExplicit
{
S0 = short.MaxValue,
I0 = int.MaxValue,
L0 = long.MaxValue,
US0 = ushort.MaxValue,
UI0 = uint.MaxValue,
UL0 = ulong.MaxValue,
S1 = short.MinValue,
I1 = int.MinValue,
L1 = long.MinValue,
US1 = ushort.MinValue,
UI1 = uint.MinValue,
UL1 = ulong.MinValue
};
//public static Span<byte> GetSpanBE()
//{
// Span<byte> spanBE = new byte[Unsafe.SizeOf<TestStructExplicit>()];
// WriteInt16BigEndian(spanBE, s_testExplicitStruct.S0);
// WriteInt32BigEndian(spanBE.Slice(2), s_testExplicitStruct.I0);
// WriteInt64BigEndian(spanBE.Slice(6), s_testExplicitStruct.L0);
// WriteUInt16BigEndian(spanBE.Slice(14), s_testExplicitStruct.US0);
// WriteUInt32BigEndian(spanBE.Slice(16), s_testExplicitStruct.UI0);
// WriteUInt64BigEndian(spanBE.Slice(20), s_testExplicitStruct.UL0);
// WriteInt16BigEndian(spanBE.Slice(28), s_testExplicitStruct.S1);
// WriteInt32BigEndian(spanBE.Slice(30), s_testExplicitStruct.I1);
// WriteInt64BigEndian(spanBE.Slice(34), s_testExplicitStruct.L1);
// WriteUInt16BigEndian(spanBE.Slice(42), s_testExplicitStruct.US1);
// WriteUInt32BigEndian(spanBE.Slice(44), s_testExplicitStruct.UI1);
// WriteUInt64BigEndian(spanBE.Slice(48), s_testExplicitStruct.UL1);
// Assert.Equal(56, spanBE.Length);
// return spanBE;
//}
//public static Span<byte> GetSpanLE()
//{
// Span<byte> spanLE = new byte[Unsafe.SizeOf<TestStructExplicit>()];
// WriteInt16LittleEndian(spanLE, s_testExplicitStruct.S0);
// WriteInt32LittleEndian(spanLE.Slice(2), s_testExplicitStruct.I0);
// WriteInt64LittleEndian(spanLE.Slice(6), s_testExplicitStruct.L0);
// WriteUInt16LittleEndian(spanLE.Slice(14), s_testExplicitStruct.US0);
// WriteUInt32LittleEndian(spanLE.Slice(16), s_testExplicitStruct.UI0);
// WriteUInt64LittleEndian(spanLE.Slice(20), s_testExplicitStruct.UL0);
// WriteInt16LittleEndian(spanLE.Slice(28), s_testExplicitStruct.S1);
// WriteInt32LittleEndian(spanLE.Slice(30), s_testExplicitStruct.I1);
// WriteInt64LittleEndian(spanLE.Slice(34), s_testExplicitStruct.L1);
// WriteUInt16LittleEndian(spanLE.Slice(42), s_testExplicitStruct.US1);
// WriteUInt32LittleEndian(spanLE.Slice(44), s_testExplicitStruct.UI1);
// WriteUInt64LittleEndian(spanLE.Slice(48), s_testExplicitStruct.UL1);
// Assert.Equal(56, spanLE.Length);
// return spanLE;
//}
public static string BuildString(int length, int seed)
{
Random rnd = new Random(seed);
var builder = new StringBuilder();
for (int i = 0; i < length; i++)
{
builder.Append((char)rnd.Next(65, 91));
}
return builder.ToString();
}
[StructLayout(LayoutKind.Explicit)]
public struct TestStructExplicit
{
[FieldOffset(0)]
public short S0;
[FieldOffset(2)]
public int I0;
[FieldOffset(6)]
public long L0;
[FieldOffset(14)]
public ushort US0;
[FieldOffset(16)]
public uint UI0;
[FieldOffset(20)]
public ulong UL0;
[FieldOffset(28)]
public short S1;
[FieldOffset(30)]
public int I1;
[FieldOffset(34)]
public long L1;
[FieldOffset(42)]
public ushort US1;
[FieldOffset(44)]
public uint UI1;
[FieldOffset(48)]
public ulong UL1;
}
[StructLayout(LayoutKind.Sequential)]
public sealed class TestClass
{
private double _d;
public char C0;
public char C1;
public char C2;
public char C3;
public char C4;
}
[StructLayout(LayoutKind.Sequential)]
public struct TestValueTypeWithReference
{
public int I;
public string S;
}
#pragma warning disable 0649 //Field 'SpanTests.InnerStruct.J' is never assigned to, and will always have its default value 0
internal struct StructWithReferences
{
public int I;
public InnerStruct Inner;
}
internal struct InnerStruct
{
public int J;
public object O;
}
#pragma warning restore 0649 //Field 'SpanTests.InnerStruct.J' is never assigned to, and will always have its default value 0
public enum TestEnum
{
E0,
E1,
E2,
E3,
E4,
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void DoNotIgnore<T>(T value, int consumed)
{
}
//
// { text, start, length } triplets. A "-1" in start or length means "test the overload that doesn't have that parameter."
//
public static IEnumerable<object[]> StringSliceTestData
{
get
{
foreach (string text in new string[] { string.Empty, "012" })
{
yield return new object[] { text, -1, -1 };
for (int start = 0; start <= text.Length; start++)
{
yield return new object[] { text, start, -1 };
for (int length = 0; length <= text.Length - start; length++)
{
yield return new object[] { text, start, length };
}
}
}
}
}
public static IEnumerable<object[]> StringSlice2ArgTestOutOfRangeData
{
get
{
foreach (string text in new string[] { string.Empty, "012" })
{
yield return new object[] { text, -1 };
yield return new object[] { text, int.MinValue };
yield return new object[] { text, text.Length + 1 };
yield return new object[] { text, int.MaxValue };
}
}
}
public static IEnumerable<object[]> StringSlice3ArgTestOutOfRangeData
{
get
{
foreach (string text in new string[] { string.Empty, "012" })
{
yield return new object[] { text, -1, 0 };
yield return new object[] { text, int.MinValue, 0 };
yield return new object[] { text, text.Length + 1, 0 };
yield return new object[] { text, int.MaxValue, 0 };
yield return new object[] { text, 0, -1 };
yield return new object[] { text, 0, int.MinValue };
yield return new object[] { text, 0, text.Length + 1 };
yield return new object[] { text, 0, int.MaxValue };
yield return new object[] { text, 1, text.Length };
yield return new object[] { text, 1, int.MaxValue };
yield return new object[] { text, text.Length - 1, 2 };
yield return new object[] { text, text.Length - 1, int.MaxValue };
yield return new object[] { text, text.Length, 1 };
yield return new object[] { text, text.Length, int.MaxValue };
}
}
}
/// <summary>Creates a <see cref="Memory{T}"/> with the specified values in its backing field.</summary>
public static Memory<T> DangerousCreateMemory<T>(object obj, int offset, int length)
{
Memory<T> mem = default;
object boxedMemory = mem;
typeof(Memory<T>).GetField("_object", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(boxedMemory, obj);
typeof(Memory<T>).GetField("_index", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(boxedMemory, offset);
typeof(Memory<T>).GetField("_length", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(boxedMemory, length);
return (Memory<T>)boxedMemory;
}
/// <summary>Creates a <see cref="ReadOnlyMemory{T}"/> with the specified values in its backing field.</summary>
public static ReadOnlyMemory<T> DangerousCreateReadOnlyMemory<T>(object obj, int offset, int length) =>
DangerousCreateMemory<T>(obj, offset, length);
//public static TheoryData<string[], bool> ContainsNullData => new TheoryData<string[], bool>()
//{
// { new string[] { "1", null, "2" }, true},
// { new string[] { "1", "3", "2" }, false},
// { null, false},
// { new string[] { "1", null, null }, true},
// { new string[] { null, null, null }, true},
//};
//public static TheoryData<string[], string[], bool> SequenceEqualsNullData => new TheoryData<string[], string[], bool>()
//{
// { new string[] { "1", null, "2" }, new string[] { "1", null, "2" } , true},
// { new string[] { "1", null, "2" }, new string[] { "1", "3", "2" } , false},
// { new string[] { "1", null, "2" }, new string[] { null, "3", "2" } , false},
// { new string[] { "1", null, "2" }, new string[] { null } , false},
// { new string[] { "1", null, "2" }, null , false},
// { new string[] { null, "2", "1" }, new string[] { null, "2" } , false},
// { null, new string[] { null }, false},
// { null, null , true},
// { null, new string[] { "1", "3", "2" } , false},
// { null, new string[] { "1", null, "2" } , false},
// { new string[] { "1", null, null }, new string[] { "1", null, null }, true},
// { new string[] { null, null, null }, new string[] { null, null, null }, true},
//};
//public static TheoryData<string[], int> IndexOfNullData => new TheoryData<string[], int>()
//{
// { new string[] { "1", null, "2" }, 1},
// { new string[] { "1", "3", "2" }, -1},
// { null, -1},
// { new string[] { "1", null, null }, 1},
// { new string[] { null, null, null }, 0},
//};
//public static TheoryData<string[], string[], int> IndexOfNullSequenceData => new TheoryData<string[], string[], int>()
//{
// { new string[] { "1", null, "2" }, new string[] { "1", null, "2" }, 0},
// { new string[] { "1", null, "2" }, new string[] { null }, 1},
// { new string[] { "1", null, "2" }, (string[])null, 0},
// { new string[] { "1", "3", "2" }, new string[] { "1", null, "2" }, -1},
// { new string[] { "1", "3", "2" }, new string[] { null }, -1},
// { new string[] { "1", "3", "2" }, (string[])null, 0},
// { null, new string[] { "1", null, "2" }, -1},
// { new string[] { "1", null, null }, new string[] { null, null, "2" }, -1},
// { new string[] { null, null, null }, new string[] { null, null }, 0},
//};
//public static TheoryData<string[], string[], int> IndexOfAnyNullSequenceData => new TheoryData<string[], string[], int>()
//{
// { new string[] { "1", null, "2" }, new string[] { "1", null, "2" }, 0},
// { new string[] { "1", null, "2" }, new string[] { null, null }, 1},
// { new string[] { "1", null, "2" }, new string[] { "3", null }, 1},
// { new string[] { "1", null, "2" }, new string[] { "1", "2" }, 0},
// { new string[] { "1", null, "2" }, new string[] { "3", "4" }, -1},
// { new string[] { null, null, "2" }, new string[] { "3", null }, 0},
// { new string[] { null, null, "2" }, new string[] { null, "1" }, 0},
// { new string[] { null, null, "2" }, new string[] { null, "1" }, 0},
// { new string[] { "1", "3", "2" }, new string[] { "1", null, "2" }, 0},
// { new string[] { "1", "3", "2" }, new string[] { null, null }, -1},
// { new string[] { "1", "3", "2" }, new string[] { null, "1" }, 0},
// { null, new string[] { "1", null, "2" }, -1},
// { new string[] { "1", null, null }, new string[] { null, null, "2" }, 1},
// { new string[] { null, null, null }, new string[] { null, null }, 0},
// { new string[] { "1", "3", "2" }, null, -1},
// { new string[] { "1", null, "2" }, null, -1},
//};
//public static TheoryData<string[], int> LastIndexOfNullData => new TheoryData<string[], int>()
//{
// { new string[] { "1", null, "2" }, 1},
// { new string[] { "1", "3", "2" }, -1},
// { null, -1},
// { new string[] { "1", null, null }, 2},
// { new string[] { null, null, null }, 2},
// { new string[] { null, null, "3" }, 1},
//};
//public static TheoryData<string[], string[], int> LastIndexOfNullSequenceData => new TheoryData<string[], string[], int>()
//{
// { new string[] { "1", null, "2" }, new string[] { "1", null, "2" }, 0},
// { new string[] { "1", null, "2" }, new string[] { null }, 1},
// { new string[] { "1", null, "2" }, (string[])null, 3},
// { new string[] { "1", "3", "1" }, new string[] { "1", null, "2" }, -1},
// { new string[] { "1", "3", "1" }, new string[] { "1" }, 2},
// { new string[] { "1", "3", "1" }, new string[] { null }, -1},
// { new string[] { "1", "3", "1" }, (string[])null, 3},
// { null, new string[] { "1", null, "2" }, -1},
// { new string[] { "1", null, null }, new string[] { null, null, "2" }, -1},
// { new string[] { null, null, null }, new string[] { null, null }, 1},
//};
//public static TheoryData<string[], string[], int> LastIndexOfAnyNullSequenceData => new TheoryData<string[], string[], int>()
//{
// { new string[] { "1", null, "2" }, new string[] { "1", null, "3" }, 1},
// { new string[] { "1", null, "2" }, new string[] { null, null }, 1},
// { new string[] { "1", null, "2" }, new string[] { "3", "4" }, -1},
// { new string[] { "1", null, "2" }, new string[] { "3", null }, 1},
// { new string[] { "1", null, "2" }, new string[] { "1", null }, 1},
// { new string[] { "1", null, "2" }, new string[] { null, null }, 1},
// { new string[] { "1", null, "2" }, new string[] { "1", "2" }, 2},
// { null, new string[] { "1", null, "2" }, -1},
// { new string[] { null, null, "2" }, new string[] { "3", null }, 1},
// { new string[] { null, null, "2" }, new string[] { null, "1" }, 1},
// { new string[] { null, null, "2" }, new string[] { null, "1" }, 1},
// { new string[] { "1", "3", "2" }, new string[] { null, "1" }, 0},
// { new string[] { "1", "3", "2" }, new string[] { "1", "2", null }, 2},
// { new string[] { "1", "3", "2" }, new string[] { null, null }, -1},
// { null, new string[] { null, "1" }, -1},
// { new string[] { "1", null, null }, new string[] { null, null, "2" }, 2},
// { new string[] { null, null, null }, new string[] { null, null }, 2},
// { new string[] { "1", null, "2" }, null, -1},
// { new string[] { "1", "3", "2" }, null, -1},
//};
}
}