mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
feat(config): 添加架构配置集成测试和模块实现
- 实现了 ArchitectureConfigIntegrationTests 测试类,验证配置模块在架构场景下的完整链路 - 添加了 GameConfigModule 类,提供基于 Architecture 的配置模块接入入口 - 实现了配置模块的生命周期管理,包括首次加载和热重载支持 - 集成了 BootstrapInitializationHook 确保配置在 utility 初始化前完成加载 - 添加了模块复用限制和安装窗口验证机制 - 实现了架构销毁时的资源清理和生命周期钩子注册
This commit is contained in:
parent
eb7a8c702c
commit
febf948077
@ -1,15 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.Core.Extensions;
|
||||
using GFramework.Core.Utility;
|
||||
using GFramework.Game.Abstractions.Config;
|
||||
using GFramework.Game.Config;
|
||||
using GFramework.Game.Config.Generated;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Game.Tests.Config;
|
||||
|
||||
@ -153,33 +149,39 @@ public class ArchitectureConfigIntegrationTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证配置启动帮助器在同步阻塞且存在 <see cref="SynchronizationContext" /> 的线程上仍可完成初始化,
|
||||
/// 避免架构生命周期钩子的同步桥接因为 await 捕获上下文而死锁。
|
||||
/// 验证配置模块在同步阻塞且存在 <see cref="SynchronizationContext" /> 的线程上仍可完成架构初始化,
|
||||
/// 直接覆盖 <see cref="GameConfigModule" /> 通过生命周期钩子执行同步桥接的真实路径。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GameConfigBootstrapShouldSupportSynchronousBridgeOnBlockingSynchronizationContext()
|
||||
public void GameConfigModuleShouldSupportSynchronousBridgeOnBlockingSynchronizationContext()
|
||||
{
|
||||
var rootPath = CreateTempConfigRoot();
|
||||
GameConfigBootstrap? bootstrap = null;
|
||||
ConsumerArchitecture? architecture = null;
|
||||
var initialized = false;
|
||||
try
|
||||
{
|
||||
bootstrap = CreateBootstrap(rootPath);
|
||||
architecture = new ConsumerArchitecture(rootPath);
|
||||
|
||||
RunBlockingOnSynchronizationContext(
|
||||
() => bootstrap.InitializeAsync(),
|
||||
() => architecture.InitializeAsync(),
|
||||
TimeSpan.FromSeconds(5));
|
||||
initialized = true;
|
||||
|
||||
var monsterTable = bootstrap.Registry.GetMonsterTable();
|
||||
var monsterTable = architecture.Registry.GetMonsterTable();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(bootstrap.IsInitialized, Is.True);
|
||||
Assert.That(architecture.ConfigModule.IsInitialized, Is.True);
|
||||
Assert.That(monsterTable.Get(1).Name, Is.EqualTo("Slime"));
|
||||
Assert.That(monsterTable.FindByFaction("dungeon").Count(), Is.EqualTo(2));
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
bootstrap?.Dispose();
|
||||
if (architecture is not null && initialized)
|
||||
{
|
||||
architecture.DestroyAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
DeleteDirectoryIfExists(rootPath);
|
||||
}
|
||||
}
|
||||
@ -322,14 +324,9 @@ public class ArchitectureConfigIntegrationTests
|
||||
}
|
||||
}
|
||||
|
||||
private static GameConfigBootstrap CreateBootstrap(string configRoot)
|
||||
{
|
||||
return new GameConfigBootstrap(CreateBootstrapOptions(configRoot));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个使用配置模块的模块实例。
|
||||
/// </summary>
|
||||
/// 创建一个使用配置模块的模块实例。
|
||||
/// </summary>
|
||||
/// <param name="configRoot">测试配置根目录。</param>
|
||||
/// <returns>已配置的模块实例。</returns>
|
||||
private static GameConfigModule CreateModule(string configRoot)
|
||||
|
||||
@ -22,7 +22,7 @@ public sealed class GameConfigModule : IArchitectureModule
|
||||
{
|
||||
private const int InstallStateNotInstalled = 0;
|
||||
private const int InstallStateInstalling = 1;
|
||||
private const int InstallStateInstalled = 2;
|
||||
private const int InstallStateConsumed = 2;
|
||||
|
||||
private readonly GameConfigBootstrap _bootstrap;
|
||||
private readonly ModuleBootstrapLifetimeUtility _lifetimeUtility;
|
||||
@ -76,6 +76,11 @@ public sealed class GameConfigModule : IArchitectureModule
|
||||
/// <param name="architecture">目标架构实例。</param>
|
||||
/// <exception cref="ArgumentNullException">当 <paramref name="architecture" /> 为空时抛出。</exception>
|
||||
/// <exception cref="InvalidOperationException">当同一个模块实例被重复安装时抛出。</exception>
|
||||
/// <remarks>
|
||||
/// 生命周期阶段校验会在任何注册动作前执行,因此错过安装窗口的调用不会消耗当前模块实例。
|
||||
/// 一旦开始向架构注册 utility 或生命周期钩子,就不存在回滚 API;因此后续任何失败都会把该模块实例视为已消耗,
|
||||
/// 调用方必须创建新的 <see cref="GameConfigModule" /> 再重试。
|
||||
/// </remarks>
|
||||
public void Install(IArchitecture architecture)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(architecture);
|
||||
@ -93,16 +98,18 @@ public sealed class GameConfigModule : IArchitectureModule
|
||||
|
||||
try
|
||||
{
|
||||
// 先注册生命周期钩子,确保任何“已错过 BeforeUtilityInit”的安装都会在暴露注册表之前失败,
|
||||
// 避免架构看到永远不会完成首次加载的半安装配置入口。
|
||||
architecture.RegisterLifecycleHook(new BootstrapInitializationHook(_bootstrap));
|
||||
// 阶段窗口已经在前面做过无副作用校验,因此这里优先注册 utility,
|
||||
// 让常见的容器/上下文接线失败在不可回滚的 hook 注册之前暴露出来。
|
||||
architecture.RegisterUtility(Registry);
|
||||
architecture.RegisterUtility(_lifetimeUtility);
|
||||
Volatile.Write(ref _installState, InstallStateInstalled);
|
||||
architecture.RegisterLifecycleHook(new BootstrapInitializationHook(_bootstrap));
|
||||
Volatile.Write(ref _installState, InstallStateConsumed);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Volatile.Write(ref _installState, InstallStateNotInstalled);
|
||||
// 架构对 utility / hook 注册都不提供回滚入口,因此一旦进入注册阶段,
|
||||
// 即使安装失败也必须禁止复用同一个模块实例,避免重复暴露共享注册表或挂上第二个 hook。
|
||||
Volatile.Write(ref _installState, InstallStateConsumed);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user