using System.IO; using System.Runtime.CompilerServices; using GFramework.Core.Architectures; using GFramework.Core.Coroutine.Extensions; namespace GFramework.Core.Tests.Packaging; /// /// 验证运行时模块在构建期间会自动生成 transitive global usings 资产。 /// 该测试覆盖命名空间自动发现、框架侧过滤和消费者侧排除钩子的最终构建产物。 /// [TestFixture] public class TransitiveGlobalUsingsPackagingTests { /// /// 使用真实类型派生架构命名空间,避免测试断言和命名空间重构脱节。 /// private static readonly string ArchitectureNamespace = typeof(Architecture).Namespace ?? throw new InvalidOperationException( "Architecture namespace should not be null."); /// /// 使用真实类型派生扩展命名空间,避免对字面量命名空间字符串的重复维护。 /// private static readonly string ExtensionsNamespace = typeof(ContextAwareEnvironmentExtensions).Namespace ?? throw new InvalidOperationException( "Extensions namespace should not be null."); /// /// 使用真实类型派生协程扩展命名空间,确保断言和源码自动发现保持一致。 /// private static readonly string CoroutineExtensionsNamespace = typeof(CoroutineExtensions).Namespace ?? throw new InvalidOperationException( "Coroutine extensions namespace should not be null."); /// /// 验证 GFramework.Core 在构建后会生成 transitive global usings props, /// 且 props 内容来自源码自动发现,并保留消费者侧排除机制。 /// [Test] public void CoreBuild_Should_Generate_AutoDiscovered_TransitiveGlobalUsingsProps() { var repositoryRoot = ResolveRepositoryRoot(); var propsPath = Path.Combine( repositoryRoot, "GFramework.Core", "obj", "gframework", "GeWuYou.GFramework.Core.props"); Assert.That(File.Exists(propsPath), Is.True, $"Expected generated props to exist: {propsPath}"); var propsContent = File.ReadAllText(propsPath); Assert.That(propsContent, Does.Contain(ExtensionsNamespace)); Assert.That(propsContent, Does.Contain(ArchitectureNamespace)); Assert.That(propsContent, Does.Contain(CoroutineExtensionsNamespace)); Assert.That(propsContent, Does.Contain("Remove=\"@(GFrameworkExcludedUsing)\"")); Assert.That(propsContent, Does.Not.Contain("System.Runtime.CompilerServices")); } /// /// 基于当前测试源文件的已知位置解析仓库根目录。 /// 这里不扫描解决方案文件,避免测试对仓库布局演进产生额外脆弱性。 /// /// 由编译器注入的当前测试源文件绝对路径。 /// 仓库根目录绝对路径。 private static string ResolveRepositoryRoot([CallerFilePath] string sourceFilePath = "") { if (string.IsNullOrWhiteSpace(sourceFilePath)) { throw new InvalidOperationException("Caller file path is required to resolve the repository root."); } var sourceDirectory = Path.GetDirectoryName(sourceFilePath) ?? throw new DirectoryNotFoundException( $"Could not determine the directory for source file path: {sourceFilePath}"); return Path.GetFullPath(Path.Combine(sourceDirectory, "..", "..")); } }