// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Runtime.CompilerServices; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CommunityToolkit.HighPerformance.UnitTests; /* ==================================================================== * NOTE * ==================================================================== * All the tests here mirror the ones for Memory2D, as the two types * are basically the same except for some small differences in return types * or some checks being done upon construction. See comments in the test * file for Memory2D for more info on these tests. */ [TestClass] public class Test_ReadOnlyMemory2DT { [TestMethod] public void Test_ReadOnlyMemory2DT_Empty() { ReadOnlyMemory2D empty1 = default; Assert.IsTrue(empty1.IsEmpty); Assert.AreEqual(empty1.Length, 0); Assert.AreEqual(empty1.Width, 0); Assert.AreEqual(empty1.Height, 0); ReadOnlyMemory2D empty2 = ReadOnlyMemory2D.Empty; Assert.IsTrue(empty2.IsEmpty); Assert.AreEqual(empty2.Length, 0); Assert.AreEqual(empty2.Width, 0); Assert.AreEqual(empty2.Height, 0); } [TestMethod] public void Test_ReadOnlyMemory2DT_Array1DConstructor() { int[] array = { 1, 2, 3, 4, 5, 6 }; ReadOnlyMemory2D memory2d = new(array, 1, 2, 2, 1); Assert.IsFalse(memory2d.IsEmpty); Assert.AreEqual(memory2d.Length, 4); Assert.AreEqual(memory2d.Width, 2); Assert.AreEqual(memory2d.Height, 2); Assert.AreEqual(memory2d.Span[0, 0], 2); Assert.AreEqual(memory2d.Span[1, 1], 6); // Here we check to ensure a covariant array conversion is allowed for ReadOnlyMemory2D _ = new ReadOnlyMemory2D(new string[1], 1, 1); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, -99, 1, 1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, -10, 1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, 1, 1, -1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, 1, -100, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, 2, 4, 0)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, 3, 3, 0)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 1, 2, 3, 0)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, 10, 1, 120)); } [TestMethod] public void Test_ReadOnlyMemory2DT_Array2DConstructor_1() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array); Assert.IsFalse(memory2d.IsEmpty); Assert.AreEqual(memory2d.Length, 6); Assert.AreEqual(memory2d.Width, 3); Assert.AreEqual(memory2d.Height, 2); Assert.AreEqual(memory2d.Span[0, 1], 2); Assert.AreEqual(memory2d.Span[1, 2], 6); _ = new ReadOnlyMemory2D(new string[1, 2]); } [TestMethod] public void Test_ReadOnlyMemory2DT_Array2DConstructor_2() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array, 0, 1, 2, 2); Assert.IsFalse(memory2d.IsEmpty); Assert.AreEqual(memory2d.Length, 4); Assert.AreEqual(memory2d.Width, 2); Assert.AreEqual(memory2d.Height, 2); Assert.AreEqual(memory2d.Span[0, 0], 2); Assert.AreEqual(memory2d.Span[1, 1], 6); _ = new ReadOnlyMemory2D(new string[1, 2]); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(new string[1, 2], 0, 0, 2, 2)); } [TestMethod] public void Test_ReadOnlyMemory2DT_Array3DConstructor_1() { int[,,] array = { { { 1, 2, 3 }, { 4, 5, 6 } }, { { 10, 20, 30 }, { 40, 50, 60 } } }; ReadOnlyMemory2D memory2d = new(array, 1); Assert.IsFalse(memory2d.IsEmpty); Assert.AreEqual(memory2d.Length, 6); Assert.AreEqual(memory2d.Width, 3); Assert.AreEqual(memory2d.Height, 2); Assert.AreEqual(memory2d.Span[0, 1], 20); Assert.AreEqual(memory2d.Span[1, 2], 60); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, -1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 20)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 2)); } [TestMethod] public void Test_ReadOnlyMemory2DT_Array3DConstructor_2() { int[,,] array = { { { 1, 2, 3 }, { 4, 5, 6 } }, { { 10, 20, 30 }, { 40, 50, 60 } } }; ReadOnlyMemory2D memory2d = new(array, 1, 0, 1, 2, 2); Assert.IsFalse(memory2d.IsEmpty); Assert.AreEqual(memory2d.Length, 4); Assert.AreEqual(memory2d.Width, 2); Assert.AreEqual(memory2d.Height, 2); Assert.AreEqual(memory2d.Span[0, 0], 20); Assert.AreEqual(memory2d.Span[1, 1], 60); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, -1, 1, 1, 1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 1, -1, 1, 1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 1, 1, -1, 1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 1, 1, 1, -1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 1, 1, 1, 1, -1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 2, 0, 0, 2, 3)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, 0, 1, 2, 3)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, 0, 0, 2, 4)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array, 0, 0, 0, 3, 3)); } #if NET6_0_OR_GREATER [TestMethod] public void Test_ReadOnlyMemory2DT_ReadOnlyMemoryConstructor() { ReadOnlyMemory memory = new[] { 1, 2, 3, 4, 5, 6 }; ReadOnlyMemory2D memory2d = memory.AsMemory2D(1, 2, 2, 1); Assert.IsFalse(memory2d.IsEmpty); Assert.AreEqual(memory2d.Length, 4); Assert.AreEqual(memory2d.Width, 2); Assert.AreEqual(memory2d.Height, 2); Assert.AreEqual(memory2d.Span[0, 0], 2); Assert.AreEqual(memory2d.Span[1, 1], 6); _ = Assert.ThrowsException(() => memory.AsMemory2D(-99, 1, 1, 1)); _ = Assert.ThrowsException(() => memory.AsMemory2D(0, -10, 1, 1)); _ = Assert.ThrowsException(() => memory.AsMemory2D(0, 1, 1, -1)); _ = Assert.ThrowsException(() => memory.AsMemory2D(0, 1, -100, 1)); _ = Assert.ThrowsException(() => memory.AsMemory2D(0, 2, 4, 0)); _ = Assert.ThrowsException(() => memory.AsMemory2D(0, 3, 3, 0)); _ = Assert.ThrowsException(() => memory.AsMemory2D(1, 2, 3, 0)); _ = Assert.ThrowsException(() => memory.AsMemory2D(0, 10, 1, 120)); } #endif [TestMethod] public void Test_ReadOnlyMemory2DT_Slice_1() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array); ReadOnlyMemory2D slice1 = memory2d.Slice(1, 1, 1, 2); Assert.AreEqual(slice1.Length, 2); Assert.AreEqual(slice1.Height, 1); Assert.AreEqual(slice1.Width, 2); Assert.AreEqual(slice1.Span[0, 0], 5); Assert.AreEqual(slice1.Span[0, 1], 6); ReadOnlyMemory2D slice2 = memory2d.Slice(0, 1, 2, 2); Assert.AreEqual(slice2.Length, 4); Assert.AreEqual(slice2.Height, 2); Assert.AreEqual(slice2.Width, 2); Assert.AreEqual(slice2.Span[0, 0], 2); Assert.AreEqual(slice2.Span[1, 0], 5); Assert.AreEqual(slice2.Span[1, 1], 6); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(-1, 1, 1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(1, -1, 1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(1, 1, 1, -1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(1, 1, -1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(10, 1, 1, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(1, 12, 1, 12)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(1, 1, 55, 1)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(0, 0, 2, 4)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(0, 0, 3, 3)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(0, 1, 2, 3)); _ = Assert.ThrowsException(() => new ReadOnlyMemory2D(array).Slice(1, 0, 2, 3)); } [TestMethod] public void Test_ReadOnlyMemory2DT_Slice_2() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array); ReadOnlyMemory2D slice1 = memory2d.Slice(0, 0, 2, 2); Assert.AreEqual(slice1.Length, 4); Assert.AreEqual(slice1.Height, 2); Assert.AreEqual(slice1.Width, 2); Assert.AreEqual(slice1.Span[0, 0], 1); Assert.AreEqual(slice1.Span[1, 1], 5); ReadOnlyMemory2D slice2 = slice1.Slice(1, 0, 1, 2); Assert.AreEqual(slice2.Length, 2); Assert.AreEqual(slice2.Height, 1); Assert.AreEqual(slice2.Width, 2); Assert.AreEqual(slice2.Span[0, 0], 4); Assert.AreEqual(slice2.Span[0, 1], 5); ReadOnlyMemory2D slice3 = slice2.Slice(0, 1, 1, 1); Assert.AreEqual(slice3.Length, 1); Assert.AreEqual(slice3.Height, 1); Assert.AreEqual(slice3.Width, 1); Assert.AreEqual(slice3.Span[0, 0], 5); } [TestMethod] public void Test_ReadOnlyMemory2DT_TryGetReadOnlyMemory_1() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array); bool success = memory2d.TryGetMemory(out ReadOnlyMemory memory); #if NETFRAMEWORK Assert.IsFalse(success); Assert.IsTrue(memory.IsEmpty); #else Assert.IsTrue(success); Assert.AreEqual(memory.Length, array.Length); Assert.IsTrue(Unsafe.AreSame(ref array[0, 0], ref Unsafe.AsRef(memory.Span[0]))); #endif } [TestMethod] public void Test_ReadOnlyMemory2DT_TryGetReadOnlyMemory_2() { int[] array = { 1, 2, 3, 4 }; ReadOnlyMemory2D memory2d = new(array, 2, 2); bool success = memory2d.TryGetMemory(out ReadOnlyMemory memory); Assert.IsTrue(success); Assert.AreEqual(memory.Length, array.Length); Assert.AreEqual(memory.Span[2], 3); } #if NET6_0_OR_GREATER [TestMethod] public void Test_ReadOnlyMemory2DT_TryGetReadOnlyMemory_3() { ReadOnlyMemory data = new[] { 1, 2, 3, 4 }; ReadOnlyMemory2D memory2d = data.AsMemory2D(2, 2); bool success = memory2d.TryGetMemory(out ReadOnlyMemory memory); Assert.IsTrue(success); Assert.AreEqual(memory.Length, data.Length); Assert.AreEqual(memory.Span[2], 3); } #endif [TestMethod] public unsafe void Test_ReadOnlyMemory2DT_Pin_1() { int[] array = { 1, 2, 3, 4 }; ReadOnlyMemory2D memory2d = new(array, 2, 2); using System.Buffers.MemoryHandle pin = memory2d.Pin(); Assert.AreEqual(((int*)pin.Pointer)[0], 1); Assert.AreEqual(((int*)pin.Pointer)[3], 4); } [TestMethod] public unsafe void Test_ReadOnlyMemory2DT_Pin_2() { int[] array = { 1, 2, 3, 4 }; ReadOnlyMemory2D memory2d = new(array, 2, 2); using System.Buffers.MemoryHandle pin = memory2d.Pin(); Assert.AreEqual(((int*)pin.Pointer)[0], 1); Assert.AreEqual(((int*)pin.Pointer)[3], 4); } [TestMethod] public void Test_ReadOnlyMemory2DT_ToArray_1() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array); int[,] copy = memory2d.ToArray(); Assert.AreEqual(copy.GetLength(0), array.GetLength(0)); Assert.AreEqual(copy.GetLength(1), array.GetLength(1)); CollectionAssert.AreEqual(array, copy); } [TestMethod] public void Test_ReadOnlyMemory2DT_ToArray_2() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array, 0, 0, 2, 2); int[,] copy = memory2d.ToArray(); Assert.AreEqual(copy.GetLength(0), 2); Assert.AreEqual(copy.GetLength(1), 2); int[,] expected = { { 1, 2 }, { 4, 5 } }; CollectionAssert.AreEqual(expected, copy); } [TestMethod] public void Test_ReadOnlyMemory2DT_Equals() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D readOnlyMemory2D = new(array); Assert.IsFalse(readOnlyMemory2D.Equals(null)); Assert.IsFalse(readOnlyMemory2D.Equals(new ReadOnlyMemory2D(array, 0, 1, 2, 2))); Assert.IsTrue(readOnlyMemory2D.Equals(new ReadOnlyMemory2D(array))); Assert.IsTrue(readOnlyMemory2D.Equals(readOnlyMemory2D)); Memory2D memory2d = array; Assert.IsTrue(readOnlyMemory2D.Equals((object)memory2d)); Assert.IsFalse(readOnlyMemory2D.Equals((object)memory2d.Slice(0, 1, 2, 2))); } [TestMethod] public void Test_ReadOnlyMemory2DT_GetHashCode() { Assert.AreEqual(ReadOnlyMemory2D.Empty.GetHashCode(), 0); int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array); int a = memory2d.GetHashCode(); int b = memory2d.GetHashCode(); Assert.AreEqual(a, b); int c = new ReadOnlyMemory2D(array, 0, 1, 2, 2).GetHashCode(); Assert.AreNotEqual(a, c); } [TestMethod] public void Test_ReadOnlyMemory2DT_ToString() { int[,] array = { { 1, 2, 3 }, { 4, 5, 6 } }; ReadOnlyMemory2D memory2d = new(array); string text = memory2d.ToString(); const string expected = "CommunityToolkit.HighPerformance.ReadOnlyMemory2D[2, 3]"; Assert.AreEqual(text, expected); } #if NET6_0_OR_GREATER // See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/3536 [TestMethod] [DataRow(720, 1280)] public void Test_ReadOnlyMemory2DT_CastAndSlice_WorksCorrectly(int height, int width) { ReadOnlyMemory2D data = new byte[width * height * sizeof(int)] .AsMemory() .Cast() .AsMemory2D(height: height, width: width); ReadOnlyMemory2D slice = data.Slice( row: height / 2, column: 0, height: height / 2, width: width); Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height / 2, 0]), ref Unsafe.AsRef(in slice.Span[0, 0]))); Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height / 2, width - 1]), ref Unsafe.AsRef(in slice.Span[0, width - 1]))); Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height - 1, 0]), ref Unsafe.AsRef(in slice.Span[(height / 2) - 1, 0]))); Assert.IsTrue(Unsafe.AreSame(ref Unsafe.AsRef(in data.Span[height - 1, width - 1]), ref Unsafe.AsRef(in slice.Span[(height / 2) - 1, width - 1]))); } #endif }