2026-06-08 01:20:30 +07:00
|
|
|
|
using Microsoft.CodeAnalysis;
|
|
|
|
|
|
using Microsoft.CodeAnalysis.Text;
|
|
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
|
|
namespace G;
|
|
|
|
|
|
|
|
|
|
|
|
[Generator]
|
|
|
|
|
|
public class TestsGenerator : IIncrementalGenerator
|
|
|
|
|
|
{
|
|
|
|
|
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
var compilationProvider = context.CompilationProvider;
|
|
|
|
|
|
|
|
|
|
|
|
context.RegisterSourceOutput(compilationProvider, (spc, compilation) =>
|
|
|
|
|
|
{
|
2026-06-08 12:00:10 +07:00
|
|
|
|
if (compilation.AssemblyName == null || !compilation.AssemblyName.EndsWith(".Tests"))
|
2026-06-08 01:20:30 +07:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
var mensuraAssembly = compilation.References
|
|
|
|
|
|
.Select(r => compilation.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol)
|
2026-06-09 16:45:22 +07:00
|
|
|
|
.FirstOrDefault(a => a?.Name == "QWERTYkez.Mensura");
|
2026-06-08 01:20:30 +07:00
|
|
|
|
|
|
|
|
|
|
if (mensuraAssembly == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
var unitTypes = CollectAllUnitTypes(mensuraAssembly.GlobalNamespace);
|
|
|
|
|
|
if (unitTypes.Count == 0) return;
|
2026-06-08 01:20:30 +07:00
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
// 1. Сериализация
|
|
|
|
|
|
GenerateSerializationTests(spc, unitTypes);
|
2026-06-08 01:20:30 +07:00
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
// 2. Операторы (с атрибутом)
|
|
|
|
|
|
var operators = CollectOperatorsFromUnitTypes(unitTypes);
|
|
|
|
|
|
if (operators.Length > 0)
|
|
|
|
|
|
GenerateOperatorTests(spc, operators);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. Коллекционные методы
|
|
|
|
|
|
GenerateCollectionTests(spc, unitTypes);
|
2026-06-08 01:20:30 +07:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
// ========== 1. СЕРИАЛИЗАЦИЯ ==========
|
|
|
|
|
|
private static void GenerateSerializationTests(SourceProductionContext spc, List<INamedTypeSymbol> types)
|
2026-06-08 12:00:10 +07:00
|
|
|
|
{
|
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
|
sb.AppendLine("// <auto-generated/>");
|
|
|
|
|
|
sb.AppendLine("#pragma warning disable");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
sb.AppendLine("using Xunit;");
|
2026-06-09 16:45:22 +07:00
|
|
|
|
sb.AppendLine("using System.Text.Json;");
|
|
|
|
|
|
sb.AppendLine("using Newtonsoft.Json;");
|
2026-06-08 12:00:10 +07:00
|
|
|
|
sb.AppendLine("using QWERTYkez.Mensura.Units;");
|
|
|
|
|
|
sb.AppendLine();
|
2026-06-09 16:45:22 +07:00
|
|
|
|
sb.AppendLine("namespace QWERTYkez.Mensura.Tests");
|
2026-06-08 12:00:10 +07:00
|
|
|
|
sb.AppendLine("{");
|
2026-06-09 16:45:22 +07:00
|
|
|
|
sb.AppendLine(" public class SerializationTests");
|
2026-06-08 12:00:10 +07:00
|
|
|
|
sb.AppendLine(" {");
|
2026-06-09 16:45:22 +07:00
|
|
|
|
sb.AppendLine(" private const double Tolerance = 1e-12;");
|
2026-06-08 12:00:10 +07:00
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
foreach (var type in types)
|
2026-06-08 12:00:10 +07:00
|
|
|
|
{
|
2026-06-09 16:45:22 +07:00
|
|
|
|
var baseProp = GetBaseUnitProperty(type);
|
|
|
|
|
|
if (baseProp == null) continue;
|
|
|
|
|
|
var typeName = type.Name;
|
|
|
|
|
|
var propName = baseProp.Name;
|
|
|
|
|
|
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_SystemTextJson_SerializeDeserialize()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var original = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var json = System.Text.Json.JsonSerializer.Serialize(original);");
|
2026-06-08 12:00:10 +07:00
|
|
|
|
sb.AppendLine($" var deserialized = System.Text.Json.JsonSerializer.Deserialize<{typeName}>(json);");
|
2026-06-09 16:45:22 +07:00
|
|
|
|
sb.AppendLine($" Assert.Equal((double)original, (double)deserialized, Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_NewtonsoftJson_SerializeDeserialize()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var original = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var json = Newtonsoft.Json.JsonConvert.SerializeObject(original);");
|
2026-06-08 12:00:10 +07:00
|
|
|
|
sb.AppendLine($" var deserialized = Newtonsoft.Json.JsonConvert.DeserializeObject<{typeName}>(json);");
|
2026-06-09 16:45:22 +07:00
|
|
|
|
sb.AppendLine($" Assert.Equal((double)original, (double)deserialized, Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
2026-06-08 12:00:10 +07:00
|
|
|
|
}
|
2026-06-09 16:45:22 +07:00
|
|
|
|
|
2026-06-08 12:00:10 +07:00
|
|
|
|
sb.AppendLine(" }");
|
|
|
|
|
|
sb.AppendLine("}");
|
2026-06-09 16:45:22 +07:00
|
|
|
|
spc.AddSource("SerializationTests.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 2. ОПЕРАТОРЫ (только с атрибутом) ==========
|
|
|
|
|
|
private static ImmutableArray<OperatorInfo> CollectOperatorsFromUnitTypes(List<INamedTypeSymbol> unitTypes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = new List<OperatorInfo>();
|
|
|
|
|
|
foreach (var containingType in unitTypes)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var member in containingType.GetMembers())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (member is not IMethodSymbol method || method.MethodKind != MethodKind.UserDefinedOperator)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
bool hasAttr = method.GetAttributes().Any(attr =>
|
|
|
|
|
|
attr.AttributeClass?.Name == "OperatorsGeneratorAttribute" ||
|
|
|
|
|
|
attr.AttributeClass?.ToString() == "QWERTYkez.Mensura.OperatorsGeneratorAttribute");
|
|
|
|
|
|
if (!hasAttr) continue;
|
|
|
|
|
|
|
|
|
|
|
|
string opSymbol = method.Name switch
|
|
|
|
|
|
{
|
|
|
|
|
|
"op_Multiply" => "*",
|
|
|
|
|
|
"op_Division" => "/",
|
|
|
|
|
|
_ => null
|
|
|
|
|
|
};
|
|
|
|
|
|
if (opSymbol == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
var pars = method.Parameters;
|
|
|
|
|
|
if (pars.Length != 2) continue;
|
|
|
|
|
|
|
|
|
|
|
|
var leftType = pars[0].Type as INamedTypeSymbol;
|
|
|
|
|
|
var rightType = pars[1].Type as INamedTypeSymbol;
|
|
|
|
|
|
var returnType = method.ReturnType as INamedTypeSymbol;
|
|
|
|
|
|
if (leftType == null || rightType == null || returnType == null) continue;
|
2026-06-08 12:00:10 +07:00
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
if (!unitTypes.Contains(leftType, SymbolEqualityComparer.Default) ||
|
|
|
|
|
|
!unitTypes.Contains(rightType, SymbolEqualityComparer.Default) ||
|
|
|
|
|
|
!unitTypes.Contains(returnType, SymbolEqualityComparer.Default))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
string? coeffField = null;
|
|
|
|
|
|
foreach (var attr in method.GetAttributes())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (attr.AttributeClass?.Name == "OperatorsGeneratorAttribute" ||
|
|
|
|
|
|
attr.AttributeClass?.ToString() == "QWERTYkez.Mensura.OperatorsGeneratorAttribute")
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var arg in attr.ConstructorArguments)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (arg.Value is string s)
|
|
|
|
|
|
{
|
|
|
|
|
|
coeffField = s;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result.Add(new OperatorInfo(opSymbol, leftType, rightType, returnType, coeffField, containingType));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result.ToImmutableArray();
|
2026-06-08 12:00:10 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
private static void GenerateOperatorTests(SourceProductionContext spc, ImmutableArray<OperatorInfo> operators)
|
2026-06-08 01:20:30 +07:00
|
|
|
|
{
|
2026-06-09 16:45:22 +07:00
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
|
sb.AppendLine("// <auto-generated/>");
|
|
|
|
|
|
sb.AppendLine("#pragma warning disable");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
sb.AppendLine("using Xunit;");
|
|
|
|
|
|
sb.AppendLine("using QWERTYkez.Mensura.Units;");
|
|
|
|
|
|
sb.AppendLine("using System.Reflection;");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
sb.AppendLine("namespace QWERTYkez.Mensura.Tests");
|
|
|
|
|
|
sb.AppendLine("{");
|
|
|
|
|
|
sb.AppendLine(" public class OperatorsTests");
|
|
|
|
|
|
sb.AppendLine(" {");
|
|
|
|
|
|
sb.AppendLine(" private const double Tolerance = 1e-12;");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var op in operators)
|
2026-06-08 01:20:30 +07:00
|
|
|
|
{
|
2026-06-09 16:45:22 +07:00
|
|
|
|
var leftBase = GetBaseUnitProperty(op.LeftType);
|
|
|
|
|
|
var rightBase = GetBaseUnitProperty(op.RightType);
|
|
|
|
|
|
if (leftBase == null || rightBase == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
string leftTypeName = op.LeftType.Name;
|
|
|
|
|
|
string rightTypeName = op.RightType.Name;
|
|
|
|
|
|
string returnTypeName = op.ReturnType.Name;
|
|
|
|
|
|
string leftProp = leftBase.Name;
|
|
|
|
|
|
string rightProp = rightBase.Name;
|
|
|
|
|
|
|
|
|
|
|
|
string testName = $"{op.OperatorSymbol}_{leftTypeName}_{rightTypeName}_Returns_{returnTypeName}"
|
|
|
|
|
|
.Replace("<", "_").Replace(">", "_").Replace(",", "_").Replace(" ", "")
|
|
|
|
|
|
.Replace("*", "Mul").Replace("/", "Div");
|
|
|
|
|
|
|
|
|
|
|
|
string expectedExpr;
|
|
|
|
|
|
if (op.CoefficientField != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
expectedExpr = $@"(double)left {op.OperatorSymbol} (double)right * " +
|
|
|
|
|
|
$@"(double)typeof({op.ContainingType.Name}).GetField(""{op.CoefficientField}"", " +
|
|
|
|
|
|
$@"BindingFlags.NonPublic | BindingFlags.Static).GetValue(null)";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
expectedExpr = $"(double)left {op.OperatorSymbol} (double)right";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {testName}()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var left = {leftTypeName}.{leftProp};");
|
|
|
|
|
|
sb.AppendLine($" var right = {rightTypeName}.{rightProp};");
|
|
|
|
|
|
sb.AppendLine($" var result = left {op.OperatorSymbol} right;");
|
|
|
|
|
|
sb.AppendLine($" var expected = {expectedExpr};");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(expected, (double)result, Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
2026-06-08 01:20:30 +07:00
|
|
|
|
}
|
2026-06-09 16:45:22 +07:00
|
|
|
|
|
|
|
|
|
|
if (operators.Length == 0)
|
|
|
|
|
|
sb.AppendLine(" // No operators with [OperatorsGenerator] found");
|
|
|
|
|
|
|
|
|
|
|
|
sb.AppendLine(" }");
|
|
|
|
|
|
sb.AppendLine("}");
|
|
|
|
|
|
spc.AddSource("OperatorsTests.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 3. КОЛЛЕКЦИОННЫЕ МЕТОДЫ ==========
|
|
|
|
|
|
private static void GenerateCollectionTests(SourceProductionContext spc, List<INamedTypeSymbol> types)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
|
sb.AppendLine("// <auto-generated/>");
|
|
|
|
|
|
sb.AppendLine("#pragma warning disable");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
sb.AppendLine("using Xunit;");
|
|
|
|
|
|
sb.AppendLine("using QWERTYkez.Mensura.Units;");
|
|
|
|
|
|
sb.AppendLine("using QWERTYkez.Mensura.Extensions;");
|
|
|
|
|
|
sb.AppendLine("using System.Collections.Generic;");
|
|
|
|
|
|
sb.AppendLine("using System.Linq;");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
sb.AppendLine("namespace QWERTYkez.Mensura.Tests");
|
|
|
|
|
|
sb.AppendLine("{");
|
|
|
|
|
|
sb.AppendLine(" public class CollectionTests");
|
|
|
|
|
|
sb.AppendLine(" {");
|
|
|
|
|
|
sb.AppendLine(" private const double Tolerance = 1e-12;");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var type in types)
|
2026-06-08 01:20:30 +07:00
|
|
|
|
{
|
2026-06-09 16:45:22 +07:00
|
|
|
|
var baseProp = GetBaseUnitProperty(type);
|
|
|
|
|
|
if (baseProp == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
string typeName = type.Name;
|
|
|
|
|
|
string propName = baseProp.Name;
|
|
|
|
|
|
|
|
|
|
|
|
// Создаём массив из двух тестовых значений
|
|
|
|
|
|
sb.AppendLine($" private {typeName}[] GetTestArray() => new[] {{ {typeName}.{propName}, {typeName}.{propName} }};");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
// ========== Multiply ==========
|
|
|
|
|
|
// scalar * array
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Multiply_ScalarByArray_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var arr = GetTestArray();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Multiply(arr);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Length);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// array * scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Multiply_ArrayByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var arr = GetTestArray();");
|
|
|
|
|
|
sb.AppendLine($" var result = arr.Multiply(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Length);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// scalar * List
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Multiply_ScalarByList_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var list = GetTestArray().ToList();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Multiply(list);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// List * scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Multiply_ListByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var list = GetTestArray().ToList();");
|
|
|
|
|
|
sb.AppendLine($" var result = list.Multiply(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// scalar * IEnumerable
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Multiply_ScalarByEnumerable_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var enumerable = GetTestArray().AsEnumerable();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Multiply(enumerable);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count());");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result.ElementAt(0), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result.ElementAt(1), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// IEnumerable * scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Multiply_EnumerableByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var enumerable = GetTestArray().AsEnumerable();");
|
|
|
|
|
|
sb.AppendLine($" var result = enumerable.Multiply(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count());");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result.ElementAt(0), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar * (double)scalar, (double)result.ElementAt(1), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
// ========== Divide ==========
|
|
|
|
|
|
// scalar / array
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Divide_ScalarByArray_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var arr = GetTestArray();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Divide(arr);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Length);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// array / scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Divide_ArrayByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var arr = GetTestArray();");
|
|
|
|
|
|
sb.AppendLine($" var result = arr.Divide(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Length);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// scalar / List
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Divide_ScalarByList_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var list = GetTestArray().ToList();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Divide(list);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// List / scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Divide_ListByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var list = GetTestArray().ToList();");
|
|
|
|
|
|
sb.AppendLine($" var result = list.Divide(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// scalar / IEnumerable
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Divide_ScalarByEnumerable_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var enumerable = GetTestArray().AsEnumerable();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Divide(enumerable);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count());");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result.ElementAt(0), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result.ElementAt(1), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// IEnumerable / scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Divide_EnumerableByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var enumerable = GetTestArray().AsEnumerable();");
|
|
|
|
|
|
sb.AppendLine($" var result = enumerable.Divide(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count());");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result.ElementAt(0), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar / (double)scalar, (double)result.ElementAt(1), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
// ========== Plus ==========
|
|
|
|
|
|
// scalar + array
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Plus_ScalarByArray_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var arr = GetTestArray();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Plus(arr);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Length);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// array + scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Plus_ArrayByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var arr = GetTestArray();");
|
|
|
|
|
|
sb.AppendLine($" var result = arr.Plus(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Length);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// scalar + List
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Plus_ScalarByList_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var list = GetTestArray().ToList();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Plus(list);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// List + scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Plus_ListByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var list = GetTestArray().ToList();");
|
|
|
|
|
|
sb.AppendLine($" var result = list.Plus(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// scalar + IEnumerable
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Plus_ScalarByEnumerable_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var enumerable = GetTestArray().AsEnumerable();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Plus(enumerable);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count());");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result.ElementAt(0), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result.ElementAt(1), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// IEnumerable + scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Plus_EnumerableByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var enumerable = GetTestArray().AsEnumerable();");
|
|
|
|
|
|
sb.AppendLine($" var result = enumerable.Plus(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count());");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result.ElementAt(0), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar + (double)scalar, (double)result.ElementAt(1), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
// ========== Minus ==========
|
|
|
|
|
|
// scalar - array
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Minus_ScalarByArray_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var arr = GetTestArray();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Minus(arr);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Length);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// array - scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Minus_ArrayByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var arr = GetTestArray();");
|
|
|
|
|
|
sb.AppendLine($" var result = arr.Minus(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Length);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// scalar - List
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Minus_ScalarByList_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var list = GetTestArray().ToList();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Minus(list);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// List - scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Minus_ListByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var list = GetTestArray().ToList();");
|
|
|
|
|
|
sb.AppendLine($" var result = list.Minus(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result[0], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result[1], Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// scalar - IEnumerable
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Minus_ScalarByEnumerable_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var enumerable = GetTestArray().AsEnumerable();");
|
|
|
|
|
|
sb.AppendLine($" var result = scalar.Minus(enumerable);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count());");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result.ElementAt(0), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result.ElementAt(1), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
|
|
|
|
|
// IEnumerable - scalar
|
|
|
|
|
|
sb.AppendLine($" [Fact]");
|
|
|
|
|
|
sb.AppendLine($" public void {typeName}_Minus_EnumerableByScalar_Works()");
|
|
|
|
|
|
sb.AppendLine($" {{");
|
|
|
|
|
|
sb.AppendLine($" var scalar = {typeName}.{propName};");
|
|
|
|
|
|
sb.AppendLine($" var enumerable = GetTestArray().AsEnumerable();");
|
|
|
|
|
|
sb.AppendLine($" var result = enumerable.Minus(scalar);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal(2, result.Count());");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result.ElementAt(0), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" Assert.Equal((double)scalar - (double)scalar, (double)result.ElementAt(1), Tolerance);");
|
|
|
|
|
|
sb.AppendLine($" }}");
|
|
|
|
|
|
sb.AppendLine();
|
2026-06-08 01:20:30 +07:00
|
|
|
|
}
|
2026-06-09 16:45:22 +07:00
|
|
|
|
|
|
|
|
|
|
sb.AppendLine(" }");
|
|
|
|
|
|
sb.AppendLine("}");
|
|
|
|
|
|
spc.AddSource("CollectionTests.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ========== ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ ==========
|
|
|
|
|
|
private static List<INamedTypeSymbol> CollectAllUnitTypes(INamespaceSymbol root)
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = new List<INamedTypeSymbol>();
|
|
|
|
|
|
CollectTypesRecursive(root, result, "QWERTYkez.Mensura.Units");
|
|
|
|
|
|
return result;
|
2026-06-08 01:20:30 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
private static void CollectTypesRecursive(INamespaceSymbol ns, List<INamedTypeSymbol> result, string targetNamespacePrefix)
|
2026-06-08 01:20:30 +07:00
|
|
|
|
{
|
2026-06-09 16:45:22 +07:00
|
|
|
|
var nsFullName = ns.ToString();
|
|
|
|
|
|
if (nsFullName.StartsWith(targetNamespacePrefix))
|
2026-06-08 01:20:30 +07:00
|
|
|
|
{
|
2026-06-09 16:45:22 +07:00
|
|
|
|
foreach (var type in ns.GetTypeMembers())
|
|
|
|
|
|
result.Add(type);
|
2026-06-08 01:20:30 +07:00
|
|
|
|
}
|
2026-06-09 16:45:22 +07:00
|
|
|
|
foreach (var childNs in ns.GetNamespaceMembers())
|
|
|
|
|
|
CollectTypesRecursive(childNs, result, targetNamespacePrefix);
|
2026-06-08 01:20:30 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
private static IPropertySymbol? GetBaseUnitProperty(ITypeSymbol type)
|
2026-06-08 01:20:30 +07:00
|
|
|
|
{
|
|
|
|
|
|
foreach (var member in type.GetMembers())
|
|
|
|
|
|
{
|
2026-06-09 16:45:22 +07:00
|
|
|
|
if (member is IPropertySymbol prop && prop.IsStatic && prop.Name.StartsWith("_") && SymbolEqualityComparer.Default.Equals(prop.Type, type))
|
|
|
|
|
|
return prop;
|
2026-06-08 01:20:30 +07:00
|
|
|
|
}
|
2026-06-09 16:45:22 +07:00
|
|
|
|
return null;
|
2026-06-08 01:20:30 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-09 16:45:22 +07:00
|
|
|
|
private readonly struct OperatorInfo(string symbol, ITypeSymbol left, ITypeSymbol right, ITypeSymbol ret, string? coeff, ITypeSymbol containing)
|
2026-06-08 01:20:30 +07:00
|
|
|
|
{
|
2026-06-09 16:45:22 +07:00
|
|
|
|
public string OperatorSymbol { get; } = symbol;
|
|
|
|
|
|
public ITypeSymbol LeftType { get; } = left;
|
|
|
|
|
|
public ITypeSymbol RightType { get; } = right;
|
|
|
|
|
|
public ITypeSymbol ReturnType { get; } = ret;
|
|
|
|
|
|
public string? CoefficientField { get; } = coeff;
|
|
|
|
|
|
public ITypeSymbol ContainingType { get; } = containing;
|
2026-06-08 01:20:30 +07:00
|
|
|
|
}
|
|
|
|
|
|
}
|