1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using Xunit; 6 using System.Collections.Generic; 7 using System.Runtime.InteropServices; 8 using System.Runtime.CompilerServices; 9 10 using static System.Buffers.Binary.BinaryPrimitives; 11 12 namespace System 13 { 14 public static class TestHelpers 15 { 16 public static void Validate<T>(this Span<T> span, params T[] expected) where T : struct, IEquatable<T> 17 { span.SequenceEqualSystem.TestHelpers.IEquatable18 Assert.True(span.SequenceEqual(expected)); 19 } 20 ValidateReferenceType(this Span<T> span, params T[] expected)21 public static void ValidateReferenceType<T>(this Span<T> span, params T[] expected) 22 { 23 Assert.Equal(span.Length, expected.Length); 24 for (int i = 0; i < expected.Length; i++) 25 { 26 T actual = span[i]; 27 Assert.Same(expected[i], actual); 28 } 29 30 T ignore; 31 AssertThrows<IndexOutOfRangeException, T>(span, (_span) => ignore = _span[expected.Length]); 32 } 33 ValidateNonNullEmpty(this Span<T> span)34 public static unsafe void ValidateNonNullEmpty<T>(this Span<T> span) 35 { 36 Assert.True(span.IsEmpty); 37 38 // Validate that empty Span is not normalized to null 39 Assert.True(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)) != null); 40 } 41 AssertThrowsAction(Span<T> span)42 public delegate void AssertThrowsAction<T>(Span<T> span); 43 44 // Cannot use standard Assert.Throws() when testing Span - Span and closures don't get along. 45 public static void AssertThrows<E, T>(Span<T> span, AssertThrowsAction<T> action) where E : Exception 46 { 47 try 48 { 49 action(span); 50 Assert.False(true, "Expected exception: " + typeof(E).GetType()); 51 } 52 catch (E) 53 { 54 } 55 catch (Exception wrongException) 56 { 57 Assert.False(true, "Wrong exception thrown: Expected " + typeof(E).GetType() + ": Actual: " + wrongException.GetType()); 58 } 59 } 60 61 // 62 // The innocent looking construct: 63 // 64 // Assert.Throws<E>( () => new Span() ); 65 // 66 // generates a hidden box of the Span as the return value of the lambda. This makes the IL illegal and unloadable on 67 // runtimes that enforce the actual Span rules (never mind that we expect never to reach the box instruction...) 68 // 69 // The workaround is to code it like this: 70 // 71 // Assert.Throws<E>( () => new Span().DontBox() ); 72 // 73 // which turns the lambda return type back to "void" and eliminates the troublesome box instruction. 74 // DontBox(this Span<T> span)75 public static void DontBox<T>(this Span<T> span) 76 { 77 // This space intentionally left blank. 78 } 79 80 public static void Validate<T>(this ReadOnlySpan<T> span, params T[] expected) where T : struct, IEquatable<T> 81 { span.SequenceEqualSystem.TestHelpers.IEquatable82 Assert.True(span.SequenceEqual(expected)); 83 } 84 ValidateReferenceType(this ReadOnlySpan<T> span, params T[] expected)85 public static void ValidateReferenceType<T>(this ReadOnlySpan<T> span, params T[] expected) 86 { 87 Assert.Equal(span.Length, expected.Length); 88 for (int i = 0; i < expected.Length; i++) 89 { 90 T actual = span[i]; 91 Assert.Same(expected[i], actual); 92 } 93 94 T ignore; 95 AssertThrows<IndexOutOfRangeException, T>(span, (_span) => ignore = _span[expected.Length]); 96 } 97 ValidateNonNullEmpty(this ReadOnlySpan<T> span)98 public static unsafe void ValidateNonNullEmpty<T>(this ReadOnlySpan<T> span) 99 { 100 Assert.True(span.IsEmpty); 101 102 // Validate that empty Span is not normalized to null 103 Assert.True(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)) != null); 104 } 105 AssertThrowsActionReadOnly(ReadOnlySpan<T> span)106 public delegate void AssertThrowsActionReadOnly<T>(ReadOnlySpan<T> span); 107 108 // Cannot use standard Assert.Throws() when testing Span - Span and closures don't get along. 109 public static void AssertThrows<E, T>(ReadOnlySpan<T> span, AssertThrowsActionReadOnly<T> action) where E : Exception 110 { 111 try 112 { 113 action(span); 114 Assert.False(true, "Expected exception: " + typeof(E).GetType()); 115 } 116 catch (E) 117 { 118 } 119 catch (Exception wrongException) 120 { 121 Assert.False(true, "Wrong exception thrown: Expected " + typeof(E).GetType() + ": Actual: " + wrongException.GetType()); 122 } 123 } 124 125 // 126 // The innocent looking construct: 127 // 128 // Assert.Throws<E>( () => new Span() ); 129 // 130 // generates a hidden box of the Span as the return value of the lambda. This makes the IL illegal and unloadable on 131 // runtimes that enforce the actual Span rules (never mind that we expect never to reach the box instruction...) 132 // 133 // The workaround is to code it like this: 134 // 135 // Assert.Throws<E>( () => new Span().DontBox() ); 136 // 137 // which turns the lambda return type back to "void" and eliminates the troublesome box instruction. 138 // DontBox(this ReadOnlySpan<T> span)139 public static void DontBox<T>(this ReadOnlySpan<T> span) 140 { 141 // This space intentionally left blank. 142 } 143 144 public static void Validate<T>(this Memory<T> memory, params T[] expected) where T : struct, IEquatable<T> 145 { memory.Span.SequenceEqualSystem.TestHelpers.IEquatable146 Assert.True(memory.Span.SequenceEqual(expected)); 147 } 148 ValidateReferenceType(this Memory<T> memory, params T[] expected)149 public static void ValidateReferenceType<T>(this Memory<T> memory, params T[] expected) 150 { 151 T[] bufferArray = memory.ToArray(); 152 Assert.Equal(memory.Length, expected.Length); 153 for (int i = 0; i < expected.Length; i++) 154 { 155 T actual = bufferArray[i]; 156 Assert.Same(expected[i], actual); 157 } 158 } 159 160 public static void Validate<T>(this ReadOnlyMemory<T> memory, params T[] expected) where T : struct, IEquatable<T> 161 { memory.Span.SequenceEqualSystem.TestHelpers.IEquatable162 Assert.True(memory.Span.SequenceEqual(expected)); 163 } 164 ValidateReferenceType(this ReadOnlyMemory<T> memory, params T[] expected)165 public static void ValidateReferenceType<T>(this ReadOnlyMemory<T> memory, params T[] expected) 166 { 167 T[] bufferArray = memory.ToArray(); 168 Assert.Equal(memory.Length, expected.Length); 169 for (int i = 0; i < expected.Length; i++) 170 { 171 T actual = bufferArray[i]; 172 Assert.Same(expected[i], actual); 173 } 174 } 175 176 public static void Validate<T>(Span<byte> span, T value) where T : struct 177 { 178 T read = ReadMachineEndian<T>(span); Assert.EqualSystem.TestHelpers.__anon1179 Assert.Equal(value, read); span.ClearSystem.TestHelpers.__anon1180 span.Clear(); 181 } 182 183 public static TestStructExplicit s_testExplicitStruct = new TestStructExplicit 184 { 185 S0 = short.MaxValue, 186 I0 = int.MaxValue, 187 L0 = long.MaxValue, 188 US0 = ushort.MaxValue, 189 UI0 = uint.MaxValue, 190 UL0 = ulong.MaxValue, 191 S1 = short.MinValue, 192 I1 = int.MinValue, 193 L1 = long.MinValue, 194 US1 = ushort.MinValue, 195 UI1 = uint.MinValue, 196 UL1 = ulong.MinValue 197 }; 198 GetSpanBE()199 public static Span<byte> GetSpanBE() 200 { 201 Span<byte> spanBE = new byte[Unsafe.SizeOf<TestStructExplicit>()]; 202 203 WriteInt16BigEndian(spanBE, s_testExplicitStruct.S0); 204 WriteInt32BigEndian(spanBE.Slice(2), s_testExplicitStruct.I0); 205 WriteInt64BigEndian(spanBE.Slice(6), s_testExplicitStruct.L0); 206 WriteUInt16BigEndian(spanBE.Slice(14), s_testExplicitStruct.US0); 207 WriteUInt32BigEndian(spanBE.Slice(16), s_testExplicitStruct.UI0); 208 WriteUInt64BigEndian(spanBE.Slice(20), s_testExplicitStruct.UL0); 209 WriteInt16BigEndian(spanBE.Slice(28), s_testExplicitStruct.S1); 210 WriteInt32BigEndian(spanBE.Slice(30), s_testExplicitStruct.I1); 211 WriteInt64BigEndian(spanBE.Slice(34), s_testExplicitStruct.L1); 212 WriteUInt16BigEndian(spanBE.Slice(42), s_testExplicitStruct.US1); 213 WriteUInt32BigEndian(spanBE.Slice(44), s_testExplicitStruct.UI1); 214 WriteUInt64BigEndian(spanBE.Slice(48), s_testExplicitStruct.UL1); 215 216 Assert.Equal(56, spanBE.Length); 217 return spanBE; 218 } 219 GetSpanLE()220 public static Span<byte> GetSpanLE() 221 { 222 Span<byte> spanLE = new byte[Unsafe.SizeOf<TestStructExplicit>()]; 223 224 WriteInt16LittleEndian(spanLE, s_testExplicitStruct.S0); 225 WriteInt32LittleEndian(spanLE.Slice(2), s_testExplicitStruct.I0); 226 WriteInt64LittleEndian(spanLE.Slice(6), s_testExplicitStruct.L0); 227 WriteUInt16LittleEndian(spanLE.Slice(14), s_testExplicitStruct.US0); 228 WriteUInt32LittleEndian(spanLE.Slice(16), s_testExplicitStruct.UI0); 229 WriteUInt64LittleEndian(spanLE.Slice(20), s_testExplicitStruct.UL0); 230 WriteInt16LittleEndian(spanLE.Slice(28), s_testExplicitStruct.S1); 231 WriteInt32LittleEndian(spanLE.Slice(30), s_testExplicitStruct.I1); 232 WriteInt64LittleEndian(spanLE.Slice(34), s_testExplicitStruct.L1); 233 WriteUInt16LittleEndian(spanLE.Slice(42), s_testExplicitStruct.US1); 234 WriteUInt32LittleEndian(spanLE.Slice(44), s_testExplicitStruct.UI1); 235 WriteUInt64LittleEndian(spanLE.Slice(48), s_testExplicitStruct.UL1); 236 237 Assert.Equal(56, spanLE.Length); 238 return spanLE; 239 } 240 241 [StructLayout(LayoutKind.Explicit)] 242 public struct TestStructExplicit 243 { 244 [FieldOffset(0)] 245 public short S0; 246 [FieldOffset(2)] 247 public int I0; 248 [FieldOffset(6)] 249 public long L0; 250 [FieldOffset(14)] 251 public ushort US0; 252 [FieldOffset(16)] 253 public uint UI0; 254 [FieldOffset(20)] 255 public ulong UL0; 256 [FieldOffset(28)] 257 public short S1; 258 [FieldOffset(30)] 259 public int I1; 260 [FieldOffset(34)] 261 public long L1; 262 [FieldOffset(42)] 263 public ushort US1; 264 [FieldOffset(44)] 265 public uint UI1; 266 [FieldOffset(48)] 267 public ulong UL1; 268 } 269 270 [StructLayout(LayoutKind.Sequential)] 271 public sealed class TestClass 272 { 273 private double _d; 274 public char C0; 275 public char C1; 276 public char C2; 277 public char C3; 278 public char C4; 279 } 280 281 [StructLayout(LayoutKind.Sequential)] 282 public struct TestValueTypeWithReference 283 { 284 public int I; 285 public string S; 286 } 287 288 public enum TestEnum 289 { 290 e0, 291 e1, 292 e2, 293 e3, 294 e4, 295 } 296 297 [MethodImpl(MethodImplOptions.NoInlining)] DoNotIgnore(T value, int consumed)298 public static void DoNotIgnore<T>(T value, int consumed) 299 { 300 } 301 302 // 303 // { text, start, length } triplets. A "-1" in start or length means "test the overload that doesn't have that parameter." 304 // 305 public static IEnumerable<object[]> StringSliceTestData 306 { 307 get 308 { 309 foreach (string text in new string[] { string.Empty, "012" }) 310 { 311 yield return new object[] { text, -1, -1 }; 312 for (int start = 0; start <= text.Length; start++) 313 { 314 yield return new object[] { text, start, -1 }; 315 316 for (int length = 0; length <= text.Length - start; length++) 317 { 318 yield return new object[] { text, start, length }; 319 } 320 } 321 } 322 } 323 } 324 325 public static IEnumerable<object[]> StringSlice2ArgTestOutOfRangeData 326 { 327 get 328 { 329 foreach (string text in new string[] { string.Empty, "012" }) 330 { 331 yield return new object[] { text, -1 }; 332 yield return new object[] { text, int.MinValue }; 333 334 yield return new object[] { text, text.Length + 1 }; 335 yield return new object[] { text, int.MaxValue }; 336 } 337 } 338 } 339 340 public static IEnumerable<object[]> StringSlice3ArgTestOutOfRangeData 341 { 342 get 343 { 344 foreach (string text in new string[] { string.Empty, "012" }) 345 { 346 yield return new object[] { text, -1, 0 }; 347 yield return new object[] { text, int.MinValue, 0 }; 348 349 yield return new object[] { text, text.Length + 1, 0 }; 350 yield return new object[] { text, int.MaxValue, 0 }; 351 352 yield return new object[] { text, 0, -1 }; 353 yield return new object[] { text, 0, int.MinValue }; 354 355 yield return new object[] { text, 0, text.Length + 1 }; 356 yield return new object[] { text, 0, int.MaxValue }; 357 358 yield return new object[] { text, 1, text.Length }; 359 yield return new object[] { text, 1, int.MaxValue }; 360 361 yield return new object[] { text, text.Length - 1, 2 }; 362 yield return new object[] { text, text.Length - 1, int.MaxValue }; 363 364 yield return new object[] { text, text.Length, 1 }; 365 yield return new object[] { text, text.Length, int.MaxValue }; 366 } 367 } 368 } 369 } 370 } 371 372