tests
This commit is contained in:
170
QWERTYkez.Mensura.Tests/AggregateUnitExtensions.cs
Normal file
170
QWERTYkez.Mensura.Tests/AggregateUnitExtensions.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace QWERTYkez.Mensura.Tests;
|
||||
|
||||
public class AggregateUnitExtensions
|
||||
{
|
||||
// Вспомогательный метод для создания объекта Length.
|
||||
// Если у вас используется фабричный метод (например, Length.FromMeters), замените код внутри.
|
||||
private static Length CreateLength(double value) => value * Length._MilliMeter;
|
||||
|
||||
#region Инфраструктура Рефлексии (Invoker)
|
||||
|
||||
private delegate Length SpanDelegate(ReadOnlySpan<Length> units);
|
||||
private delegate Length SpanNullableDelegate(ReadOnlySpan<Length?> units);
|
||||
|
||||
private static class Invoker
|
||||
{
|
||||
private static readonly Type ExtType;
|
||||
|
||||
static Invoker()
|
||||
{
|
||||
// Находим внутренний класс AggregateUnitExtensions в целевой сборке
|
||||
ExtType = typeof(Length).Assembly.GetType("QWERTYkez.Mensura.Extensions.AggregateUnitExtensions")
|
||||
?? throw new InvalidOperationException("Не удалось найти класс AggregateUnitExtensions через рефлексию.");
|
||||
}
|
||||
|
||||
public static Length InvokeSpan(string methodName, ReadOnlySpan<Length> data)
|
||||
{
|
||||
var method = FindMethod(methodName, typeof(ReadOnlySpan<>), isNullable: false);
|
||||
var closedMethod = method.MakeGenericMethod(typeof(Length));
|
||||
var del = closedMethod.CreateDelegate<SpanDelegate>();
|
||||
return del(data);
|
||||
}
|
||||
|
||||
public static Length InvokeSpanNullable(string methodName, ReadOnlySpan<Length?> data)
|
||||
{
|
||||
var method = FindMethod(methodName, typeof(ReadOnlySpan<>), isNullable: true);
|
||||
var closedMethod = method.MakeGenericMethod(typeof(Length));
|
||||
var del = closedMethod.CreateDelegate<SpanNullableDelegate>();
|
||||
return del(data);
|
||||
}
|
||||
|
||||
public static Length InvokeCollection(string methodName, Type genericContainer, object data, bool isNullable)
|
||||
{
|
||||
var method = FindMethod(methodName, genericContainer, isNullable);
|
||||
var closedMethod = method.MakeGenericMethod(typeof(Length));
|
||||
return (Length)closedMethod.Invoke(null, [data])!;
|
||||
}
|
||||
|
||||
private static MethodInfo FindMethod(string name, Type genericContainerType, bool isNullable)
|
||||
{
|
||||
var methods = ExtType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
|
||||
.Where(m => m.Name == name);
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1) continue;
|
||||
|
||||
var paramType = parameters[0].ParameterType;
|
||||
if (!paramType.IsGenericType) continue;
|
||||
|
||||
// Проверяем базовый контейнер (List<>, IEnumerable<>, ReadOnlySpan<>)
|
||||
if (paramType.GetGenericTypeDefinition() != genericContainerType) continue;
|
||||
|
||||
// Проверяем внутренний аргумент типа на Nullable
|
||||
var genericArgument = paramType.GetGenericArguments()[0];
|
||||
bool currentIsNullable = genericArgument.IsGenericType &&
|
||||
genericArgument.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
|
||||
if (currentIsNullable == isNullable)
|
||||
{
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
throw new MethodAccessException($"Метод {name} для контейнера {genericContainerType.Name} (Nullable: {isNullable}) не найден.");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Тесты для НЕ-выделяющих Nullable типов (Обычные структуры)
|
||||
|
||||
private readonly Length[] _standardData = [CreateLength(10), CreateLength(20), CreateLength(30)];
|
||||
private readonly Length _expectedSum = CreateLength(60);
|
||||
private readonly Length _expectedAvg = CreateLength(20);
|
||||
private readonly Length _expectedMax = CreateLength(30);
|
||||
private readonly Length _expectedMin = CreateLength(10);
|
||||
|
||||
[Theory]
|
||||
[InlineData("Sum")]
|
||||
[InlineData("Avg")]
|
||||
[InlineData("Max")]
|
||||
[InlineData("Min")]
|
||||
public void StandardContainers_ShouldCalculateCorrectly(string operation)
|
||||
{
|
||||
// Набор ожидаемых значений
|
||||
Length expected = operation switch
|
||||
{
|
||||
"Sum" => _expectedSum,
|
||||
"Avg" => _expectedAvg,
|
||||
"Max" => _expectedMax,
|
||||
"Min" => _expectedMin,
|
||||
_ => throw new ArgumentException(operation)
|
||||
};
|
||||
|
||||
// 1. Тест ReadOnlySpan<T>
|
||||
ReadOnlySpan<Length> span = _standardData;
|
||||
Assert.Equal(expected, Invoker.InvokeSpan(operation, span));
|
||||
|
||||
// 2. Тест List<T>
|
||||
var list = _standardData.ToList();
|
||||
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(List<>), list, isNullable: false));
|
||||
|
||||
// 3. Тест IReadOnlyCollection<T>
|
||||
IReadOnlyCollection<Length> readOnlyCollection = _standardData;
|
||||
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(IReadOnlyCollection<>), readOnlyCollection, isNullable: false));
|
||||
|
||||
// 4. Тест IEnumerable<T>
|
||||
IEnumerable<Length> enumerable = _standardData.Select(x => x);
|
||||
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(IEnumerable<>), enumerable, isNullable: false));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Тесты для Nullable типов (T?)
|
||||
|
||||
// Тестируем смесь значений с null-элементами
|
||||
private readonly Length?[] _nullableData = [CreateLength(10), null, CreateLength(30)];
|
||||
private readonly Length _expectedNullSum = CreateLength(40);
|
||||
private readonly Length _expectedNullAvg = CreateLength(20); // 40 / 2 значения
|
||||
private readonly Length _expectedNullMax = CreateLength(30);
|
||||
private readonly Length _expectedNullMin = CreateLength(10);
|
||||
|
||||
[Theory]
|
||||
[InlineData("Sum")]
|
||||
[InlineData("Avg")]
|
||||
[InlineData("Max")]
|
||||
[InlineData("Min")]
|
||||
public void NullableContainers_ShouldCalculateCorrectly(string operation)
|
||||
{
|
||||
Length expected = operation switch
|
||||
{
|
||||
"Sum" => _expectedNullSum,
|
||||
"Avg" => _expectedNullAvg,
|
||||
"Max" => _expectedNullMax,
|
||||
"Min" => _expectedNullMin,
|
||||
_ => throw new ArgumentException(operation)
|
||||
};
|
||||
|
||||
// 1. Тест ReadOnlySpan<T?>
|
||||
ReadOnlySpan<Length?> span = _nullableData;
|
||||
Assert.Equal(expected, Invoker.InvokeSpanNullable(operation, span));
|
||||
|
||||
// 2. Тест List<T?>
|
||||
var list = _nullableData.ToList();
|
||||
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(List<>), list, isNullable: true));
|
||||
|
||||
// 3. Тест IReadOnlyCollection<T?>
|
||||
IReadOnlyCollection<Length?> readOnlyCollection = _nullableData;
|
||||
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(IReadOnlyCollection<>), readOnlyCollection, isNullable: true));
|
||||
|
||||
// 4. Тест IEnumerable<T?>
|
||||
IEnumerable<Length?> enumerable = _nullableData.Select(x => x);
|
||||
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(IEnumerable<>), enumerable, isNullable: true));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
165
QWERTYkez.Mensura.Tests/CastExtensions.cs
Normal file
165
QWERTYkez.Mensura.Tests/CastExtensions.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
namespace QWERTYkez.Mensura.Tests;
|
||||
|
||||
public class CastExtensions
|
||||
{
|
||||
private const double NormalValue1 = 42.42;
|
||||
private const double NormalValue2 = 100.05;
|
||||
|
||||
#region 1. Слабое место: Побитовая идентичность и спец-значения (NaN/Inf)
|
||||
// Так как используется Unsafe.As, нужно убедиться, что битовая сетка double
|
||||
// не ломается при сохранении пограничных значений (NaN, Infinity).
|
||||
|
||||
[Theory]
|
||||
[InlineData(NormalValue1)]
|
||||
[InlineData(double.NaN)]
|
||||
[InlineData(double.PositiveInfinity)]
|
||||
[InlineData(double.NegativeInfinity)]
|
||||
[InlineData(double.Epsilon)]
|
||||
public void SingleConversion_Should_Preserve_Exact_BitPattern_For_Special_Doubles(double specialValue)
|
||||
{
|
||||
// Act
|
||||
Length unit = specialValue.ToUnit<Length>();
|
||||
double recovered = unit.ToDouble();
|
||||
|
||||
// Assert
|
||||
// Используем BitConverter, чтобы проверить идентичность на уровне битов (особенно важно для NaN)
|
||||
long originalBits = BitConverter.DoubleToInt64Bits(specialValue);
|
||||
long recoveredBits = BitConverter.DoubleToInt64Bits(recovered);
|
||||
|
||||
Assert.Equal(originalBits, recoveredBits);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 2. Слабое место: Взрыв CLR при изменении емкости (EnsureCapacity)
|
||||
// При ReCast списков подменяется MethodTable самого списка и его внутреннего массива.
|
||||
// Если мы начнем добавлять элементы, список выделит новый массив через Array.CreateInstance.
|
||||
// Если тип в MethodTable не совпадет с тем, что ожидает среда исполнения, GC или рантайм упадет.
|
||||
|
||||
[Fact]
|
||||
public void List_ReCast_Should_Survive_Massive_Resize_And_GC_Collections()
|
||||
{
|
||||
// Arrange
|
||||
var originalList = new List<Length> { (Length)NormalValue1, (Length)NormalValue2 };
|
||||
|
||||
// Act
|
||||
List<double> morphedList = originalList.ReCast();
|
||||
|
||||
// Провоцируем многократное выделение новой памяти (Resize внутреннего массива)
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
morphedList.Add(i * 1.1);
|
||||
}
|
||||
|
||||
// Вызываем сборщик мусора, чтобы проверить, не сошел ли он с ума от нашей подмены
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(102, morphedList.Count);
|
||||
Assert.Equal(NormalValue1, morphedList[0]);
|
||||
Assert.Equal(10 * 1.1, morphedList[12], 5); // Проверка случайного элемента
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 3. Слабое место: Ловушка `ArrayTypeMismatchException` при добавлении в WrapAsList
|
||||
// WrapAsList создает фейковый список, подменяя MethodTable исходного массива на double[].
|
||||
// Но когда мы вызываем List.Add(), рантайм внутри делает Array.Copy().
|
||||
// Если CLR поймет, что исходный массив физически был массивом структур Length,
|
||||
// вылетит ArrayTypeMismatchException.
|
||||
|
||||
[Fact]
|
||||
public void WrapAsList_Must_Allow_Adding_Elements_Without_ArrayTypeMismatchException()
|
||||
{
|
||||
// Arrange
|
||||
Length[] sourceArray = [(Length)NormalValue1, (Length)NormalValue2];
|
||||
|
||||
// Act
|
||||
List<double> wrappedList = sourceArray.WrapAsList();
|
||||
|
||||
// Assert
|
||||
// Это действие вызывает внутренний Array.Copy. Самое хрупкое место!
|
||||
var exception = Record.Exception(() => wrappedList.Add(999.99));
|
||||
|
||||
Assert.Null(exception); // Тест провален, если здесь вылетит исключение типа массива
|
||||
Assert.Equal(3, wrappedList.Count);
|
||||
Assert.Equal(999.99, wrappedList[2]);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 4. Слабое место: Выравнивание памяти и макет Nullable типов
|
||||
// Структура Nullable<T> в памяти имеет размер больше, чем T (из-за флага HasValue и выравнивания).
|
||||
// Подмена MethodTable для Nullable<Length>[] в Nullable<double>[] — это огромный риск
|
||||
// смещения байт. Проверяем, что null остается null, а значения не затираются.
|
||||
|
||||
[Fact]
|
||||
public void Nullable_Array_ReCast_Should_Not_Corrupt_Flags_And_Values()
|
||||
{
|
||||
// Arrange
|
||||
Length?[] source = [(Length)NormalValue1, null, (Length)NormalValue2, null];
|
||||
|
||||
// Act
|
||||
double?[] morphed = source.ReCast();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(source.Length, morphed.Length);
|
||||
Assert.Equal(NormalValue1, morphed[0]);
|
||||
Assert.Null(morphed[1]);
|
||||
Assert.Equal(NormalValue2, morphed[2]);
|
||||
Assert.Null(morphed[3]);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 5. Слабое место: In-Place мутация (Побочный эффект "Вуду")
|
||||
// Так как ReCast и WrapAsList меняют MethodTable *оригинального* объекта прямо в куче,
|
||||
// старая ссылка на массив Length[] теперь указывает на объект, который думает, что он double[].
|
||||
// Проверяем, как ведет себя оригинальная переменная после этого хака.
|
||||
|
||||
[Fact]
|
||||
public void Verify_InPlace_Mutation_SideEffect_Does_Not_Crash_Old_Reference()
|
||||
{
|
||||
// Arrange
|
||||
Length[] originalArray = [(Length)NormalValue1];
|
||||
|
||||
// Act
|
||||
double[] morphedArray = originalArray.ReCast();
|
||||
|
||||
// Изменяем элемент через НОВЫЙ массив
|
||||
morphedArray[0] = 777.77;
|
||||
|
||||
// Assert
|
||||
// Внимание: из-за жесткой мутации в куче originalArray[0] теперь ТОЖЕ вернет 777.77,
|
||||
// потому что они смотрят на одну память. Главное — чтобы CLR не упал при обращении к старой ссылке.
|
||||
double valueFromOldRef = (double)originalArray[0];
|
||||
|
||||
Assert.Equal(777.77, valueFromOldRef);
|
||||
Assert.Same(originalArray, morphedArray); // Физически это один и тот же объект в памяти
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 6. Слабое место: Фильтры CLR и оптимизации LINQ
|
||||
// Компилятор и JIT часто оптимизируют методы вроде .Select() или .ToArray(),
|
||||
// опираясь на тип MethodTable. Проверяем, съедят ли механизмы LINQ наш "поддельный" тип.
|
||||
|
||||
[Fact]
|
||||
public void Morphed_Array_Should_Pass_Through_Linq_And_Native_Sorting_Validations()
|
||||
{
|
||||
// Arrange
|
||||
Length[] originalArray = [(Length)NormalValue2, (Length)NormalValue1]; // [100.05, 42.42]
|
||||
|
||||
// Act
|
||||
double[] morphedArray = originalArray.ReCast();
|
||||
|
||||
// 1. Проверка LINQ фильтрации
|
||||
double[] processedViaLinq = [.. morphedArray.Where(x => x > 50.0)];
|
||||
|
||||
Assert.Single(processedViaLinq);
|
||||
Assert.Equal(NormalValue2, processedViaLinq[0]);
|
||||
|
||||
// 2. Проверка работы встроенной нативной сортировки (Array.Sort использует JIT-оптимизации)
|
||||
var sortException = Record.Exception(() => Array.Sort(morphedArray));
|
||||
|
||||
Assert.Null(sortException);
|
||||
Assert.True(morphedArray[0] < morphedArray[1]); // Теперь [42.42, 100.05]
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
292
QWERTYkez.Mensura.Tests/CollectionsDivideExtensions.cs
Normal file
292
QWERTYkez.Mensura.Tests/CollectionsDivideExtensions.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
namespace QWERTYkez.Mensura.Tests
|
||||
{
|
||||
public class CollectionsDivideExtensionsTests
|
||||
{
|
||||
private const double Tolerance = 1e-12;
|
||||
private static readonly Length scalarUnit = Length.Meter; // 1000 mm
|
||||
private static readonly double scalarDouble = 2.0;
|
||||
|
||||
// Коллекции для тестирования: два элемента
|
||||
private static readonly Length[] unitsArray = [Length.Meter, Length._MilliMeter];
|
||||
private static readonly Length?[] nullableUnitsArray = [Length.Meter, null, Length._MilliMeter];
|
||||
private static readonly List<Length> unitsList = [Length.Meter, Length._MilliMeter];
|
||||
private static readonly List<Length?> nullableUnitsList = [Length.Meter, null, Length._MilliMeter];
|
||||
private static readonly double[] doubleArray = [2.0, 3.0];
|
||||
private static readonly double?[] nullableDoubleArray = [2.0, null, 3.0];
|
||||
|
||||
// ====================== 1. T[] / double ======================
|
||||
[Fact]
|
||||
public void Div_TArray_Double_Returns_TArray()
|
||||
{
|
||||
var result = unitsArray.Div<Length>(scalarDouble);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 / 2.0, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 2. T?[] / double ======================
|
||||
[Fact]
|
||||
public void Div_NullableTArray_Double_Returns_NullableTArray()
|
||||
{
|
||||
var result = nullableUnitsArray.Div<Length>(scalarDouble);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1 / 2.0, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 3. double / T[] ======================
|
||||
[Fact]
|
||||
public void Div_Double_TArray_Returns_TArray()
|
||||
{
|
||||
var result = scalarDouble.Div<Length>(unitsArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(2.0 / 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 4. double / T?[] ======================
|
||||
[Fact]
|
||||
public void Div_Double_NullableTArray_Returns_NullableTArray()
|
||||
{
|
||||
var result = scalarDouble.Div<Length>(nullableUnitsArray);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(2.0 / 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 5. List<T> / double ======================
|
||||
[Fact]
|
||||
public void Div_ListT_Double_Returns_ListT()
|
||||
{
|
||||
var result = unitsList.Div<Length>(scalarDouble);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 / 2.0, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 6. List<T?> / double ======================
|
||||
[Fact]
|
||||
public void Div_ListNullableT_Double_Returns_ListNullableT()
|
||||
{
|
||||
var result = nullableUnitsList.Div<Length>(scalarDouble);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1 / 2.0, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 7. double / List<T> ======================
|
||||
[Fact]
|
||||
public void Div_Double_ListT_Returns_ListT()
|
||||
{
|
||||
var result = scalarDouble.Div<Length>(unitsList);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(2.0 / 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 8. double / List<T?> ======================
|
||||
[Fact]
|
||||
public void Div_Double_ListNullableT_Returns_ListNullableT()
|
||||
{
|
||||
var result = scalarDouble.Div<Length>(nullableUnitsList);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(2.0 / 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 9. IReadOnlyCollection<T> / double (Span) ======================
|
||||
[Fact]
|
||||
public void Div_IReadOnlyCollectionT_Double_Span()
|
||||
{
|
||||
Span<Length> dest = new Length[2];
|
||||
((IReadOnlyCollection<Length>)unitsArray).Div(scalarDouble, dest);
|
||||
Assert.Equal(1000 / 2.0, (double)dest[0], Tolerance);
|
||||
Assert.Equal(1 / 2.0, (double)dest[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 10. IReadOnlyCollection<T?> / double (Span) ======================
|
||||
[Fact]
|
||||
public void Div_IReadOnlyCollectionNullableT_Double_Span()
|
||||
{
|
||||
Span<Length?> dest = new Length?[3];
|
||||
((IReadOnlyCollection<Length?>)nullableUnitsArray).Div(scalarDouble, dest);
|
||||
Assert.Equal(1000 / 2.0, (double)dest[0]!, Tolerance);
|
||||
Assert.Null(dest[1]);
|
||||
Assert.Equal(1 / 2.0, (double)dest[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 11. double / IReadOnlyCollection<T> (Span) ======================
|
||||
[Fact]
|
||||
public void Div_Double_IReadOnlyCollectionT_Span()
|
||||
{
|
||||
Span<Length> dest = new Length[2];
|
||||
scalarDouble.Div((IReadOnlyCollection<Length>)unitsArray, dest);
|
||||
Assert.Equal(2.0 / 1000, (double)dest[0], Tolerance);
|
||||
Assert.Equal(2.0 / 1, (double)dest[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 12. double / IReadOnlyCollection<T?> (Span) ======================
|
||||
[Fact]
|
||||
public void Div_Double_IReadOnlyCollectionNullableT_Span()
|
||||
{
|
||||
Span<Length?> dest = new Length?[3];
|
||||
scalarDouble.Div((IReadOnlyCollection<Length?>)nullableUnitsArray, dest);
|
||||
Assert.Equal(2.0 / 1000, (double)dest[0]!, Tolerance);
|
||||
Assert.Null(dest[1]);
|
||||
Assert.Equal(2.0 / 1, (double)dest[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 13. IEnumerable<T> / double ======================
|
||||
[Fact]
|
||||
public void Div_IEnumerableT_Double_Returns_IEnumerableT()
|
||||
{
|
||||
var result = ((IEnumerable<Length>)unitsArray).Div<Length>(scalarDouble).ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 / 2.0, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 14. IEnumerable<T?> / double ======================
|
||||
[Fact]
|
||||
public void Div_IEnumerableNullableT_Double_Returns_IEnumerableNullableT()
|
||||
{
|
||||
var result = ((IEnumerable<Length?>)nullableUnitsArray).Div<Length>(scalarDouble).ToList();
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1 / 2.0, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 15. double / IEnumerable<T> ======================
|
||||
[Fact]
|
||||
public void Div_Double_IEnumerableT_Returns_IEnumerableT()
|
||||
{
|
||||
var result = scalarDouble.Div<Length>((IEnumerable<Length>)unitsArray).ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(2.0 / 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 16. double / IEnumerable<T?> ======================
|
||||
[Fact]
|
||||
public void Div_Double_IEnumerableNullableT_Returns_IEnumerableNullableT()
|
||||
{
|
||||
var result = scalarDouble.Div<Length>((IEnumerable<Length?>)nullableUnitsArray).ToList();
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(2.0 / 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 17. double[] / T ======================
|
||||
[Fact]
|
||||
public void Div_DoubleArray_T_Returns_TArray()
|
||||
{
|
||||
var result = doubleArray.Div(scalarUnit);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(3.0 / 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 18. double?[] / T ======================
|
||||
[Fact]
|
||||
public void Div_NullableDoubleArray_T_Returns_NullableTArray()
|
||||
{
|
||||
var result = nullableDoubleArray.Div(scalarUnit);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(3.0 / 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 19. T / double[] ======================
|
||||
[Fact]
|
||||
public void Div_T_DoubleArray_Returns_TArray()
|
||||
{
|
||||
var result = scalarUnit.Div(doubleArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 / 3.0, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 20. T / double?[] ======================
|
||||
[Fact]
|
||||
public void Div_T_NullableDoubleArray_Returns_NullableTArray()
|
||||
{
|
||||
var result = scalarUnit.Div(nullableDoubleArray);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1000 / 3.0, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 21. List<double> / T ======================
|
||||
[Fact]
|
||||
public void Div_ListDouble_T_Returns_ListT()
|
||||
{
|
||||
var list = new List<double> { 2.0, 3.0 };
|
||||
var result = list.Div(scalarUnit);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(3.0 / 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 22. List<double?> / T ======================
|
||||
[Fact]
|
||||
public void Div_ListNullableDouble_T_Returns_ListNullableT()
|
||||
{
|
||||
var list = new List<double?> { 2.0, null, 3.0 };
|
||||
var result = list.Div(scalarUnit);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(3.0 / 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 23. T / List<double> ======================
|
||||
[Fact]
|
||||
public void Div_T_ListDouble_Returns_ListT()
|
||||
{
|
||||
var list = new List<double> { 2.0, 3.0 };
|
||||
var result = scalarUnit.Div(list);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 / 3.0, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 24. T / List<double?> ======================
|
||||
[Fact]
|
||||
public void Div_T_ListNullableDouble_Returns_ListNullableT()
|
||||
{
|
||||
var list = new List<double?> { 2.0, null, 3.0 };
|
||||
var result = scalarUnit.Div(list);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1000 / 3.0, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 25. T / T[] -> double[] ======================
|
||||
[Fact]
|
||||
public void Div_T_TArray_Returns_DoubleArray()
|
||||
{
|
||||
var result = scalarUnit.Div(unitsArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 / 1000.0, result[0], Tolerance);
|
||||
Assert.Equal(1000 / 1.0, result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== Дополнительно: пустые коллекции, null аргументы ======================
|
||||
[Fact]
|
||||
public void Div_EmptyArray_ReturnsEmptyArray()
|
||||
{
|
||||
var empty = Array.Empty<Length>();
|
||||
var result = empty.Div<Length>(2.0);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
337
QWERTYkez.Mensura.Tests/CollectionsMinusExtensions.cs
Normal file
337
QWERTYkez.Mensura.Tests/CollectionsMinusExtensions.cs
Normal file
@@ -0,0 +1,337 @@
|
||||
namespace QWERTYkez.Mensura.Tests
|
||||
{
|
||||
public class CollectionsMinusExtensionsTests
|
||||
{
|
||||
private const double Tolerance = 1e-12;
|
||||
private static readonly Length scalarUnit = Length.Meter; // 1000 mm
|
||||
private static readonly double scalarDouble = 1000.0; // уменьшаемое/вычитаемое в мм
|
||||
|
||||
// Коллекции единиц
|
||||
private static readonly Length[] unitsArray = [Length.Meter, Length._MilliMeter];
|
||||
private static readonly Length?[] nullableUnitsArray = [Length.Meter, null, Length._MilliMeter];
|
||||
private static readonly List<Length> unitsList = [Length.Meter, Length._MilliMeter];
|
||||
private static readonly List<Length?> nullableUnitsList = [Length.Meter, null, Length._MilliMeter];
|
||||
|
||||
// Коллекции double
|
||||
private static readonly double[] doubleArray = [500.0, 200.0]; // мм
|
||||
private static readonly double?[] nullableDoubleArray = [500.0, null, 200.0];
|
||||
private static readonly List<double> doubleList = [500.0, 200.0];
|
||||
private static readonly List<double?> nullableDoubleList = [500.0, null, 200.0];
|
||||
|
||||
// ====================== 1. T[] - double (результат T[]) ======================
|
||||
[Fact]
|
||||
public void Minus_TArray_Double_Returns_TArray()
|
||||
{
|
||||
// units - subtrahend
|
||||
var result = unitsArray.Minus<Length>(scalarDouble);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 - 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Minus_TArray_Double_ByZero_Works()
|
||||
{
|
||||
var result = unitsArray.Minus<Length>(0.0);
|
||||
Assert.Equal(1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 2. T?[] - double (результат T?[]) ======================
|
||||
[Fact]
|
||||
public void Minus_NullableTArray_Double_Returns_NullableTArray()
|
||||
{
|
||||
var result = nullableUnitsArray.Minus<Length>(scalarDouble);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1 - 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 3. double - T[] (результат T[]) ======================
|
||||
[Fact]
|
||||
public void Minus_Double_TArray_Returns_TArray()
|
||||
{
|
||||
var result = scalarDouble.Minus<Length>(unitsArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 - 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 4. double - T?[] (результат T?[]) ======================
|
||||
[Fact]
|
||||
public void Minus_Double_NullableTArray_Returns_NullableTArray()
|
||||
{
|
||||
var result = scalarDouble.Minus<Length>(nullableUnitsArray);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1000 - 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 5. List<T> - double ======================
|
||||
[Fact]
|
||||
public void Minus_ListT_Double_Returns_ListT()
|
||||
{
|
||||
var result = unitsList.Minus<Length>(scalarDouble);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 - 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 6. List<T?> - double ======================
|
||||
[Fact]
|
||||
public void Minus_ListNullableT_Double_Returns_ListNullableT()
|
||||
{
|
||||
var result = nullableUnitsList.Minus<Length>(scalarDouble);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1 - 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 7. double - List<T> ======================
|
||||
[Fact]
|
||||
public void Minus_Double_ListT_Returns_ListT()
|
||||
{
|
||||
var result = scalarDouble.Minus<Length>(unitsList);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 - 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 8. double - List<T?> ======================
|
||||
[Fact]
|
||||
public void Minus_Double_ListNullableT_Returns_ListNullableT()
|
||||
{
|
||||
var result = scalarDouble.Minus<Length>(nullableUnitsList);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1000 - 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 9. IReadOnlyCollection<T> - double (Span) ======================
|
||||
[Fact]
|
||||
public void Minus_IReadOnlyCollectionT_Double_Span()
|
||||
{
|
||||
Span<Length> dest = new Length[2];
|
||||
((IReadOnlyCollection<Length>)unitsArray).Minus<Length>(scalarDouble, dest);
|
||||
Assert.Equal(1000 - 1000, (double)dest[0], Tolerance);
|
||||
Assert.Equal(1 - 1000, (double)dest[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 10. IReadOnlyCollection<T?> - double (Span) ======================
|
||||
[Fact]
|
||||
public void Minus_IReadOnlyCollectionNullableT_Double_Span()
|
||||
{
|
||||
Span<Length?> dest = new Length?[3];
|
||||
((IReadOnlyCollection<Length?>)nullableUnitsArray).Minus<Length>(scalarDouble, dest);
|
||||
Assert.Equal(1000 - 1000, (double)dest[0]!, Tolerance);
|
||||
Assert.Null(dest[1]);
|
||||
Assert.Equal(1 - 1000, (double)dest[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 11. double - IReadOnlyCollection<T> (Span) ======================
|
||||
[Fact]
|
||||
public void Minus_Double_IReadOnlyCollectionT_Span()
|
||||
{
|
||||
Span<Length> dest = new Length[2];
|
||||
scalarDouble.Minus<Length>((IReadOnlyCollection<Length>)unitsArray, dest);
|
||||
Assert.Equal(1000 - 1000, (double)dest[0], Tolerance);
|
||||
Assert.Equal(1000 - 1, (double)dest[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 12. double - IReadOnlyCollection<T?> (Span) ======================
|
||||
[Fact]
|
||||
public void Minus_Double_IReadOnlyCollectionNullableT_Span()
|
||||
{
|
||||
Span<Length?> dest = new Length?[3];
|
||||
scalarDouble.Minus<Length>((IReadOnlyCollection<Length?>)nullableUnitsArray, dest);
|
||||
Assert.Equal(1000 - 1000, (double)dest[0]!, Tolerance);
|
||||
Assert.Null(dest[1]);
|
||||
Assert.Equal(1000 - 1, (double)dest[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 13. IEnumerable<T> - double ======================
|
||||
[Fact]
|
||||
public void Minus_IEnumerableT_Double_Returns_IEnumerableT()
|
||||
{
|
||||
var result = ((IEnumerable<Length>)unitsArray).Minus<Length>(scalarDouble).ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 - 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 14. IEnumerable<T?> - double ======================
|
||||
[Fact]
|
||||
public void Minus_IEnumerableNullableT_Double_Returns_IEnumerableNullableT()
|
||||
{
|
||||
var result = ((IEnumerable<Length?>)nullableUnitsArray).Minus<Length>(scalarDouble).ToList();
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1 - 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 15. double - IEnumerable<T> ======================
|
||||
[Fact]
|
||||
public void Minus_Double_IEnumerableT_Returns_IEnumerableT()
|
||||
{
|
||||
var result = scalarDouble.Minus<Length>((IEnumerable<Length>)unitsArray).ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 - 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 16. double - IEnumerable<T?> ======================
|
||||
[Fact]
|
||||
public void Minus_Double_IEnumerableNullableT_Returns_IEnumerableNullableT()
|
||||
{
|
||||
var result = scalarDouble.Minus<Length>((IEnumerable<Length?>)nullableUnitsArray).ToList();
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1000 - 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 17. double[] - T ======================
|
||||
[Fact]
|
||||
public void Minus_DoubleArray_T_Returns_TArray()
|
||||
{
|
||||
var result = doubleArray.Minus(scalarUnit);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(500 - 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(200 - 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 18. double?[] - T ======================
|
||||
[Fact]
|
||||
public void Minus_NullableDoubleArray_T_Returns_NullableTArray()
|
||||
{
|
||||
var result = nullableDoubleArray.Minus(scalarUnit);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(500 - 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(200 - 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 19. T - double[] ======================
|
||||
[Fact]
|
||||
public void Minus_T_DoubleArray_Returns_TArray()
|
||||
{
|
||||
var result = scalarUnit.Minus(doubleArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 - 500, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 - 200, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 20. T - double?[] ======================
|
||||
[Fact]
|
||||
public void Minus_T_NullableDoubleArray_Returns_NullableTArray()
|
||||
{
|
||||
var result = scalarUnit.Minus(nullableDoubleArray);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 - 500, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1000 - 200, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 21. List<double> - T ======================
|
||||
[Fact]
|
||||
public void Minus_ListDouble_T_Returns_ListT()
|
||||
{
|
||||
var result = doubleList.Minus(scalarUnit);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(500 - 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(200 - 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 22. List<double?> - T ======================
|
||||
[Fact]
|
||||
public void Minus_ListNullableDouble_T_Returns_ListNullableT()
|
||||
{
|
||||
var result = nullableDoubleList.Minus(scalarUnit);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(500 - 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(200 - 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 23. T - List<double> ======================
|
||||
[Fact]
|
||||
public void Minus_T_ListDouble_Returns_ListT()
|
||||
{
|
||||
var result = scalarUnit.Minus(doubleList);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 - 500, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 - 200, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 24. T - List<double?> ======================
|
||||
[Fact]
|
||||
public void Minus_T_ListNullableDouble_Returns_ListNullableT()
|
||||
{
|
||||
var result = scalarUnit.Minus(nullableDoubleList);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 - 500, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1000 - 200, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 25. T - T[] -> double[] ======================
|
||||
[Fact]
|
||||
public void Minus_T_TArray_Returns_DoubleArray()
|
||||
{
|
||||
var result = scalarUnit - unitsArray;
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 - 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1000 - 1, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
// Дополнительно: перегрузки, где результат double (без указания R)
|
||||
// Тестируем Minus<T>(T[] units, T subtrahend) – возвращает double[]
|
||||
[Fact]
|
||||
public void Minus_TArray_T_Returns_DoubleArray()
|
||||
{
|
||||
var result = unitsArray - scalarUnit;
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 - 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1 - 1000, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Minus_ListT_T_Returns_DoubleList()
|
||||
{
|
||||
var result = unitsList - scalarUnit;
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 - 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1 - 1000, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
// Проверка пустых коллекций
|
||||
[Fact]
|
||||
public void Minus_EmptyArray_ReturnsEmptyArray()
|
||||
{
|
||||
var empty = Array.Empty<Length>();
|
||||
var result = empty.Minus<Length>(5.0);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Minus_NullArray_ReturnsNull()
|
||||
{
|
||||
Length[] nullArray = null!;
|
||||
var result = nullArray.Minus<Length>(5.0);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Minus_NullList_ReturnsNull()
|
||||
{
|
||||
List<Length> nullList = null!;
|
||||
var result = nullList.Minus<Length>(5.0);
|
||||
Assert.Null(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
335
QWERTYkez.Mensura.Tests/CollectionsMultiplyExtensions.cs
Normal file
335
QWERTYkez.Mensura.Tests/CollectionsMultiplyExtensions.cs
Normal file
@@ -0,0 +1,335 @@
|
||||
namespace QWERTYkez.Mensura.Tests
|
||||
{
|
||||
public class CollectionsMultiplyExtensionsTests
|
||||
{
|
||||
private const double Tolerance = 1e-12;
|
||||
private static readonly Length scalarUnit = Length.Meter; // 1000 mm
|
||||
private static readonly double scalarDouble = 2.0;
|
||||
|
||||
// Коллекции единиц
|
||||
private static readonly Length[] unitsArray = [Length.Meter, Length._MilliMeter];
|
||||
private static readonly Length?[] nullableUnitsArray = [Length.Meter, null, Length._MilliMeter];
|
||||
private static readonly List<Length> unitsList = [Length.Meter, Length._MilliMeter];
|
||||
private static readonly List<Length?> nullableUnitsList = [Length.Meter, null, Length._MilliMeter];
|
||||
|
||||
// Коллекции double
|
||||
private static readonly double[] doubleArray = [5.0, 3.0];
|
||||
private static readonly double?[] nullableDoubleArray = [5.0, null, 3.0];
|
||||
private static readonly List<double> doubleList = [5.0, 3.0];
|
||||
private static readonly List<double?> nullableDoubleList = [5.0, null, 3.0];
|
||||
|
||||
// ====================== 1. T[] * double ======================
|
||||
[Fact]
|
||||
public void Mul_TArray_Double_Returns_TArray()
|
||||
{
|
||||
var result = unitsArray.Mul<Length>(scalarDouble);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 * 2, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 * 2, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mul_TArray_Double_ByZero_Works()
|
||||
{
|
||||
var result = unitsArray.Mul<Length>(0.0);
|
||||
Assert.Equal(0, (double)result[0], Tolerance);
|
||||
Assert.Equal(0, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 2. T?[] * double ======================
|
||||
[Fact]
|
||||
public void Mul_NullableTArray_Double_Returns_NullableTArray()
|
||||
{
|
||||
var result = nullableUnitsArray.Mul<Length>(scalarDouble);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 * 2, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null * 2 = null
|
||||
Assert.Equal(1 * 2, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 3. double * T[] ======================
|
||||
[Fact]
|
||||
public void Mul_Double_TArray_Returns_TArray()
|
||||
{
|
||||
var result = scalarDouble.Mul<Length>(unitsArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(2 * 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(2 * 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 4. double * T?[] ======================
|
||||
[Fact]
|
||||
public void Mul_Double_NullableTArray_Returns_NullableTArray()
|
||||
{
|
||||
var result = scalarDouble.Mul<Length>(nullableUnitsArray);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(2 * 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // 2 * null = null
|
||||
Assert.Equal(2 * 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 5. List<T> * double ======================
|
||||
[Fact]
|
||||
public void Mul_ListT_Double_Returns_ListT()
|
||||
{
|
||||
var result = unitsList.Mul<Length>(scalarDouble);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 * 2, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 * 2, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 6. List<T?> * double ======================
|
||||
[Fact]
|
||||
public void Mul_ListNullableT_Double_Returns_ListNullableT()
|
||||
{
|
||||
var result = nullableUnitsList.Mul<Length>(scalarDouble);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 * 2, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null * 2 = null
|
||||
Assert.Equal(1 * 2, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 7. double * List<T> ======================
|
||||
[Fact]
|
||||
public void Mul_Double_ListT_Returns_ListT()
|
||||
{
|
||||
var result = scalarDouble.Mul<Length>(unitsList);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(2 * 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(2 * 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 8. double * List<T?> ======================
|
||||
[Fact]
|
||||
public void Mul_Double_ListNullableT_Returns_ListNullableT()
|
||||
{
|
||||
var result = scalarDouble.Mul<Length>(nullableUnitsList);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(2 * 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // 2 * null = null
|
||||
Assert.Equal(2 * 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 9. IReadOnlyCollection<T> * double (Span) ======================
|
||||
[Fact]
|
||||
public void Mul_IReadOnlyCollectionT_Double_Span()
|
||||
{
|
||||
Span<Length> dest = new Length[2];
|
||||
((IReadOnlyCollection<Length>)unitsArray).Mul<Length>(scalarDouble, dest);
|
||||
Assert.Equal(1000 * 2, (double)dest[0], Tolerance);
|
||||
Assert.Equal(1 * 2, (double)dest[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 10. IReadOnlyCollection<T?> * double (Span) ======================
|
||||
[Fact]
|
||||
public void Mul_IReadOnlyCollectionNullableT_Double_Span()
|
||||
{
|
||||
Span<Length?> dest = new Length?[3];
|
||||
((IReadOnlyCollection<Length?>)nullableUnitsArray).Mul<Length>(scalarDouble, dest);
|
||||
Assert.Equal(1000 * 2, (double)dest[0]!, Tolerance);
|
||||
Assert.Null(dest[1]); // null * 2 = null
|
||||
Assert.Equal(1 * 2, (double)dest[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 11. double * IReadOnlyCollection<T> (Span) ======================
|
||||
[Fact]
|
||||
public void Mul_Double_IReadOnlyCollectionT_Span()
|
||||
{
|
||||
Span<Length> dest = new Length[2];
|
||||
scalarDouble.Mul<Length>((IReadOnlyCollection<Length>)unitsArray, dest);
|
||||
Assert.Equal(2 * 1000, (double)dest[0], Tolerance);
|
||||
Assert.Equal(2 * 1, (double)dest[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 12. double * IReadOnlyCollection<T?> (Span) ======================
|
||||
[Fact]
|
||||
public void Mul_Double_IReadOnlyCollectionNullableT_Span()
|
||||
{
|
||||
Span<Length?> dest = new Length?[3];
|
||||
scalarDouble.Mul<Length>((IReadOnlyCollection<Length?>)nullableUnitsArray, dest);
|
||||
Assert.Equal(2 * 1000, (double)dest[0]!, Tolerance);
|
||||
Assert.Null(dest[1]); // 2 * null = null
|
||||
Assert.Equal(2 * 1, (double)dest[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 13. IEnumerable<T> * double ======================
|
||||
[Fact]
|
||||
public void Mul_IEnumerableT_Double_Returns_IEnumerableT()
|
||||
{
|
||||
var result = ((IEnumerable<Length>)unitsArray).Mul<Length>(scalarDouble).ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 * 2, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 * 2, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 14. IEnumerable<T?> * double ======================
|
||||
[Fact]
|
||||
public void Mul_IEnumerableNullableT_Double_Returns_IEnumerableNullableT()
|
||||
{
|
||||
var result = ((IEnumerable<Length?>)nullableUnitsArray).Mul<Length>(scalarDouble).ToList();
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 * 2, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null * 2 = null
|
||||
Assert.Equal(1 * 2, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 15. double * IEnumerable<T> ======================
|
||||
[Fact]
|
||||
public void Mul_Double_IEnumerableT_Returns_IEnumerableT()
|
||||
{
|
||||
var result = scalarDouble.Mul<Length>((IEnumerable<Length>)unitsArray).ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(2 * 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(2 * 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 16. double * IEnumerable<T?> ======================
|
||||
[Fact]
|
||||
public void Mul_Double_IEnumerableNullableT_Returns_IEnumerableNullableT()
|
||||
{
|
||||
var result = scalarDouble.Mul<Length>((IEnumerable<Length?>)nullableUnitsArray).ToList();
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(2 * 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // 2 * null = null
|
||||
Assert.Equal(2 * 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 17. double[] * T ======================
|
||||
[Fact]
|
||||
public void Mul_DoubleArray_T_Returns_TArray()
|
||||
{
|
||||
var result = doubleArray.Mul(scalarUnit);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(5 * 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(3 * 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 18. double?[] * T ======================
|
||||
[Fact]
|
||||
public void Mul_NullableDoubleArray_T_Returns_NullableTArray()
|
||||
{
|
||||
var result = nullableDoubleArray.Mul(scalarUnit);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(5 * 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null * Length = null
|
||||
Assert.Equal(3 * 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 19. T * double[] ======================
|
||||
[Fact]
|
||||
public void Mul_T_DoubleArray_Returns_TArray()
|
||||
{
|
||||
var result = scalarUnit.Mul(doubleArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 * 5, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 * 3, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 20. T * double?[] ======================
|
||||
[Fact]
|
||||
public void Mul_T_NullableDoubleArray_Returns_NullableTArray()
|
||||
{
|
||||
var result = scalarUnit.Mul(nullableDoubleArray);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 * 5, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // Length * null = null
|
||||
Assert.Equal(1000 * 3, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 21. List<double> * T ======================
|
||||
[Fact]
|
||||
public void Mul_ListDouble_T_Returns_ListT()
|
||||
{
|
||||
var result = doubleList.Mul(scalarUnit);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(5 * 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(3 * 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 22. List<double?> * T ======================
|
||||
[Fact]
|
||||
public void Mul_ListNullableDouble_T_Returns_ListNullableT()
|
||||
{
|
||||
var result = nullableDoubleList.Mul(scalarUnit);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(5 * 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null * Length = null
|
||||
Assert.Equal(3 * 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 23. T * List<double> ======================
|
||||
[Fact]
|
||||
public void Mul_T_ListDouble_Returns_ListT()
|
||||
{
|
||||
var result = scalarUnit.Mul(doubleList);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 * 5, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 * 3, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 24. T * List<double?> ======================
|
||||
[Fact]
|
||||
public void Mul_T_ListNullableDouble_Returns_ListNullableT()
|
||||
{
|
||||
var result = scalarUnit.Mul(nullableDoubleList);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 * 5, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // Length * null = null
|
||||
Assert.Equal(1000 * 3, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 25. T * T[] -> double[] ======================
|
||||
[Fact]
|
||||
public void Mul_T_TArray_Returns_DoubleArray()
|
||||
{
|
||||
var result = scalarUnit * unitsArray;
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 * 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1000 * 1, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
// Дополнительно: перегрузки с результатом double (без R)
|
||||
[Fact]
|
||||
public void Mul_TArray_T_Returns_DoubleArray()
|
||||
{
|
||||
var result = unitsArray * scalarUnit;
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 * 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1 * 1000, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mul_ListT_T_Returns_DoubleList()
|
||||
{
|
||||
var result = unitsList * scalarUnit;
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 * 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1 * 1000, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== Обработка null коллекций ======================
|
||||
[Fact]
|
||||
public void Mul_NullArray_ReturnsNull()
|
||||
{
|
||||
Length[] nullArray = null!;
|
||||
var result = nullArray.Mul<Length>(5.0);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mul_NullList_ReturnsNull()
|
||||
{
|
||||
List<Length> nullList = null!;
|
||||
var result = nullList.Mul<Length>(5.0);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mul_EmptyArray_ReturnsEmptyArray()
|
||||
{
|
||||
var empty = Array.Empty<Length>();
|
||||
var result = empty.Mul<Length>(5.0);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
334
QWERTYkez.Mensura.Tests/CollectionsPlusExtensions.cs
Normal file
334
QWERTYkez.Mensura.Tests/CollectionsPlusExtensions.cs
Normal file
@@ -0,0 +1,334 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QWERTYkez.Mensura.Units;
|
||||
using QWERTYkez.Mensura.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace QWERTYkez.Mensura.Tests
|
||||
{
|
||||
public class CollectionsPlusExtensionsTests
|
||||
{
|
||||
private const double Tolerance = 1e-12;
|
||||
private static readonly Length scalarUnit = Length.Meter; // 1000 mm
|
||||
private static readonly double scalarDouble = 500.0; // прибавляемое значение в мм
|
||||
|
||||
// Коллекции единиц
|
||||
private static readonly Length[] unitsArray = new[] { Length.Meter, Length._MilliMeter };
|
||||
private static readonly Length?[] nullableUnitsArray = new Length?[] { Length.Meter, null, Length._MilliMeter };
|
||||
private static readonly List<Length> unitsList = new List<Length> { Length.Meter, Length._MilliMeter };
|
||||
private static readonly List<Length?> nullableUnitsList = new List<Length?> { Length.Meter, null, Length._MilliMeter };
|
||||
|
||||
// Коллекции double
|
||||
private static readonly double[] doubleArray = new double[] { 200.0, 300.0 };
|
||||
private static readonly double?[] nullableDoubleArray = new double?[] { 200.0, null, 300.0 };
|
||||
private static readonly List<double> doubleList = new List<double> { 200.0, 300.0 };
|
||||
private static readonly List<double?> nullableDoubleList = new List<double?> { 200.0, null, 300.0 };
|
||||
|
||||
// ====================== 1. T[] + double ======================
|
||||
[Fact]
|
||||
public void Plus_TArray_Double_Returns_TArray()
|
||||
{
|
||||
var result = unitsArray.Plus<Length>(scalarDouble);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 + 500, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 + 500, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 2. T?[] + double ======================
|
||||
[Fact]
|
||||
public void Plus_NullableTArray_Double_Returns_NullableTArray()
|
||||
{
|
||||
var result = nullableUnitsArray.Plus<Length>(scalarDouble);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 + 500, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null + число = null
|
||||
Assert.Equal(1 + 500, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 3. double + T[] ======================
|
||||
[Fact]
|
||||
public void Plus_Double_TArray_Returns_TArray()
|
||||
{
|
||||
var result = scalarDouble.Plus<Length>(unitsArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(500 + 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(500 + 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 4. double + T?[] ======================
|
||||
[Fact]
|
||||
public void Plus_Double_NullableTArray_Returns_NullableTArray()
|
||||
{
|
||||
var result = scalarDouble.Plus<Length>(nullableUnitsArray);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(500 + 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // число + null = null
|
||||
Assert.Equal(500 + 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 5. List<T> + double ======================
|
||||
[Fact]
|
||||
public void Plus_ListT_Double_Returns_ListT()
|
||||
{
|
||||
var result = unitsList.Plus<Length>(scalarDouble);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 + 500, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 + 500, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 6. List<T?> + double ======================
|
||||
[Fact]
|
||||
public void Plus_ListNullableT_Double_Returns_ListNullableT()
|
||||
{
|
||||
var result = nullableUnitsList.Plus<Length>(scalarDouble);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 + 500, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null + число = null
|
||||
Assert.Equal(1 + 500, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 7. double + List<T> ======================
|
||||
[Fact]
|
||||
public void Plus_Double_ListT_Returns_ListT()
|
||||
{
|
||||
var result = scalarDouble.Plus<Length>(unitsList);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(500 + 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(500 + 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 8. double + List<T?> ======================
|
||||
[Fact]
|
||||
public void Plus_Double_ListNullableT_Returns_ListNullableT()
|
||||
{
|
||||
var result = scalarDouble.Plus<Length>(nullableUnitsList);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(500 + 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // число + null = null
|
||||
Assert.Equal(500 + 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 9. IReadOnlyCollection<T> + double (Span) ======================
|
||||
[Fact]
|
||||
public void Plus_IReadOnlyCollectionT_Double_Span()
|
||||
{
|
||||
Span<Length> dest = new Length[2];
|
||||
((IReadOnlyCollection<Length>)unitsArray).Plus<Length>(scalarDouble, dest);
|
||||
Assert.Equal(1000 + 500, (double)dest[0], Tolerance);
|
||||
Assert.Equal(1 + 500, (double)dest[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 10. IReadOnlyCollection<T?> + double (Span) ======================
|
||||
[Fact]
|
||||
public void Plus_IReadOnlyCollectionNullableT_Double_Span()
|
||||
{
|
||||
Span<Length?> dest = new Length?[3];
|
||||
((IReadOnlyCollection<Length?>)nullableUnitsArray).Plus<Length>(scalarDouble, dest);
|
||||
Assert.Equal(1000 + 500, (double)dest[0]!, Tolerance);
|
||||
Assert.Null(dest[1]); // null + число = null
|
||||
Assert.Equal(1 + 500, (double)dest[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 11. double + IReadOnlyCollection<T> (Span) ======================
|
||||
[Fact]
|
||||
public void Plus_Double_IReadOnlyCollectionT_Span()
|
||||
{
|
||||
Span<Length> dest = new Length[2];
|
||||
scalarDouble.Plus<Length>((IReadOnlyCollection<Length>)unitsArray, dest);
|
||||
Assert.Equal(500 + 1000, (double)dest[0], Tolerance);
|
||||
Assert.Equal(500 + 1, (double)dest[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 12. double + IReadOnlyCollection<T?> (Span) ======================
|
||||
[Fact]
|
||||
public void Plus_Double_IReadOnlyCollectionNullableT_Span()
|
||||
{
|
||||
Span<Length?> dest = new Length?[3];
|
||||
scalarDouble.Plus<Length>((IReadOnlyCollection<Length?>)nullableUnitsArray, dest);
|
||||
Assert.Equal(500 + 1000, (double)dest[0]!, Tolerance);
|
||||
Assert.Null(dest[1]); // число + null = null
|
||||
Assert.Equal(500 + 1, (double)dest[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 13. IEnumerable<T> + double ======================
|
||||
[Fact]
|
||||
public void Plus_IEnumerableT_Double_Returns_IEnumerableT()
|
||||
{
|
||||
var result = ((IEnumerable<Length>)unitsArray).Plus<Length>(scalarDouble).ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 + 500, (double)result[0], Tolerance);
|
||||
Assert.Equal(1 + 500, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 14. IEnumerable<T?> + double ======================
|
||||
[Fact]
|
||||
public void Plus_IEnumerableNullableT_Double_Returns_IEnumerableNullableT()
|
||||
{
|
||||
var result = ((IEnumerable<Length?>)nullableUnitsArray).Plus<Length>(scalarDouble).ToList();
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 + 500, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null + число = null
|
||||
Assert.Equal(1 + 500, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 15. double + IEnumerable<T> ======================
|
||||
[Fact]
|
||||
public void Plus_Double_IEnumerableT_Returns_IEnumerableT()
|
||||
{
|
||||
var result = scalarDouble.Plus<Length>((IEnumerable<Length>)unitsArray).ToList();
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(500 + 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(500 + 1, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 16. double + IEnumerable<T?> ======================
|
||||
[Fact]
|
||||
public void Plus_Double_IEnumerableNullableT_Returns_IEnumerableNullableT()
|
||||
{
|
||||
var result = scalarDouble.Plus<Length>((IEnumerable<Length?>)nullableUnitsArray).ToList();
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(500 + 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // число + null = null
|
||||
Assert.Equal(500 + 1, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 17. double[] + T ======================
|
||||
[Fact]
|
||||
public void Plus_DoubleArray_T_Returns_TArray()
|
||||
{
|
||||
var result = doubleArray.Plus(scalarUnit);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(200 + 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(300 + 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 18. double?[] + T ======================
|
||||
[Fact]
|
||||
public void Plus_NullableDoubleArray_T_Returns_NullableTArray()
|
||||
{
|
||||
var result = nullableDoubleArray.Plus(scalarUnit);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(200 + 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null + Length = null
|
||||
Assert.Equal(300 + 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 19. T + double[] ======================
|
||||
[Fact]
|
||||
public void Plus_T_DoubleArray_Returns_TArray()
|
||||
{
|
||||
var result = scalarUnit.Plus(doubleArray);
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 + 200, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 + 300, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 20. T + double?[] ======================
|
||||
[Fact]
|
||||
public void Plus_T_NullableDoubleArray_Returns_NullableTArray()
|
||||
{
|
||||
var result = scalarUnit.Plus(nullableDoubleArray);
|
||||
Assert.Equal(3, result.Length);
|
||||
Assert.Equal(1000 + 200, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // Length + null = null
|
||||
Assert.Equal(1000 + 300, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 21. List<double> + T ======================
|
||||
[Fact]
|
||||
public void Plus_ListDouble_T_Returns_ListT()
|
||||
{
|
||||
var result = doubleList.Plus(scalarUnit);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(200 + 1000, (double)result[0], Tolerance);
|
||||
Assert.Equal(300 + 1000, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 22. List<double?> + T ======================
|
||||
[Fact]
|
||||
public void Plus_ListNullableDouble_T_Returns_ListNullableT()
|
||||
{
|
||||
var result = nullableDoubleList.Plus(scalarUnit);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(200 + 1000, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // null + Length = null
|
||||
Assert.Equal(300 + 1000, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 23. T + List<double> ======================
|
||||
[Fact]
|
||||
public void Plus_T_ListDouble_Returns_ListT()
|
||||
{
|
||||
var result = scalarUnit.Plus(doubleList);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 + 200, (double)result[0], Tolerance);
|
||||
Assert.Equal(1000 + 300, (double)result[1], Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 24. T + List<double?> ======================
|
||||
[Fact]
|
||||
public void Plus_T_ListNullableDouble_Returns_ListNullableT()
|
||||
{
|
||||
var result = scalarUnit.Plus(nullableDoubleList);
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1000 + 200, (double)result[0]!, Tolerance);
|
||||
Assert.Null(result[1]); // Length + null = null
|
||||
Assert.Equal(1000 + 300, (double)result[2]!, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== 25. T + T[] -> double[] ======================
|
||||
[Fact]
|
||||
public void Plus_T_TArray_Returns_DoubleArray()
|
||||
{
|
||||
var result = scalarUnit + unitsArray;
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 + 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1000 + 1, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
// Дополнительно: перегрузки с результатом double (без R)
|
||||
[Fact]
|
||||
public void Plus_TArray_T_Returns_DoubleArray()
|
||||
{
|
||||
var result = unitsArray + scalarUnit;
|
||||
Assert.Equal(2, result.Length);
|
||||
Assert.Equal(1000 + 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1 + 1000, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plus_ListT_T_Returns_DoubleList()
|
||||
{
|
||||
var result = unitsList + scalarUnit;
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1000 + 1000, result[0]._Value, Tolerance);
|
||||
Assert.Equal(1 + 1000, result[1]._Value, Tolerance);
|
||||
}
|
||||
|
||||
// ====================== Обработка null коллекций ======================
|
||||
[Fact]
|
||||
public void Plus_NullArray_ReturnsNull()
|
||||
{
|
||||
Length[] nullArray = null;
|
||||
var result = nullArray.Plus<Length>(5.0);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plus_NullList_ReturnsNull()
|
||||
{
|
||||
List<Length> nullList = null;
|
||||
var result = nullList.Plus<Length>(5.0);
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Plus_EmptyArray_ReturnsEmptyArray()
|
||||
{
|
||||
var empty = Array.Empty<Length>();
|
||||
var result = empty.Plus<Length>(5.0);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
QWERTYkez.Mensura.Tests/CollectionsPow2Extensions.cs
Normal file
73
QWERTYkez.Mensura.Tests/CollectionsPow2Extensions.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
namespace QWERTYkez.Mensura.Tests;
|
||||
|
||||
public class CollectionsPow2Extensions
|
||||
{
|
||||
[Fact]
|
||||
public void Pow2_Array_CalculatesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
Length[] source = [new(2), new(3), new(10)];
|
||||
|
||||
// Act
|
||||
Length[] result = source.Pow2<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(4, result[0]._Value);
|
||||
Assert.Equal(9, result[1]._Value);
|
||||
Assert.Equal(100, result[2]._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pow2_NullableArray_HandlesNulls()
|
||||
{
|
||||
// Arrange
|
||||
Length?[] source = [new(5), null, new(4)];
|
||||
|
||||
// Act
|
||||
Length?[] result = source.Pow2<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(25, result[0]?._Value);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(16, result[2]?._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pow2_List_ReturnsCorrectResult()
|
||||
{
|
||||
// Arrange
|
||||
var source = new List<Length> { new(2), new(8) };
|
||||
|
||||
// Act
|
||||
var result = source.Pow2<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(4, result[0]._Value);
|
||||
Assert.Equal(64, result[1]._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pow2_IEnumerable_ProcessesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
IEnumerable<Length> source = new HashSet<Length> { new(3), new(7) };
|
||||
|
||||
// Act
|
||||
var result = source.Pow2<Length, Length>().ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Contains(result, x => x._Value == 9);
|
||||
Assert.Contains(result, x => x._Value == 49);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pow2_DestinationTooShort_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
Length[] source = [new(2), new(3)];
|
||||
Length[] dest = new Length[1];
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => source.Pow2(dest));
|
||||
}
|
||||
}
|
||||
59
QWERTYkez.Mensura.Tests/CollectionsPow3Extensions.cs
Normal file
59
QWERTYkez.Mensura.Tests/CollectionsPow3Extensions.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
namespace QWERTYkez.Mensura.Tests;
|
||||
|
||||
public class Pow3ExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Pow3_Array_CalculatesCorrectly()
|
||||
{
|
||||
// Arrange: 2^3=8, 3^3=27, 4^3=64
|
||||
Length[] source = [new(2), new(3), new(4)];
|
||||
|
||||
// Act
|
||||
Length[] result = source.Pow3<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(8, result[0]._Value);
|
||||
Assert.Equal(27, result[1]._Value);
|
||||
Assert.Equal(64, result[2]._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pow3_NullableArray_HandlesNulls()
|
||||
{
|
||||
// Arrange
|
||||
Length?[] source = [new(5), null, new(10)];
|
||||
|
||||
// Act
|
||||
Length?[] result = source.Pow3<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(125, result[0]?._Value);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(1000, result[2]?._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pow3_List_ReturnsCorrectResult()
|
||||
{
|
||||
// Arrange
|
||||
var source = new List<Length> { new(2), new(5) };
|
||||
|
||||
// Act
|
||||
var result = source.Pow3<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(8, result[0]._Value);
|
||||
Assert.Equal(125, result[1]._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Pow3_DestinationTooShort_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
Length[] source = [new(2), new(3)];
|
||||
Length[] dest = new Length[1];
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => source.Pow3(dest));
|
||||
}
|
||||
}
|
||||
75
QWERTYkez.Mensura.Tests/CollectionsPowNExtensions.cs
Normal file
75
QWERTYkez.Mensura.Tests/CollectionsPowNExtensions.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
namespace QWERTYkez.Mensura.Tests;
|
||||
|
||||
public class CollectionsPowNExtensions
|
||||
{
|
||||
private const double BaseVal = 3.0;
|
||||
private const double Expected = 9.0; // 3^2 = 9
|
||||
|
||||
[Fact]
|
||||
public void Test_PowN()
|
||||
{
|
||||
Length[] arr = [new(BaseVal)];
|
||||
Length?[] arrNull = [new(BaseVal), null];
|
||||
List<Length> list = [new(BaseVal)];
|
||||
List<Length?> listNull = [new(BaseVal), null];
|
||||
IReadOnlyCollection<Length> roc = list;
|
||||
IEnumerable<Length> en = list;
|
||||
IEnumerable<Length?> enNull = listNull;
|
||||
|
||||
// --- 1. Целые степени (int) ---
|
||||
// Span-based Core
|
||||
Span<Length> dstSpan = new Length[1];
|
||||
arr.AsSpan().PowCore(2, 1, dstSpan);
|
||||
Assert.Equal(Expected, dstSpan[0]._Value);
|
||||
|
||||
arrNull.AsSpan().PowCore(2, 2, new Length?[2]); // Проверка nullable span
|
||||
|
||||
arr.AsSpan().Pow(2, dstSpan);
|
||||
Assert.Equal(Expected, dstSpan[0]._Value);
|
||||
|
||||
// Arrays
|
||||
Assert.Equal(Expected, arr.Pow<Length, Length>(2)[0]._Value);
|
||||
Assert.Equal(Expected, arrNull.Pow<Length, Length>(2)[0]?._Value);
|
||||
|
||||
// Lists
|
||||
Assert.Equal(Expected, list.Pow<Length, Length>(2)[0]._Value);
|
||||
Assert.Equal(Expected, listNull.Pow<Length, Length>(2)[0]?._Value);
|
||||
|
||||
// ReadOnlyCollection
|
||||
roc.Pow(2, dstSpan);
|
||||
Assert.Equal(Expected, dstSpan[0]._Value);
|
||||
|
||||
// IEnumerable / Iterators
|
||||
Assert.Equal(Expected, en.Pow<Length, Length>(2).First()._Value);
|
||||
Assert.Equal(Expected, enNull.Pow<Length, Length>(2).First(x => x.HasValue)?._Value);
|
||||
|
||||
|
||||
// --- 2. Дробные степени (double) ---
|
||||
// Span-based Core
|
||||
arr.AsSpan().PowCore(2.0, 1, dstSpan);
|
||||
Assert.Equal(Expected, dstSpan[0]._Value);
|
||||
|
||||
arr.AsSpan().Pow(2.0, dstSpan);
|
||||
Assert.Equal(Expected, dstSpan[0]._Value);
|
||||
|
||||
// Arrays
|
||||
Assert.Equal(Expected, arr.Pow<Length, Length>(2.0)[0]._Value);
|
||||
Assert.Equal(Expected, arrNull.Pow<Length, Length>(2.0)[0]?._Value);
|
||||
|
||||
// Lists
|
||||
Assert.Equal(Expected, list.Pow<Length, Length>(2.0)[0]._Value);
|
||||
Assert.Equal(Expected, listNull.Pow<Length, Length>(2.0)[0]?._Value);
|
||||
|
||||
// ReadOnlyCollection
|
||||
roc.Pow(2.0, dstSpan);
|
||||
Assert.Equal(Expected, dstSpan[0]._Value);
|
||||
|
||||
// IEnumerable / Iterators
|
||||
Assert.Equal(Expected, en.Pow<Length, Length>(2.0).First()._Value);
|
||||
Assert.Equal(Expected, enNull.Pow<Length, Length>(2.0).First(x => x.HasValue)?._Value);
|
||||
|
||||
// Дополнительные итераторы
|
||||
Assert.Equal(Expected, en.PowIterator<Length, Length>(2.0).First()._Value);
|
||||
Assert.Equal(Expected, enNull.PowNullableIterator<Length, Length>(2.0).First(x => x.HasValue)?._Value);
|
||||
}
|
||||
}
|
||||
59
QWERTYkez.Mensura.Tests/CollectionsRootOfCubeExtensions.cs
Normal file
59
QWERTYkez.Mensura.Tests/CollectionsRootOfCubeExtensions.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
namespace QWERTYkez.Mensura.Tests;
|
||||
|
||||
public class CollectionsRootOfCubeExtensions
|
||||
{
|
||||
[Fact]
|
||||
public void Cbrt_Array_CalculatesCorrectly()
|
||||
{
|
||||
Length[] source = [new(4), new(9), new(16), new(25)];
|
||||
// Ожидаем корень, но важно: Math.Sqrt(4) = 2.
|
||||
// Если ваш код делает специфичные преобразования, адаптируйте Assert.
|
||||
var result = source.Cbrt<Length, Length>();
|
||||
|
||||
Assert.Equal(2, result[0]._Value);
|
||||
Assert.Equal(3, result[1]._Value);
|
||||
Assert.Equal(4, result[2]._Value);
|
||||
Assert.Equal(5, result[3]._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Cbrt_NullableArray_HandlesNulls()
|
||||
{
|
||||
Length?[] source = [new(16), null, new(36)];
|
||||
var result = source.Cbrt<Length, Length>();
|
||||
|
||||
Assert.Equal(4, result[0]?._Value);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(6, result[2]?._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Cbrt_DestinationTooShort_ThrowsArgumentException()
|
||||
{
|
||||
Length[] source = [new(1), new(4)];
|
||||
Length[] dest = new Length[1];
|
||||
|
||||
Assert.Throws<ArgumentException>(() => source.Cbrt(dest));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Cbrt_List_WorksCorrectly()
|
||||
{
|
||||
var list = new List<Length> { new(1), new(4) };
|
||||
var result = list.Cbrt<Length, Length>();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal(1, result[0]._Value);
|
||||
Assert.Equal(2, result[1]._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Cbrt_IEnumerable_NoExtraAllocations()
|
||||
{
|
||||
IEnumerable<Length> source = new HashSet<Length> { new(9), new(81) };
|
||||
var result = source.Cbrt<Length, Length>().ToList();
|
||||
|
||||
Assert.Contains(result, x => x._Value == 3);
|
||||
Assert.Contains(result, x => x._Value == 9);
|
||||
}
|
||||
}
|
||||
75
QWERTYkez.Mensura.Tests/CollectionsRootOfSquareExtensions.cs
Normal file
75
QWERTYkez.Mensura.Tests/CollectionsRootOfSquareExtensions.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
namespace QWERTYkez.Mensura.Tests;
|
||||
|
||||
public class CollectionsRootOfSquareExtensions
|
||||
{
|
||||
[Fact]
|
||||
public void Sqrt_Array_ShouldCalculateCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
Length[] source = [new(4), new(9), new(16)];
|
||||
|
||||
// Act
|
||||
Length[] result = source.Sqrt<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result[0]._Value);
|
||||
Assert.Equal(3, result[1]._Value);
|
||||
Assert.Equal(4, result[2]._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sqrt_NullableArray_ShouldHandleNulls()
|
||||
{
|
||||
// Arrange
|
||||
Length?[] source = [new(25), null, new(100)];
|
||||
|
||||
// Act
|
||||
Length?[] result = source.Sqrt<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(5, result[0]?._Value);
|
||||
Assert.Null(result[1]);
|
||||
Assert.Equal(10, result[2]?._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sqrt_List_ShouldReturnCorrectList()
|
||||
{
|
||||
// Arrange
|
||||
var source = new List<Length> { new(1), new(4), new(9) };
|
||||
|
||||
// Act
|
||||
var result = source.Sqrt<Length, Length>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.Equal(1, result[0]._Value);
|
||||
Assert.Equal(2, result[1]._Value);
|
||||
Assert.Equal(3, result[2]._Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sqrt_DestinationTooShort_ShouldThrowArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
Length[] source = [new(4), new(9)];
|
||||
Length[] dest = new Length[1]; // Слишком мало
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => source.AsSpan().Sqrt(dest.AsSpan()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sqrt_IEnumerable_ShouldWorkWithGenericCollection()
|
||||
{
|
||||
// Arrange
|
||||
IEnumerable<Length> source = new HashSet<Length> { new(16), new(64) };
|
||||
|
||||
// Act
|
||||
var result = source.Sqrt<Length, Length>().ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Contains(result, x => x._Value == 4);
|
||||
Assert.Contains(result, x => x._Value == 8);
|
||||
}
|
||||
}
|
||||
164
QWERTYkez.Mensura.Tests/DoubleExtensions.cs
Normal file
164
QWERTYkez.Mensura.Tests/DoubleExtensions.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
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<float> sourceList = [.. sourceArray];
|
||||
|
||||
double[] destFromArray = new double[count];
|
||||
double[] destFromList = new double[count];
|
||||
|
||||
// Act
|
||||
// Явное приведение к IReadOnlyCollection устраняет ошибку CS0121 (неоднозначность вызова)
|
||||
((IReadOnlyCollection<float>)sourceArray).ToDouble(destFromArray);
|
||||
((IReadOnlyCollection<float>)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<UInt128?>)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<int> list = [4, 5, 6];
|
||||
IEnumerable<int> 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<ArgumentException>(() => ((IReadOnlyCollection<int>)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<int>?)nullArray)!.ToDouble(destination);
|
||||
((IReadOnlyCollection<int>)emptyArray).ToDouble(destination);
|
||||
|
||||
Assert.All(destination, x => Assert.Equal(0d, x)); // Назначение осталось нетронутым (все нули по умолчанию)
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -7,6 +7,14 @@
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net10.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;IDE0221</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net10.0|AnyCPU'">
|
||||
<NoWarn>1701;1702;IDE0221</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="10.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -23,6 +31,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\QWERTYkez.Mensura\QWERTYkez.Mensura.csproj" />
|
||||
<InternalsVisibleTo Include="..\QWERTYkez.Mensura\QWERTYkez.Mensura.csproj" />
|
||||
|
||||
<ProjectReference Include="..\QWERTYkez.Mensura.Generator\QWERTYkez.Mensura.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
2
QWERTYkez.Mensura.Tests/globals.cs
Normal file
2
QWERTYkez.Mensura.Tests/globals.cs
Normal file
@@ -0,0 +1,2 @@
|
||||
global using QWERTYkez.Mensura.Extensions;
|
||||
global using QWERTYkez.Mensura.Units;
|
||||
Reference in New Issue
Block a user