解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
5.57 KiB
X
using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using NewLife.SourceGenerator;
using Xunit;

namespace XUnitTest.SourceGenerator;

/// <summary>服务注册扫描生成器测试</summary>
public class ServiceRegistrarGeneratorTests
{
    #region 测试源码
    private const String Stub = """
namespace NewLife.Model
{
    public interface IObjectContainer { }

    public static class ContainerStub
    {
        public static IObjectContainer AddTransient<TService, TImplementation>(this IObjectContainer container) => container;
        public static IObjectContainer TryAddTransient<TService, TImplementation>(this IObjectContainer container) => container;
        public static IObjectContainer AddSingleton<TService, TImplementation>(this IObjectContainer container) => container;
        public static IObjectContainer TryAddSingleton<TService, TImplementation>(this IObjectContainer container) => container;
        public static IObjectContainer AddScoped<TService, TImplementation>(this IObjectContainer container) => container;
        public static IObjectContainer TryAddScoped<TService, TImplementation>(this IObjectContainer container) => container;
        public static IObjectContainer AddTransient<TService>(this IObjectContainer container) => container;
        public static IObjectContainer AddScoped<TService>(this IObjectContainer container) => container;
        public static IObjectContainer AddSingleton<TService>(this IObjectContainer container, TService instance = null) where TService : class => container;
        public static IObjectContainer TryAddTransient<TService>(this IObjectContainer container, TService instance = null) where TService : class => container;
        public static IObjectContainer AddTransient(this IObjectContainer container, Type serviceType, Type implementationType) => container;
    }

    public static class ServiceFactoryTable
    {
        public static void Register(Type type, Func<Object> factory) { }
    }
}
""";

    private const String Source = """
public interface IFoo { }

public class Foo : IFoo { }

public class NoCtor : IFoo { }

public class WithDep { public WithDep(IFoo foo) { } }

public abstract class AbstractSvc { }

public class InternalCtor { internal InternalCtor() { } }

public class Multi { public Multi() { } public Multi(Int32 x) { } }

public class Consumer
{
    public void Configure(IObjectContainer container)
    {
        container.AddTransient<IFoo, Foo>();
        container.AddSingleton<IFoo, NoCtor>();
        container.TryAddScoped<IFoo, Foo>();
        container.AddScoped<Multi>();
        container.AddTransient<WithDep>();
        container.AddTransient<AbstractSvc>();
        container.AddSingleton<IFoo>(new Foo());
        container.TryAddTransient<IFoo>();
        container.AddTransient(typeof(IFoo), typeof(NoCtor));
        container.AddTransient<IFoo, InternalCtor>();
    }
}
""";

    private static readonly String FullSource = "using NewLife.Model;" + Environment.NewLine + Stub + Environment.NewLine + Source;
    #endregion

    #region 测试
    /// <summary>扫描注册调用点,为唯一公开无参构造的实现生成工厂注册</summary>
    [Fact(DisplayName = "扫描注册调用点为无参构造实现生成工厂注册")]
    public void GenerateFactories()
    {
        var (_, result) = GeneratorTestHost.RunWithSymbols(new ServiceRegistrarGenerator(), FullSource, ["NET5_0_OR_GREATER"]);
        var sources = GeneratorTestHost.GetGeneratedSources(result);

        var exception = result.Results.Select(r => r.Exception).FirstOrDefault(e => e != null);
        Assert.True(exception == null, exception?.ToString());

        var text = Assert.Single(sources).Value;
        Assert.Contains("typeof(global::Foo)", text);
        Assert.Contains("typeof(global::NoCtor)", text);
        Assert.Contains("ServiceFactoryTable.Register", text);
        Assert.Contains("ModuleInitializer", text);

        // 重复注册去重
        Assert.Equal(1, CountOccurrences(text, "typeof(global::Foo)"));

        // 有参构造/多构造/抽象/无公开构造/实例重载:均不生成
        Assert.DoesNotContain("WithDep", text);
        Assert.DoesNotContain("Multi", text);
        Assert.DoesNotContain("AbstractSvc", text);
        Assert.DoesNotContain("InternalCtor", text);
    }

    /// <summary>生成产物与用户代码一起编译无错误(含模块初始化器与工厂委托)</summary>
    [Fact(DisplayName = "生成产物参与编译无错误")]
    public void GeneratedSourceCompiles()
    {
        var (compilation, _) = GeneratorTestHost.RunWithSymbols(new ServiceRegistrarGenerator(), FullSource, ["NET5_0_OR_GREATER"]);
        var errors = compilation.GetDiagnostics().Where(e => e.Severity == DiagnosticSeverity.Error).ToList();

        Assert.True(errors.Count == 0, String.Join(Environment.NewLine, errors.Select(e => e.ToString())));
    }

    /// <summary>低版本目标(无 NET5_0_OR_GREATER 符号)不生成,行为零影响</summary>
    [Fact(DisplayName = "低版本目标不生成")]
    public void SkipBelowNet5()
    {
        var (_, result) = GeneratorTestHost.Run(new ServiceRegistrarGenerator(), FullSource);
        var sources = GeneratorTestHost.GetGeneratedSources(result);

        Assert.Empty(sources);
    }
    #endregion

    #region 辅助
    private static Int32 CountOccurrences(String text, String value)
    {
        var count = 0;
        var index = 0;
        while ((index = text.IndexOf(value, index, StringComparison.Ordinal)) >= 0)
        {
            count++;
            index += value.Length;
        }

        return count;
    }
    #endregion
}