namespace QWERTYkez.Mensura.Tests; public class DoubleExtensions { #region 1. Тесты скалярных типов (Обычные, Экзотические и Nullable) [Fact] public void ToDouble_Should_Convert_ExoticTypes_Without_InvalidCastException() { // Проверяем типы, которые раньше могли падать в рантайме из-за Convert.ToDouble(object) Half halfValue = (Half)3.14f; Int128 int128Value = Int128.Parse("1234567890123456789012345"); UInt128 uInt128Value = UInt128.Parse("9876543210987654321098765"); nint nintValue = 42; // Act & Assert Assert.Equal(3.14, halfValue.ToDouble(), 2); Assert.Equal((double)int128Value, int128Value.ToDouble()); Assert.Equal((double)uInt128Value, uInt128Value.ToDouble()); Assert.Equal(42.0, nintValue.ToDouble()); } [Theory] [InlineData((int)100, 100.0)] [InlineData((byte)5, 5.0)] [InlineData((float)1.5f, 1.5)] public void ToDouble_StandardScalars_ShouldConvertCorrectly(object input, double expected) { double result = input switch { int i => i.ToDouble(), byte b => b.ToDouble(), float f => f.ToDouble(), _ => throw new ArgumentException("Unsupported type in test") }; Assert.Equal(expected, result); } [Fact] public void ToDouble_NullableScalars_Should_Return_Zero_When_Null() { // Arrange int? nullInt = null; Half? nullHalf = null; Int128? nullInt128 = null; int? validInt = 10; Half? validHalf = (Half)2.5f; // Act & Assert Assert.Equal(0d, nullInt.ToDouble()); Assert.Equal(0d, nullHalf.ToDouble()); Assert.Equal(0d, nullInt128.ToDouble()); Assert.Equal(10d, validInt.ToDouble()); Assert.Equal(2.5d, validHalf.ToDouble(), 1); } #endregion #region 2. Тесты SIMD и оптимизаций коллекций (Различные длины массивов) [Theory] [InlineData(0)] // Пустой массив [InlineData(1)] // 1 элемент (чисто скалярный fallback) [InlineData(7)] // Нечетное число элементов [InlineData(16)] // Кратный размер (размер Vector128/Vector256) [InlineData(35)] // Большой массив с остатком для скалярного хвоста public void ToDouble_ArrayAndList_Should_Correctly_Map_Via_SIMD_Or_Fallback(int count) { // Arrange float[] sourceArray = [.. Enumerable.Range(1, count).Select(x => x * 1.5f)]; List sourceList = [.. sourceArray]; double[] destFromArray = new double[count]; double[] destFromList = new double[count]; // Act // Явное приведение к IReadOnlyCollection устраняет ошибку CS0121 (неоднозначность вызова) ((IReadOnlyCollection)sourceArray).ToDouble(destFromArray); ((IReadOnlyCollection)sourceList).ToDouble(destFromList); // Assert for (int i = 0; i < count; i++) { double expected = sourceArray[i]; Assert.Equal(expected, destFromArray[i]); Assert.Equal(expected, destFromList[i]); } } #endregion #region 3. Тесты Nullable-коллекций (Проверка на null-элементы внутри) [Fact] public void ToDouble_Nullable_Collections_Should_Keep_Nulls_In_Destination() { // Arrange UInt128?[] sourceArray = [10, null, 20, null, 30]; double?[] destination = new double?[sourceArray.Length]; // Act ((IReadOnlyCollection)sourceArray).ToDouble(destination); // Assert Assert.Equal(10d, destination[0]); Assert.Null(destination[1]); Assert.Equal(20d, destination[2]); Assert.Null(destination[3]); Assert.Equal(30d, destination[4]); } #endregion #region 4. Тесты LINQ / Отложенного выполнения (IEnumerable) [Fact] public void ToDouble_IEnumerable_Extension_Should_Handle_Execution_Types_Correctly() { // Arrange int[] array = [1, 2, 3]; List list = [4, 5, 6]; IEnumerable genericEnum = Enumerable.Range(7, 3); // Act var resFromArray = array.ToDouble(); // Быстрый бранч для массива var resFromList = list.ToDouble(); // Быстрый бранч для списка var resFromEnum = genericEnum.ToDouble(); // Медленный итератор по IEnumerable // Assert Assert.Equal([1d, 2d, 3d], resFromArray); Assert.Equal([4d, 5d, 6d], resFromList); Assert.Equal([7d, 8d, 9d], resFromEnum); } #endregion #region 5. Тесты безопасности и валидации аргументов [Fact] public void ToDouble_Should_Throw_ArgumentException_When_Destination_Is_Too_Short() { // Arrange int[] source = [1, 2, 3, 4, 5]; double[] destinationTooShort = new double[4]; // Нужен размер >= 5 // Act & Assert Assert.Throws(() => ((IReadOnlyCollection)source).ToDouble(destinationTooShort)); } [Fact] public void ToDouble_Should_Do_Nothing_And_Return_When_Collection_Is_Empty_Or_Null() { // Arrange int[]? nullArray = null; int[] emptyArray = []; double[] destination = new double[5]; // Act & Assert (Не должно выбрасывать NullReferenceException или ArgumentException) ((IReadOnlyCollection?)nullArray)!.ToDouble(destination); ((IReadOnlyCollection)emptyArray).ToDouble(destination); Assert.All(destination, x => Assert.Equal(0d, x)); // Назначение осталось нетронутым (все нули по умолчанию) } #endregion }