mirror of
https://github.com/CommunityToolkit/dotnet.git
synced 2026-10-29 11:36:51 +08:00
Updated all unit tests to consistently use the expected value as the first argument in Assert.AreEqual calls. This improves readability and aligns with best practices for test assertions.
1148 lines
33 KiB
C#
1148 lines
33 KiB
C#
// 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.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using CommunityToolkit.HighPerformance.Enumerables;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
|
|
namespace CommunityToolkit.HighPerformance.UnitTests;
|
|
|
|
[TestClass]
|
|
public class Test_Span2DT
|
|
{
|
|
[TestMethod]
|
|
public void Test_Span2DT_Empty()
|
|
{
|
|
// Like in the tests for Memory2D<T>, here we validate a number of empty spans
|
|
Span2D<int> empty1 = default;
|
|
|
|
Assert.IsTrue(empty1.IsEmpty);
|
|
Assert.AreEqual(0, empty1.Length);
|
|
Assert.AreEqual(0, empty1.Width);
|
|
Assert.AreEqual(0, empty1.Height);
|
|
|
|
Span2D<string> empty2 = Span2D<string>.Empty;
|
|
|
|
Assert.IsTrue(empty2.IsEmpty);
|
|
Assert.AreEqual(0, empty2.Length);
|
|
Assert.AreEqual(0, empty2.Width);
|
|
Assert.AreEqual(0, empty2.Height);
|
|
|
|
Span2D<int> empty3 = new int[4, 0];
|
|
|
|
Assert.IsTrue(empty3.IsEmpty);
|
|
Assert.AreEqual(0, empty3.Length);
|
|
Assert.AreEqual(0, empty3.Width);
|
|
Assert.AreEqual(4, empty3.Height);
|
|
|
|
Span2D<int> empty4 = new int[0, 7];
|
|
|
|
Assert.IsTrue(empty4.IsEmpty);
|
|
Assert.AreEqual(0, empty4.Length);
|
|
Assert.AreEqual(7, empty4.Width);
|
|
Assert.AreEqual(0, empty4.Height);
|
|
}
|
|
|
|
#if NET6_0_OR_GREATER
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_RefConstructor()
|
|
{
|
|
Span<int> span = stackalloc[]
|
|
{
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6
|
|
};
|
|
|
|
// Test for a Span2D<T> instance created from a target reference. This is only supported
|
|
// on runtimes with fast Span<T> support (as we need the API to power this with just a ref).
|
|
Span2D<int> span2d = Span2D<int>.DangerousCreate(ref span[0], 2, 3, 0);
|
|
|
|
Assert.IsFalse(span2d.IsEmpty);
|
|
Assert.AreEqual(6, span2d.Length);
|
|
Assert.AreEqual(3, span2d.Width);
|
|
Assert.AreEqual(2, span2d.Height);
|
|
|
|
span2d[0, 0] = 99;
|
|
span2d[1, 2] = 101;
|
|
|
|
// Validate that those values were mapped to the right spot in the target span
|
|
Assert.AreEqual(99, span[0]);
|
|
Assert.AreEqual(101, span[5]);
|
|
|
|
// A few cases with invalid indices
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => Span2D<int>.DangerousCreate(ref Unsafe.AsRef<int>(null), -1, 0, 0));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => Span2D<int>.DangerousCreate(ref Unsafe.AsRef<int>(null), 1, -2, 0));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => Span2D<int>.DangerousCreate(ref Unsafe.AsRef<int>(null), 1, 0, -5));
|
|
}
|
|
#endif
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_PtrConstructor()
|
|
{
|
|
int* ptr = stackalloc[]
|
|
{
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6
|
|
};
|
|
|
|
// Same as above, but creating a Span2D<T> from a raw pointer
|
|
Span2D<int> span2d = new(ptr, 2, 3, 0);
|
|
|
|
Assert.IsFalse(span2d.IsEmpty);
|
|
Assert.AreEqual(6, span2d.Length);
|
|
Assert.AreEqual(3, span2d.Width);
|
|
Assert.AreEqual(2, span2d.Height);
|
|
|
|
span2d[0, 0] = 99;
|
|
span2d[1, 2] = 101;
|
|
|
|
Assert.AreEqual(99, ptr[0]);
|
|
Assert.AreEqual(101, ptr[5]);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>((void*)0, -1, 0, 0));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>((void*)0, 1, -2, 0));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>((void*)0, 1, 0, -5));
|
|
_ = Assert.ThrowsExactly<ArgumentException>(() => new Span2D<string>((void*)0, 2, 2, 0));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Array1DConstructor()
|
|
{
|
|
int[] array =
|
|
{
|
|
1, 2, 3, 4, 5, 6
|
|
};
|
|
|
|
// Same as above, but wrapping a 1D array with data in row-major order
|
|
Span2D<int> span2d = new(array, 1, 2, 2, 1);
|
|
|
|
Assert.IsFalse(span2d.IsEmpty);
|
|
Assert.AreEqual(4, span2d.Length);
|
|
Assert.AreEqual(2, span2d.Width);
|
|
Assert.AreEqual(2, span2d.Height);
|
|
|
|
span2d[0, 0] = 99;
|
|
span2d[1, 1] = 101;
|
|
|
|
Assert.AreEqual(99, array[1]);
|
|
Assert.AreEqual(101, array[5]);
|
|
|
|
// The first check fails due to the array covariance test mentioned in the Memory2D<T> tests.
|
|
// The others just validate a number of cases with invalid arguments (eg. out of range).
|
|
_ = Assert.ThrowsExactly<ArrayTypeMismatchException>(() => new Span2D<object>(new string[1], 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, -99, 1, 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 0, -10, 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 0, 1, 1, -1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 0, 1, -100, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentException>(() => new Span2D<int>(array, 0, 10, 1, 120));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Array2DConstructor_1()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but directly wrapping a 2D array
|
|
Span2D<int> span2d = new(array);
|
|
|
|
Assert.IsFalse(span2d.IsEmpty);
|
|
Assert.AreEqual(6, span2d.Length);
|
|
Assert.AreEqual(3, span2d.Width);
|
|
Assert.AreEqual(2, span2d.Height);
|
|
|
|
span2d[0, 1] = 99;
|
|
span2d[1, 2] = 101;
|
|
|
|
Assert.AreEqual(99, array[0, 1]);
|
|
Assert.AreEqual(101, array[1, 2]);
|
|
|
|
_ = Assert.ThrowsExactly<ArrayTypeMismatchException>(() => new Span2D<object>(new string[1, 2]));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Array2DConstructor_2()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but with a custom slicing over the target 2D array
|
|
Span2D<int> span2d = new(array, 0, 1, 2, 2);
|
|
|
|
Assert.IsFalse(span2d.IsEmpty);
|
|
Assert.AreEqual(4, span2d.Length);
|
|
Assert.AreEqual(2, span2d.Width);
|
|
Assert.AreEqual(2, span2d.Height);
|
|
|
|
span2d[0, 0] = 99;
|
|
span2d[1, 1] = 101;
|
|
|
|
Assert.AreEqual(99, array[0, 1]);
|
|
Assert.AreEqual(101, array[1, 2]);
|
|
|
|
_ = Assert.ThrowsExactly<ArrayTypeMismatchException>(() => new Span2D<object>(new string[1, 2], 0, 0, 2, 2));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Array3DConstructor_1()
|
|
{
|
|
int[,,] array =
|
|
{
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
},
|
|
{
|
|
{ 10, 20, 30 },
|
|
{ 40, 50, 60 }
|
|
}
|
|
};
|
|
|
|
// Here we wrap a layer in a 3D array instead, the rest is the same
|
|
Span2D<int> span2d = new(array, 1);
|
|
|
|
Assert.IsFalse(span2d.IsEmpty);
|
|
Assert.AreEqual(6, span2d.Length);
|
|
Assert.AreEqual(3, span2d.Width);
|
|
Assert.AreEqual(2, span2d.Height);
|
|
|
|
span2d[0, 1] = 99;
|
|
span2d[1, 2] = 101;
|
|
|
|
Assert.AreEqual(10, span2d[0, 0]);
|
|
Assert.AreEqual(99, array[1, 0, 1]);
|
|
Assert.AreEqual(101, array[1, 1, 2]);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, -1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 20));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Array3DConstructor_2()
|
|
{
|
|
int[,,] array =
|
|
{
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
},
|
|
{
|
|
{ 10, 20, 30 },
|
|
{ 40, 50, 60 }
|
|
}
|
|
};
|
|
|
|
// Same as above, but also slicing a target 2D area in the 3D array layer
|
|
Span2D<int> span2d = new(array, 1, 0, 1, 2, 2);
|
|
|
|
Assert.IsFalse(span2d.IsEmpty);
|
|
Assert.AreEqual(4, span2d.Length);
|
|
Assert.AreEqual(2, span2d.Width);
|
|
Assert.AreEqual(2, span2d.Height);
|
|
|
|
span2d[0, 1] = 99;
|
|
span2d[1, 1] = 101;
|
|
|
|
Assert.AreEqual(20, span2d[0, 0]);
|
|
Assert.AreEqual(99, array[1, 0, 2]);
|
|
Assert.AreEqual(101, array[1, 1, 2]);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, -1, 1, 1, 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 1, -1, 1, 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 1, 1, -1, 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 1, 1, 1, -1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 1, 1, 1, 1, -1));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_FillAndClear_1()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Tests for the Fill and Clear APIs for Span2D<T>. These should fill
|
|
// or clear the entire wrapped 2D array (just like eg. Span<T>.Fill).
|
|
Span2D<int> span2d = new(array);
|
|
|
|
span2d.Fill(42);
|
|
|
|
Assert.IsTrue(array.Cast<int>().All(n => n == 42));
|
|
|
|
span2d.Clear();
|
|
|
|
Assert.IsTrue(array.Cast<int>().All(n => n == 0));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Fill_Empty()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but with an initial slicing as well to ensure
|
|
// these method work correctly with different internal offsets
|
|
Span2D<int> span2d = new(array, 0, 0, 0, 0);
|
|
|
|
span2d.Fill(42);
|
|
|
|
CollectionAssert.AreEqual(array, array);
|
|
|
|
span2d.Clear();
|
|
|
|
CollectionAssert.AreEqual(array, array);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_FillAndClear_2()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, just with different slicing to a target smaller 2D area
|
|
Span2D<int> span2d = new(array, 0, 1, 2, 2);
|
|
|
|
span2d.Fill(42);
|
|
|
|
int[,] filled =
|
|
{
|
|
{ 1, 42, 42 },
|
|
{ 4, 42, 42 }
|
|
};
|
|
|
|
CollectionAssert.AreEqual(array, filled);
|
|
|
|
span2d.Clear();
|
|
|
|
int[,] cleared =
|
|
{
|
|
{ 1, 0, 0 },
|
|
{ 4, 0, 0 }
|
|
};
|
|
|
|
CollectionAssert.AreEqual(array, cleared);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_CopyTo_Empty()
|
|
{
|
|
Span2D<int> span2d = Span2D<int>.Empty;
|
|
|
|
int[] target = new int[0];
|
|
|
|
// Copying an empty Span2D<T> to an empty array is just a no-op
|
|
span2d.CopyTo(target);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_CopyTo_1()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
int[] target = new int[array.Length];
|
|
|
|
// Here we copy a Span2D<T> to a target Span<T> mapping an array.
|
|
// This is valid, and the data will just be copied in row-major order.
|
|
span2d.CopyTo(target);
|
|
|
|
CollectionAssert.AreEqual(array, target);
|
|
|
|
// Exception due to the target span being too small for the source Span2D<T> instance
|
|
_ = Assert.ThrowsExactly<ArgumentException>(() => new Span2D<int>(array).CopyTo(Span<int>.Empty));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_CopyTo_2()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but with different initial slicing
|
|
Span2D<int> span2d = new(array, 0, 1, 2, 2);
|
|
|
|
int[] target = new int[4];
|
|
|
|
span2d.CopyTo(target);
|
|
|
|
int[] expected = { 2, 3, 5, 6 };
|
|
|
|
CollectionAssert.AreEqual(target, expected);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentException>(() => new Span2D<int>(array).CopyTo(Span<int>.Empty));
|
|
_ = Assert.ThrowsExactly<ArgumentException>(() => new Span2D<int>(array, 0, 1, 2, 2).CopyTo(Span<int>.Empty));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_CopyTo2D_1()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
int[,] target = new int[2, 3];
|
|
|
|
// Same as above, but copying to a target Span2D<T> instead. Note
|
|
// that this method uses the implicit T[,] to Span2D<T> conversion.
|
|
span2d.CopyTo(target);
|
|
|
|
CollectionAssert.AreEqual(array, target);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentException>(() => new Span2D<int>(array).CopyTo(Span2D<int>.Empty));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_CopyTo2D_2()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but with extra initial slicing
|
|
Span2D<int> span2d = new(array, 0, 1, 2, 2);
|
|
|
|
int[,] target = new int[2, 2];
|
|
|
|
span2d.CopyTo(target);
|
|
|
|
int[,] expected =
|
|
{
|
|
{ 2, 3 },
|
|
{ 5, 6 }
|
|
};
|
|
|
|
CollectionAssert.AreEqual(target, expected);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentException>(() => new Span2D<int>(array).CopyTo(new Span2D<int>(target)));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_TryCopyTo()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
int[] target = new int[array.Length];
|
|
|
|
// Here we test the safe TryCopyTo method, which will fail gracefully
|
|
Assert.IsTrue(span2d.TryCopyTo(target));
|
|
Assert.IsFalse(span2d.TryCopyTo(Span<int>.Empty));
|
|
|
|
int[] expected = { 1, 2, 3, 4, 5, 6 };
|
|
|
|
CollectionAssert.AreEqual(target, expected);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_TryCopyTo2D()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but copying to a 2D array with the safe TryCopyTo method
|
|
Span2D<int> span2d = new(array);
|
|
|
|
int[,] target = new int[2, 3];
|
|
|
|
Assert.IsTrue(span2d.TryCopyTo(target));
|
|
Assert.IsFalse(span2d.TryCopyTo(Span2D<int>.Empty));
|
|
|
|
int[,] expected =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
CollectionAssert.AreEqual(target, expected);
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_GetPinnableReference()
|
|
{
|
|
// Here we test that a ref from an empty Span2D<T> returns a null ref
|
|
Assert.IsTrue(Unsafe.AreSame(
|
|
ref Unsafe.AsRef<int>(null),
|
|
ref Span2D<int>.Empty.GetPinnableReference()));
|
|
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
ref int r0 = ref span2d.GetPinnableReference();
|
|
|
|
// Here we test that GetPinnableReference returns a ref to the first array element
|
|
Assert.IsTrue(Unsafe.AreSame(ref r0, ref array[0, 0]));
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_DangerousGetReference()
|
|
{
|
|
// Same as above, but using DangerousGetReference instead (faster, no conditional check)
|
|
Assert.IsTrue(Unsafe.AreSame(
|
|
ref Unsafe.AsRef<int>(null),
|
|
ref Span2D<int>.Empty.DangerousGetReference()));
|
|
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
ref int r0 = ref span2d.DangerousGetReference();
|
|
|
|
Assert.IsTrue(Unsafe.AreSame(ref r0, ref array[0, 0]));
|
|
}
|
|
|
|
#if NET6_0_OR_GREATER
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Index_Indexer_1()
|
|
{
|
|
int[,] array = new int[4, 4];
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
ref int arrayRef = ref array[1, 3];
|
|
ref int span2dRef = ref span2d[1, ^1];
|
|
|
|
Assert.IsTrue(Unsafe.AreSame(ref arrayRef, ref span2dRef));
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Index_Indexer_2()
|
|
{
|
|
int[,] array = new int[4, 4];
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
ref int arrayRef = ref array[2, 1];
|
|
ref int span2dRef = ref span2d[^2, ^3];
|
|
|
|
Assert.IsTrue(Unsafe.AreSame(ref arrayRef, ref span2dRef));
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Index_Indexer_Fail()
|
|
{
|
|
int[,] array = new int[4, 4];
|
|
|
|
_ = Assert.ThrowsExactly<IndexOutOfRangeException>(() =>
|
|
{
|
|
Span2D<int> span2d = new(array);
|
|
|
|
ref int span2dRef = ref span2d[^6, 2];
|
|
});
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Range_Indexer_1()
|
|
{
|
|
int[,] array = new int[4, 4];
|
|
|
|
Span2D<int> span2d = new(array);
|
|
Span2D<int> slice = span2d[1.., 1..];
|
|
|
|
Assert.AreEqual(9, slice.Length);
|
|
Assert.IsTrue(Unsafe.AreSame(ref array[1, 1], ref slice[0, 0]));
|
|
Assert.IsTrue(Unsafe.AreSame(ref array[3, 3], ref slice[2, 2]));
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Range_Indexer_2()
|
|
{
|
|
int[,] array = new int[4, 4];
|
|
|
|
Span2D<int> span2d = new(array);
|
|
Span2D<int> slice = span2d[0..^2, 1..^1];
|
|
|
|
Assert.AreEqual(4, slice.Length);
|
|
Assert.IsTrue(Unsafe.AreSame(ref array[0, 1], ref slice[0, 0]));
|
|
Assert.IsTrue(Unsafe.AreSame(ref array[1, 2], ref slice[1, 1]));
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Range_Indexer_Fail()
|
|
{
|
|
int[,] array = new int[4, 4];
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() =>
|
|
{
|
|
Span2D<int> span2d = new(array);
|
|
|
|
_ = span2d[0..6, 2..^1];
|
|
});
|
|
}
|
|
#endif
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Slice_1()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Here we have a number of tests that just take an initial 2D array, create a Span2D<T>,
|
|
// perform a number of slicing operations and then validate the parameters for the resulting
|
|
// instances, and that the indexer works correctly and maps to the right original elements.
|
|
Span2D<int> span2d = new(array);
|
|
|
|
Span2D<int> slice1 = span2d.Slice(1, 1, 1, 2);
|
|
|
|
Assert.AreEqual(2, slice1.Length);
|
|
Assert.AreEqual(1, slice1.Height);
|
|
Assert.AreEqual(2, slice1.Width);
|
|
Assert.AreEqual(5, slice1[0, 0]);
|
|
Assert.AreEqual(6, slice1[0, 1]);
|
|
|
|
Span2D<int> slice2 = span2d.Slice(0, 1, 2, 2);
|
|
|
|
Assert.AreEqual(4, slice2.Length);
|
|
Assert.AreEqual(2, slice2.Height);
|
|
Assert.AreEqual(2, slice2.Width);
|
|
Assert.AreEqual(2, slice2[0, 0]);
|
|
Assert.AreEqual(5, slice2[1, 0]);
|
|
Assert.AreEqual(6, slice2[1, 1]);
|
|
|
|
// Some checks for invalid arguments
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).Slice(-1, 1, 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).Slice(1, -1, 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).Slice(1, 1, 1, -1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).Slice(1, 1, -1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).Slice(10, 1, 1, 1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).Slice(1, 12, 1, 12));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).Slice(1, 1, 55, 1));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Slice_2()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
// Same as above, but with some different slicing
|
|
Span2D<int> slice1 = span2d.Slice(0, 0, 2, 2);
|
|
|
|
Assert.AreEqual(4, slice1.Length);
|
|
Assert.AreEqual(2, slice1.Height);
|
|
Assert.AreEqual(2, slice1.Width);
|
|
Assert.AreEqual(1, slice1[0, 0]);
|
|
Assert.AreEqual(5, slice1[1, 1]);
|
|
|
|
Span2D<int> slice2 = slice1.Slice(1, 0, 1, 2);
|
|
|
|
Assert.AreEqual(2, slice2.Length);
|
|
Assert.AreEqual(1, slice2.Height);
|
|
Assert.AreEqual(2, slice2.Width);
|
|
Assert.AreEqual(4, slice2[0, 0]);
|
|
Assert.AreEqual(5, slice2[0, 1]);
|
|
|
|
Span2D<int> slice3 = slice2.Slice(0, 1, 1, 1);
|
|
|
|
Assert.AreEqual(1, slice3.Length);
|
|
Assert.AreEqual(1, slice3.Height);
|
|
Assert.AreEqual(1, slice3.Width);
|
|
Assert.AreEqual(5, slice3[0, 0]);
|
|
}
|
|
|
|
#if NET6_0_OR_GREATER
|
|
[TestMethod]
|
|
public void Test_Span2DT_GetRowSpan()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
// Here we create a Span2D<T> from a 2D array and want to get a Span<T> from
|
|
// a specific row. This is only supported on runtimes with fast Span<T> support
|
|
// for the same reason mentioned in the Memory2D<T> tests (we need the Span<T>
|
|
// constructor that only takes a target ref). Then we just get some references
|
|
// to items in this span and compare them against references into the original
|
|
// 2D array to ensure they match and point to the correct elements from there.
|
|
Span<int> span = span2d.GetRowSpan(1);
|
|
|
|
Assert.IsTrue(Unsafe.AreSame(
|
|
ref span[0],
|
|
ref array[1, 0]));
|
|
Assert.IsTrue(Unsafe.AreSame(
|
|
ref span[2],
|
|
ref array[1, 2]));
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).GetRowSpan(-1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).GetRowSpan(5));
|
|
}
|
|
#endif
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_TryGetSpan_From1DArray_1()
|
|
{
|
|
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
|
|
Span2D<int> span2d = new(array, 3, 3);
|
|
|
|
bool success = span2d.TryGetSpan(out Span<int> span);
|
|
|
|
Assert.IsTrue(success);
|
|
Assert.AreEqual(span.Length, span2d.Length);
|
|
Assert.IsTrue(Unsafe.AreSame(ref array[0], ref span[0]));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_TryGetSpan_From1DArray_2()
|
|
{
|
|
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
|
|
Span2D<int> span2d = new Span2D<int>(array, 3, 3).Slice(1, 0, 2, 3);
|
|
|
|
bool success = span2d.TryGetSpan(out Span<int> span);
|
|
|
|
Assert.IsTrue(success);
|
|
Assert.AreEqual(span.Length, span2d.Length);
|
|
Assert.IsTrue(Unsafe.AreSame(ref array[3], ref span[0]));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_TryGetSpan_From1DArray_3()
|
|
{
|
|
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
|
|
|
Span2D<int> span2d = new Span2D<int>(array, 3, 3).Slice(0, 1, 3, 2);
|
|
|
|
bool success = span2d.TryGetSpan(out Span<int> span);
|
|
|
|
Assert.IsFalse(success);
|
|
Assert.AreEqual(0, span.Length);
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/WindowsCommunityToolkit/issues/3947
|
|
[TestMethod]
|
|
public void Test_Span2DT_TryGetSpan_From1DArray_4()
|
|
{
|
|
int[] array = new int[128];
|
|
Span2D<int> span2d = new(array, 8, 16);
|
|
|
|
bool success = span2d.TryGetSpan(out Span<int> span);
|
|
|
|
Assert.IsTrue(success);
|
|
Assert.AreEqual(span.Length, span2d.Length);
|
|
Assert.IsTrue(Unsafe.AreSame(ref array[0], ref span[0]));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_TryGetSpan_From2DArray_1()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
// This API tries to get a Span<T> for the entire contents of Span2D<T>.
|
|
// This only works on runtimes if the underlying data is contiguous
|
|
// and of a size that can fit into a single Span<T>. In this specific test,
|
|
// this is not expected to work on .NET Standard 2.0 because it can't create a
|
|
// Span<T> from a 2D array (reasons explained in the comments for the test above).
|
|
bool success = span2d.TryGetSpan(out Span<int> span);
|
|
|
|
#if NETFRAMEWORK
|
|
// Can't get a Span<T> over a T[,] array on .NET Standard 2.0
|
|
Assert.IsFalse(success);
|
|
Assert.AreEqual(0, span.Length);
|
|
#else
|
|
Assert.IsTrue(success);
|
|
Assert.AreEqual(span.Length, span2d.Length);
|
|
#endif
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_TryGetSpan_From2DArray_2()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but this will always fail because we're creating
|
|
// a Span2D<T> wrapping non contiguous data (the pitch is not 0).
|
|
Span2D<int> span2d = new(array, 0, 0, 2, 2);
|
|
|
|
bool success = span2d.TryGetSpan(out Span<int> span);
|
|
|
|
Assert.IsFalse(success);
|
|
Assert.IsTrue(span.IsEmpty);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_ToArray_1()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Here we create a Span2D<T> and verify that ToArray() produces
|
|
// a 2D array that is identical to the original one being wrapped.
|
|
Span2D<int> span2d = new(array);
|
|
|
|
int[,] copy = span2d.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_Span2DT_ToArray_2()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but with extra initial slicing
|
|
Span2D<int> span2d = new(array, 0, 0, 2, 2);
|
|
|
|
int[,] copy = span2d.ToArray();
|
|
|
|
Assert.AreEqual(2, copy.GetLength(0));
|
|
Assert.AreEqual(2, copy.GetLength(1));
|
|
|
|
int[,] expected =
|
|
{
|
|
{ 1, 2 },
|
|
{ 4, 5 }
|
|
};
|
|
|
|
CollectionAssert.AreEqual(expected, copy);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_Equals()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Span2D<T>.Equals always throw (this mirrors the behavior of Span<T>.Equals)
|
|
_ = Assert.ThrowsExactly<NotSupportedException>(() =>
|
|
{
|
|
Span2D<int> span2d = new(array);
|
|
|
|
_ = span2d.Equals(null);
|
|
});
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_GetHashCode()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, this always throws
|
|
_ = Assert.ThrowsExactly<NotSupportedException>(() =>
|
|
{
|
|
Span2D<int> span2d = new(array);
|
|
|
|
_ = span2d.GetHashCode();
|
|
});
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_ToString()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
Span2D<int> span2d = new(array);
|
|
|
|
// Verify that we get the nicely formatted string
|
|
string text = span2d.ToString();
|
|
|
|
const string expected = "CommunityToolkit.HighPerformance.Span2D<System.Int32>[2, 3]";
|
|
|
|
Assert.AreEqual(expected, text);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_opEquals()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Create two Span2D<T> instances wrapping the same array with the same
|
|
// parameters, and verify that the equality operators work correctly.
|
|
Span2D<int> span2d_1 = new(array);
|
|
Span2D<int> span2d_2 = new(array);
|
|
|
|
Assert.IsTrue(span2d_1 == span2d_2);
|
|
Assert.IsFalse(span2d_1 == Span2D<int>.Empty);
|
|
Assert.IsTrue(Span2D<int>.Empty == Span2D<int>.Empty);
|
|
|
|
// Same as above, but verify that a sliced span is not reported as equal
|
|
Span2D<int> span2d_3 = new(array, 0, 0, 2, 2);
|
|
|
|
Assert.IsFalse(span2d_1 == span2d_3);
|
|
Assert.IsFalse(span2d_3 == Span2D<int>.Empty);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_ImplicitCast()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Verify that an explicit constructor and the implicit conversion
|
|
// operator generate an identical Span2D<T> instance from the array.
|
|
Span2D<int> span2d_1 = array;
|
|
Span2D<int> span2d_2 = new(array);
|
|
|
|
Assert.IsTrue(span2d_1 == span2d_2);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_GetRow()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Get a target row and verify the contents match with our data
|
|
RefEnumerable<int> enumerable = new Span2D<int>(array).GetRow(1);
|
|
|
|
int[] expected = { 4, 5, 6 };
|
|
|
|
CollectionAssert.AreEqual(enumerable.ToArray(), expected);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).GetRow(-1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).GetRow(2));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).GetRow(1000));
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Pointer_GetRow()
|
|
{
|
|
int* array = stackalloc[]
|
|
{
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6
|
|
};
|
|
|
|
// Same as above, but with a Span2D<T> wrapping a raw pointer
|
|
RefEnumerable<int> enumerable = new Span2D<int>(array, 2, 3, 0).GetRow(1);
|
|
|
|
int[] expected = { 4, 5, 6 };
|
|
|
|
CollectionAssert.AreEqual(enumerable.ToArray(), expected);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 2, 3, 0).GetRow(-1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 2, 3, 0).GetRow(2));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 2, 3, 0).GetRow(1000));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_GetColumn()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
// Same as above, but getting a column instead
|
|
RefEnumerable<int> enumerable = new Span2D<int>(array).GetColumn(2);
|
|
|
|
int[] expected = { 3, 6 };
|
|
|
|
CollectionAssert.AreEqual(enumerable.ToArray(), expected);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).GetColumn(-1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).GetColumn(3));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array).GetColumn(1000));
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Pointer_GetColumn()
|
|
{
|
|
int* array = stackalloc[]
|
|
{
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6
|
|
};
|
|
|
|
// Same as above, but wrapping a raw pointer
|
|
RefEnumerable<int> enumerable = new Span2D<int>(array, 2, 3, 0).GetColumn(2);
|
|
|
|
int[] expected = { 3, 6 };
|
|
|
|
CollectionAssert.AreEqual(enumerable.ToArray(), expected);
|
|
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 2, 3, 0).GetColumn(-1));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 2, 3, 0).GetColumn(3));
|
|
_ = Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => new Span2D<int>(array, 2, 3, 0).GetColumn(1000));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_GetEnumerator()
|
|
{
|
|
int[,] array =
|
|
{
|
|
{ 1, 2, 3 },
|
|
{ 4, 5, 6 }
|
|
};
|
|
|
|
int[] result = new int[4];
|
|
int i = 0;
|
|
|
|
// Here we want to test the Span2D<T> enumerator. We create a Span2D<T> instance over
|
|
// a given section of the initial 2D array, then iterate over it and store the items
|
|
// into a temporary array. We then just compare the contents to ensure they match.
|
|
foreach (ref int item in new Span2D<int>(array, 0, 1, 2, 2))
|
|
{
|
|
// Check the reference to ensure it points to the right original item
|
|
Assert.IsTrue(Unsafe.AreSame(
|
|
ref array[i / 2, (i % 2) + 1],
|
|
ref item));
|
|
|
|
// Also store the value to compare it later (redundant, but just in case)
|
|
result[i++] = item;
|
|
}
|
|
|
|
int[] expected = { 2, 3, 5, 6 };
|
|
|
|
CollectionAssert.AreEqual(result, expected);
|
|
}
|
|
|
|
[TestMethod]
|
|
public unsafe void Test_Span2DT_Pointer_GetEnumerator()
|
|
{
|
|
int* array = stackalloc[]
|
|
{
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6
|
|
};
|
|
|
|
int[] result = new int[4];
|
|
int i = 0;
|
|
|
|
// Same test as above, but wrapping a raw pointer
|
|
foreach (ref int item in new Span2D<int>(array + 1, 2, 2, 1))
|
|
{
|
|
// Check the reference again
|
|
Assert.IsTrue(Unsafe.AreSame(
|
|
ref Unsafe.AsRef<int>(&array[((i / 2) * 3) + (i % 2) + 1]),
|
|
ref item));
|
|
|
|
result[i++] = item;
|
|
}
|
|
|
|
int[] expected = { 2, 3, 5, 6 };
|
|
|
|
CollectionAssert.AreEqual(result, expected);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Test_Span2DT_GetEnumerator_Empty()
|
|
{
|
|
Span2D<int>.Enumerator enumerator = Span2D<int>.Empty.GetEnumerator();
|
|
|
|
// Ensure that an enumerator from an empty Span2D<T> can't move next
|
|
Assert.IsFalse(enumerator.MoveNext());
|
|
}
|
|
}
|