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) => { if (compilation.AssemblyName == null || !compilation.AssemblyName.EndsWith(".Tests")) return; var mensuraAssembly = compilation.References .Select(r => compilation.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol) .FirstOrDefault(a => a?.Name == "QWERTYkez.Mensura"); if (mensuraAssembly == null) return; var unitTypes = CollectAllUnitTypes(mensuraAssembly.GlobalNamespace); if (unitTypes.Count == 0) return; // 1. Сериализация GenerateSerializationTests(spc, unitTypes); // 2. Операторы (с атрибутом) var operators = CollectOperatorsFromUnitTypes(unitTypes); if (operators.Length > 0) GenerateOperatorTests(spc, operators); // 3. Коллекционные методы GenerateCollectionTests(spc, unitTypes); }); } // ========== 1. СЕРИАЛИЗАЦИЯ ========== private static void GenerateSerializationTests(SourceProductionContext spc, List types) { var sb = new StringBuilder(); sb.AppendLine("// "); sb.AppendLine("#pragma warning disable"); sb.AppendLine(); sb.AppendLine("using Xunit;"); sb.AppendLine("using System.Text.Json;"); sb.AppendLine("using Newtonsoft.Json;"); sb.AppendLine("using QWERTYkez.Mensura.Units;"); sb.AppendLine(); sb.AppendLine("namespace QWERTYkez.Mensura.Tests"); sb.AppendLine("{"); sb.AppendLine(" public class SerializationTests"); sb.AppendLine(" {"); sb.AppendLine(" private const double Tolerance = 1e-12;"); sb.AppendLine(); foreach (var type in types) { 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);"); sb.AppendLine($" var deserialized = System.Text.Json.JsonSerializer.Deserialize<{typeName}>(json);"); 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);"); sb.AppendLine($" var deserialized = Newtonsoft.Json.JsonConvert.DeserializeObject<{typeName}>(json);"); sb.AppendLine($" Assert.Equal((double)original, (double)deserialized, Tolerance);"); sb.AppendLine($" }}"); sb.AppendLine(); } sb.AppendLine(" }"); sb.AppendLine("}"); spc.AddSource("SerializationTests.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8)); } // ========== 2. ОПЕРАТОРЫ (только с атрибутом) ========== private static ImmutableArray CollectOperatorsFromUnitTypes(List unitTypes) { var result = new List(); 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; 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(); } private static void GenerateOperatorTests(SourceProductionContext spc, ImmutableArray operators) { var sb = new StringBuilder(); sb.AppendLine("// "); 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) { 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(); } 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 types) { var sb = new StringBuilder(); sb.AppendLine("// "); 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) { 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(); } sb.AppendLine(" }"); sb.AppendLine("}"); spc.AddSource("CollectionTests.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8)); } // ========== ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ ========== private static List CollectAllUnitTypes(INamespaceSymbol root) { var result = new List(); CollectTypesRecursive(root, result, "QWERTYkez.Mensura.Units"); return result; } private static void CollectTypesRecursive(INamespaceSymbol ns, List result, string targetNamespacePrefix) { var nsFullName = ns.ToString(); if (nsFullName.StartsWith(targetNamespacePrefix)) { foreach (var type in ns.GetTypeMembers()) result.Add(type); } foreach (var childNs in ns.GetNamespaceMembers()) CollectTypesRecursive(childNs, result, targetNamespacePrefix); } private static IPropertySymbol? GetBaseUnitProperty(ITypeSymbol type) { foreach (var member in type.GetMembers()) { if (member is IPropertySymbol prop && prop.IsStatic && prop.Name.StartsWith("_") && SymbolEqualityComparer.Default.Equals(prop.Type, type)) return prop; } return null; } private readonly struct OperatorInfo(string symbol, ITypeSymbol left, ITypeSymbol right, ITypeSymbol ret, string? coeff, ITypeSymbol containing) { 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; } }