firstTest

This commit is contained in:
2026-06-08 01:20:30 +07:00
parent 0591c666c2
commit 78c1cc0ec8
5 changed files with 227 additions and 25 deletions

View File

@@ -407,6 +407,9 @@ public readonly partial record struct {typeNameZ} : IMensuraUnit<{typeNameZ}>, I
{ get => ({typeNameA})_Value; init => _Value = (double)value; }
public static explicit operator {typeNameZ}(double val) => Unsafe.As<double, {typeNameZ}>(ref val);
public static explicit operator double({typeNameZ} unit) => unit._Value;
[JsonIgnore, IgnoreDataMember] public bool IsPositive => _Value >= 0;
[JsonIgnore, IgnoreDataMember] public bool IsGreaterThanZero => _Value > 0;

View File

@@ -0,0 +1,166 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
namespace G;
[Generator]
public class TestsGenerator : IIncrementalGenerator
{
private const string UnitAttributeFullName = "QWERTYkez.Mensura.UnitGeneratorAttribute";
private const string ComplexAttributeFullName = "QWERTYkez.Mensura.ComplexUnitGeneratorAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var compilationProvider = context.CompilationProvider;
context.RegisterSourceOutput(compilationProvider, (spc, compilation) =>
{
// Генерируем тесты только если это тестовый проект
var assemblyName = compilation.AssemblyName;
if (assemblyName == null || !assemblyName.EndsWith(".Tests"))
return;
// Находим основную сборку
var mensuraAssembly = compilation.References
.Select(r => compilation.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol)
.FirstOrDefault(a => a?.Name == "QWERTYkez.Mensura" || a?.Name?.EndsWith(".Mensura") == true);
if (mensuraAssembly == null)
return;
// Собираем типы с атрибутами
var targetTypes = new List<INamedTypeSymbol>();
CollectTypesWithAttribute(mensuraAssembly.GlobalNamespace, targetTypes, UnitAttributeFullName);
CollectTypesWithAttribute(mensuraAssembly.GlobalNamespace, targetTypes, ComplexAttributeFullName);
if (targetTypes.Count == 0)
return;
foreach (var type in targetTypes)
{
var typeName = type.Name;
var unitProperties = GetStaticProperties(type);
var firstNonBaseProp = unitProperties.FirstOrDefault(p => !p.Name.StartsWith("_"));
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();
sb.AppendLine($"namespace QWERTYkez.Mensura.Tests");
sb.AppendLine("{");
sb.AppendLine($" public class {typeName}Tests");
sb.AppendLine(" {");
sb.AppendLine($" private const double Tolerance = 1e-12;");
sb.AppendLine();
// Тест значения по умолчанию
sb.AppendLine($" [Fact]");
sb.AppendLine($" public void Default_ValueIsZero()");
sb.AppendLine($" {{");
sb.AppendLine($" var unit = new {typeName}();");
sb.AppendLine($" Assert.Equal(0, (double)unit, Tolerance);");
sb.AppendLine($" }}");
sb.AppendLine();
// Тесты статических свойств (единиц)
foreach (var prop in unitProperties)
{
bool isBase = prop.Name.StartsWith("_");
sb.AppendLine($" [Fact]");
sb.AppendLine($" public void {prop.Name}_IsCorrect()");
sb.AppendLine($" {{");
sb.AppendLine($" var unit = {typeName}.{prop.Name};");
if (isBase)
{
sb.AppendLine($" Assert.Equal(1, (double)unit, Tolerance);");
}
else
{
sb.AppendLine($" Assert.True((double)unit > 0);");
}
sb.AppendLine($" }}");
sb.AppendLine();
}
// Тесты коллекционных методов
if (unitProperties.Length > 0 && firstNonBaseProp.Name != null)
{
sb.AppendLine($" [Fact]");
sb.AppendLine($" public void CollectionOperations_Work()");
sb.AppendLine($" {{");
sb.AppendLine($" var first = {typeName}.{firstNonBaseProp.Name};");
sb.AppendLine($" var items = new[] {{ first, first }};");
sb.AppendLine($" var sum = items.Sum();");
sb.AppendLine($" Assert.True((double)sum > 0);");
sb.AppendLine($" var avg = items.Average();");
sb.AppendLine($" Assert.True((double)avg > 0);");
sb.AppendLine($" var max = items.Max();");
sb.AppendLine($" Assert.True((double)max > 0);");
sb.AppendLine($" var min = items.Min();");
sb.AppendLine($" Assert.True((double)min > 0);");
sb.AppendLine($" }}");
sb.AppendLine();
}
sb.AppendLine(" }");
sb.AppendLine("}");
spc.AddSource($"{typeName}Tests.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
}
});
}
private static void CollectTypesWithAttribute(INamespaceSymbol ns, List<INamedTypeSymbol> results, string attributeFullName)
{
foreach (var type in ns.GetTypeMembers())
{
if (HasAttribute(type, attributeFullName))
results.Add(type);
}
foreach (var childNs in ns.GetNamespaceMembers())
{
CollectTypesWithAttribute(childNs, results, attributeFullName);
}
}
private static bool HasAttribute(INamedTypeSymbol type, string attributeFullName)
{
foreach (var attr in type.GetAttributes())
{
if (attr.AttributeClass?.ToString() == attributeFullName)
return true;
}
return false;
}
private static ImmutableArray<UnitProperty> GetStaticProperties(INamedTypeSymbol type)
{
var props = new List<UnitProperty>();
foreach (var member in type.GetMembers())
{
if (member is IPropertySymbol prop && prop.IsStatic && prop.DeclaredAccessibility == Accessibility.Public)
{
var propName = prop.Name;
if (propName == "EqualityContract" || propName.StartsWith("<"))
continue;
props.Add(new UnitProperty(propName, prop.Type.ToString()));
}
}
return [.. props];
}
private readonly struct UnitProperty
{
public string Name { get; }
public string Type { get; }
public UnitProperty(string name, string type) => (Name, Type) = (name, type);
}
}