diff --git a/GFramework.Core.Tests/architecture/TestArchitecture.cs b/GFramework.Core.Tests/architecture/SyncTestArchitecture.cs similarity index 60% rename from GFramework.Core.Tests/architecture/TestArchitecture.cs rename to GFramework.Core.Tests/architecture/SyncTestArchitecture.cs index 56a6546..20bba26 100644 --- a/GFramework.Core.Tests/architecture/TestArchitecture.cs +++ b/GFramework.Core.Tests/architecture/SyncTestArchitecture.cs @@ -1,15 +1,18 @@ -using GFramework.Core.architecture; +using GFramework.Core.Abstractions.enums; +using GFramework.Core.architecture; using GFramework.Core.events; using GFramework.Core.Tests.model; using GFramework.Core.Tests.system; namespace GFramework.Core.Tests.architecture; -public sealed class TestArchitecture : Architecture +public sealed class SyncTestArchitecture : Architecture { public bool ReadyEventFired { get; private set; } public bool InitCalled { get; private set; } + public List PhaseHistory { get; } = new(); + protected override void Init() { InitCalled = true; @@ -18,4 +21,10 @@ public sealed class TestArchitecture : Architecture RegisterSystem(new TestSystem()); Context.RegisterEvent(_ => { ReadyEventFired = true; }); } + + protected override void EnterPhase(ArchitecturePhase next) + { + base.EnterPhase(next); + PhaseHistory.Add(next); + } } \ No newline at end of file diff --git a/GFramework.Core.Tests/model/AsyncTestModel.cs b/GFramework.Core.Tests/model/AsyncTestModel.cs new file mode 100644 index 0000000..3088a60 --- /dev/null +++ b/GFramework.Core.Tests/model/AsyncTestModel.cs @@ -0,0 +1,61 @@ +using GFramework.Core.Abstractions.architecture; +using GFramework.Core.Abstractions.enums; +using GFramework.Core.Abstractions.model; + +namespace GFramework.Core.Tests.model; + +/// +/// 异步测试模型类,实现了IModel和IAsyncInitializable接口 +/// +public sealed class AsyncTestModel : IModel, IAsyncInitializable +{ + private IArchitectureContext _context = null!; + + /// + /// 获取模型是否已初始化的标志 + /// + public bool Inited { get; private set; } + + /// + /// 异步初始化方法,模拟异步初始化过程 + /// + /// 表示异步操作的Task + public async Task InitializeAsync() + { + await Task.Delay(10); + Inited = true; + } + + /// + /// 同步初始化方法,该方法不应该被调用 + /// + /// 当该方法被调用时抛出异常 + public void Init() + { + // sync Init 不应该被调用 + throw new InvalidOperationException("Sync Init should not be called"); + } + + /// + /// 设置架构上下文 + /// + /// 架构上下文对象 + public void SetContext(IArchitectureContext context) + { + _context = context; + } + + /// + /// 获取架构上下文 + /// + /// 架构上下文对象 + public IArchitectureContext GetContext() => _context; + + /// + /// 处理架构阶段事件 + /// + /// 架构阶段枚举值 + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/model/FailingModel.cs b/GFramework.Core.Tests/model/FailingModel.cs new file mode 100644 index 0000000..3b73f3e --- /dev/null +++ b/GFramework.Core.Tests/model/FailingModel.cs @@ -0,0 +1,51 @@ +using GFramework.Core.Abstractions.architecture; +using GFramework.Core.Abstractions.enums; +using GFramework.Core.Abstractions.model; + +namespace GFramework.Core.Tests.model; + +/// +/// 一个用于测试的失败模型类,实现IModel接口 +/// 该模型在初始化时会故意抛出异常,用于测试异常处理机制 +/// +public sealed class FailingModel : IModel +{ + /// + /// 初始化模型 + /// 该方法会故意抛出InvalidOperationException异常 + /// + /// 总是抛出此异常以模拟初始化失败 + public void Init() + { + throw new InvalidOperationException("Model init failed intentionally"); + } + + /// + /// 设置架构上下文 + /// 该方法为空实现,不执行任何操作 + /// + /// 架构上下文对象 + public void SetContext(IArchitectureContext context) + { + } + + /// + /// 获取架构上下文 + /// 该方法会抛出NotSupportedException异常 + /// + /// 不返回任何值,总是抛出异常 + /// 总是抛出此异常 + public IArchitectureContext GetContext() + { + throw new NotSupportedException(); + } + + /// + /// 处理架构阶段事件 + /// 该方法为空实现,不执行任何操作 + /// + /// 当前架构阶段 + public void OnArchitecturePhase(ArchitecturePhase phase) + { + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/system/TestSystem.cs b/GFramework.Core.Tests/system/TestSystem.cs index 3695324..2b642e5 100644 --- a/GFramework.Core.Tests/system/TestSystem.cs +++ b/GFramework.Core.Tests/system/TestSystem.cs @@ -17,12 +17,12 @@ public sealed class TestSystem : ISystem /// /// 获取系统是否已初始化的状态 /// - public bool Inited { get; private set; } + public bool Initialized { get; private set; } /// /// 获取系统是否已销毁的状态 /// - public bool Destroyed { get; private set; } + public bool DestroyCalled { get; private set; } /// /// 设置架构上下文 @@ -47,7 +47,7 @@ public sealed class TestSystem : ISystem /// public void Init() { - Inited = true; + Initialized = true; } /// @@ -55,7 +55,7 @@ public sealed class TestSystem : ISystem /// public void Destroy() { - Destroyed = true; + DestroyCalled = true; } public void OnArchitecturePhase(ArchitecturePhase phase) diff --git a/GFramework.Core.Tests/tests/ArchitectureInitializationTests.cs b/GFramework.Core.Tests/tests/ArchitectureInitializationTests.cs deleted file mode 100644 index fa147af..0000000 --- a/GFramework.Core.Tests/tests/ArchitectureInitializationTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Reflection; -using GFramework.Core.Abstractions.enums; -using GFramework.Core.architecture; -using GFramework.Core.Tests.architecture; -using GFramework.Core.Tests.model; -using GFramework.Core.Tests.system; -using NUnit.Framework; - -namespace GFramework.Core.Tests.tests; - -[TestFixture] -[NonParallelizable] -public class ArchitectureInitializationTests -{ - [SetUp] - public void SetUp() - { - GameContext.Clear(); - _architecture = new TestArchitecture(); - } - - [TearDown] - public void TearDown() - { - try - { - _architecture?.Destroy(); - } - finally - { - GameContext.Clear(); - _architecture = null; - } - } - - private TestArchitecture? _architecture; - - [Test] - public void Architecture_Should_Initialize_All_Components_Correctly() - { - // Act - _architecture!.Initialize(); - - // Assert - Assert.That(_architecture.InitCalled, Is.True); - - Assert.That(_architecture.Runtime, Is.Not.Null); - - var phaseProperty = typeof(Architecture) - .GetProperty("CurrentPhase", BindingFlags.Instance | BindingFlags.NonPublic); - - var phase = (ArchitecturePhase)phaseProperty!.GetValue(_architecture)!; - Assert.That(phase, Is.EqualTo(ArchitecturePhase.Ready)); - - var context = _architecture.Context; - - var model = context.GetModel(); - Assert.That(model, Is.Not.Null); - Assert.That(model!.Inited, Is.True); - - var system = context.GetSystem(); - Assert.That(system, Is.Not.Null); - Assert.That(system!.Inited, Is.True); - } - - [Test] - public void Architecture_Should_Register_Context_By_Type() - { - // Act - _architecture!.Initialize(); - var ctx = GameContext.GetByType(_architecture!.GetType()); - - Assert.That(ctx, Is.Not.Null); - } -} \ No newline at end of file diff --git a/GFramework.Core.Tests/tests/SyncArchitectureTests.cs b/GFramework.Core.Tests/tests/SyncArchitectureTests.cs new file mode 100644 index 0000000..b8d4eed --- /dev/null +++ b/GFramework.Core.Tests/tests/SyncArchitectureTests.cs @@ -0,0 +1,170 @@ +using System.Reflection; +using GFramework.Core.Abstractions.enums; +using GFramework.Core.architecture; +using GFramework.Core.Tests.architecture; +using GFramework.Core.Tests.model; +using GFramework.Core.Tests.system; +using NUnit.Framework; +using NUnit.Framework.Legacy; + +namespace GFramework.Core.Tests.tests; + +/// +/// 同步架构测试类,用于测试同步架构的初始化、生命周期和组件注册等功能 +/// +[TestFixture] +[NonParallelizable] +public class SyncArchitectureTests +{ + /// + /// 测试初始化方法,清理游戏上下文并创建同步测试架构实例 + /// + [SetUp] + public void SetUp() + { + GameContext.Clear(); + _architecture = new SyncTestArchitecture(); + } + + /// + /// 测试清理方法,销毁架构实例并清理游戏上下文 + /// + [TearDown] + public void TearDown() + { + try + { + _architecture?.Destroy(); + } + finally + { + GameContext.Clear(); + _architecture = null; + } + } + + private SyncTestArchitecture? _architecture; + + /// + /// 测试架构是否正确初始化所有组件 + /// 验证初始化调用、运行时状态、架构阶段和模型系统注册 + /// + [Test] + public void Architecture_Should_Initialize_All_Components_Correctly() + { + // Act + _architecture!.Initialize(); + + // Assert + Assert.That(_architecture.InitCalled, Is.True); + + Assert.That(_architecture.Runtime, Is.Not.Null); + + // 通过反射获取当前架构阶段 + var phaseProperty = typeof(Architecture) + .GetProperty("CurrentPhase", BindingFlags.Instance | BindingFlags.NonPublic); + + var phase = (ArchitecturePhase)phaseProperty!.GetValue(_architecture)!; + Assert.That(phase, Is.EqualTo(ArchitecturePhase.Ready)); + + var context = _architecture.Context; + + var model = context.GetModel(); + Assert.That(model, Is.Not.Null); + Assert.That(model!.Inited, Is.True); + + var system = context.GetSystem(); + Assert.That(system, Is.Not.Null); + Assert.That(system!.Initialized, Is.True); + } + + /// + /// 测试架构是否按类型正确注册上下文 + /// + [Test] + public void Architecture_Should_Register_Context_By_Type() + { + // Act + _architecture!.Initialize(); + var ctx = GameContext.GetByType(_architecture!.GetType()); + + Assert.That(ctx, Is.Not.Null); + } + + /// + /// 测试架构是否按正确顺序进入各个阶段 + /// 验证架构初始化过程中各阶段的执行顺序 + /// + [Test] + public void Architecture_Should_Enter_Phases_In_Correct_Order() + { + _architecture!.Initialize(); + + var phases = _architecture.PhaseHistory; + + CollectionAssert.AreEqual( + new[] + { + ArchitecturePhase.BeforeModelInit, + ArchitecturePhase.AfterModelInit, + ArchitecturePhase.BeforeSystemInit, + ArchitecturePhase.AfterSystemInit, + ArchitecturePhase.Ready + }, + phases + ); + } + + /// + /// 测试在架构就绪后注册系统是否抛出异常(当不允许时) + /// + [Test] + public void RegisterSystem_AfterReady_Should_Throw_When_NotAllowed() + { + _architecture!.Initialize(); + + Assert.Throws(() => { _architecture.RegisterSystem(new TestSystem()); }); + } + + /// + /// 测试在架构就绪后注册模型是否抛出异常(当不允许时) + /// + [Test] + public void RegisterModel_AfterReady_Should_Throw_When_NotAllowed() + { + _architecture!.Initialize(); + + Assert.Throws(() => { _architecture.RegisterModel(new TestModel()); }); + } + + /// + /// 测试架构销毁功能,验证销毁后系统被正确销毁且架构进入销毁阶段 + /// + [Test] + public void Architecture_Destroy_Should_Destroy_All_Systems_And_Enter_Destroyed() + { + _architecture!.Initialize(); + _architecture.Destroy(); + + var system = _architecture.Context.GetSystem(); + Assert.That(system!.DestroyCalled, Is.True); + + // 通过反射获取当前架构阶段 + var phaseProperty = typeof(Architecture) + .GetProperty("CurrentPhase", BindingFlags.Instance | BindingFlags.NonPublic); + + var phase = (ArchitecturePhase)phaseProperty!.GetValue(_architecture)!; + Assert.That(phase, Is.EqualTo(ArchitecturePhase.Destroyed)); + } + + /// + /// 测试当模型初始化失败时架构是否停止初始化 + /// + [Test] + public void Architecture_Should_Stop_Initialization_When_Model_Init_Fails() + { + _architecture!.RegisterModel(new FailingModel()); + + Assert.Throws(() => { _architecture!.Initialize(); }); + } +} \ No newline at end of file diff --git a/GFramework.Core/architecture/Architecture.cs b/GFramework.Core/architecture/Architecture.cs index 2ace215..169966d 100644 --- a/GFramework.Core/architecture/Architecture.cs +++ b/GFramework.Core/architecture/Architecture.cs @@ -73,11 +73,11 @@ public abstract class Architecture( { var logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(GetType)); - logger.Debug($"Installing module: {module.GetType().Name}"); + logger.Debug($"Installing module: {module.GetType().Name}.Module"); RegisterLifecycleHook(module); Container.RegisterPlurality(module); module.Install(this); - logger.Info($"Module installed: {module.GetType().Name}"); + logger.Info($"Module installed: {module.GetType().Name}.Module"); } #endregion @@ -102,7 +102,7 @@ public abstract class Architecture( /// /// 标记架构是否已初始化完成 /// - private bool _mInited; + private bool _mInitialized; /// /// 生命周期感知对象列表 @@ -132,7 +132,7 @@ public abstract class Architecture( /// /// 要进入的下一个架构阶段 /// 当阶段转换不被允许时抛出异常 - private void EnterPhase(ArchitecturePhase next) + protected virtual void EnterPhase(ArchitecturePhase next) { if (Configuration.ArchitectureProperties.StrictPhaseValidation && (!ArchitectureConstants.PhaseTransitions.TryGetValue(CurrentPhase, out var allowed) || @@ -154,7 +154,7 @@ public abstract class Architecture( // 通知所有架构阶段感知对象阶段变更 foreach (var obj in Container.GetAll()) { - _logger.Debug($"Notifying phase-aware object {obj.GetType().Name} of phase change to {next}"); + _logger.Trace($"Notifying phase-aware object {obj.GetType().Name} of phase change to {next}"); obj.OnArchitecturePhase(next); } } @@ -166,7 +166,10 @@ public abstract class Architecture( private void NotifyPhase(ArchitecturePhase phase) { foreach (var hook in _lifecycleHooks) + { hook.OnPhase(phase, this); + _logger.Trace($"Notifying lifecycle hook {hook.GetType().Name} of phase {phase}"); + } } /// @@ -228,78 +231,79 @@ public abstract class Architecture( #region Component Registration + /// + /// 同步初始化方法,阻塞当前线程直到初始化完成 + /// public void Initialize() { - // 设置日志工厂提供程序,用于创建日志记录器 - LoggerFactoryResolver.Provider = Configuration.LoggerProperties.LoggerFactoryProvider; - _logger = LoggerFactoryResolver.Provider.CreateLogger(GetType().Name); - _context ??= new ArchitectureContext(Container, TypeEventSystem); - GameContext.Bind(GetType(), _context); - // 创建架构运行时实例 - Runtime = new ArchitectureRuntime(_context); - ((ArchitectureContext)_context).Runtime = Runtime; - // 设置服务的上下文 - Services.SetContext(_context); - // 调用用户实现的初始化 - Init(); - - // == Model Init == - EnterPhase(ArchitecturePhase.BeforeModelInit); - _logger.Info($"Initializing {_mModels.Count} models"); - - // 初始化所有已注册但尚未初始化的模型 - foreach (var model in _mModels) - { - _logger.Debug($"Initializing model: {model.GetType().Name}"); - model.Init(); - } - - _mModels.Clear(); - EnterPhase(ArchitecturePhase.AfterModelInit); - _logger.Info("All models initialized"); - - // == System Init == - EnterPhase(ArchitecturePhase.BeforeSystemInit); - _logger.Info($"Initializing {_mSystems.Count} systems"); - - // 初始化所有已注册但尚未初始化的系统 - foreach (var system in _mSystems) - { - _logger.Debug($"Initializing system: {system.GetType().Name}"); - system.Init(); - } - - _mSystems.Clear(); - EnterPhase(ArchitecturePhase.AfterSystemInit); - _logger.Info("All systems initialized"); - - // == Finalize == - // 冻结IOC容器,不允许 anymore - Container.Freeze(); - _mInited = true; - EnterPhase(ArchitecturePhase.Ready); - // 发送架构生命周期就绪事件 - TypeEventSystem.Send(new ArchitectureEvents.ArchitectureLifecycleReadyEvent()); - _logger.Info($"Architecture {GetType().Name} is ready - all components initialized"); + InitializeInternalAsync(asyncMode: false).GetAwaiter().GetResult(); } - public async Task InitializeAsync() + /// + /// 异步初始化方法,返回Task以便调用者可以等待初始化完成 + /// + /// 表示异步初始化操作的Task + public Task InitializeAsync() { - // 设置日志工厂提供程序,用于创建日志记录器 + return InitializeInternalAsync(asyncMode: true); + } + + /// + /// 异步初始化组件 + /// + /// 要初始化的组件对象 + /// 是否启用异步模式 + /// 表示异步操作的任务 + private static async Task InitializeComponentAsync(object component, bool asyncMode) + { + // 根据组件类型和异步模式选择相应的初始化方法 + if (asyncMode && component is IAsyncInitializable asyncInit) + { + await asyncInit.InitializeAsync(); + } + else if (component is IModel model) + { + model.Init(); + } + else if (component is ISystem system) + { + system.Init(); + } + } + + /// + /// 异步初始化架构内部组件,包括上下文、模型和系统的初始化 + /// + /// 是否启用异步模式进行组件初始化 + /// 异步任务,表示初始化操作的完成 + private async Task InitializeInternalAsync(bool asyncMode) + { + // === 基础上下文 & Logger === + // 设置日志工厂提供程序 LoggerFactoryResolver.Provider = Configuration.LoggerProperties.LoggerFactoryProvider; - // 创建日志记录器 + // 创建日志记录器实例 _logger = LoggerFactoryResolver.Provider.CreateLogger(GetType().Name); + + // 初始化架构上下文(如果尚未初始化) _context ??= new ArchitectureContext(Container, TypeEventSystem); + // 将当前架构类型与上下文绑定到游戏上下文 GameContext.Bind(GetType(), _context); + // 创建架构运行时实例 Runtime = new ArchitectureRuntime(_context); + // 设置上下文中的运行时引用 ((ArchitectureContext)_context).Runtime = Runtime; - // 设置服务的上下文 + // 为服务设置上下文 Services.SetContext(_context); - // 调用用户实现的初始化 - Init(); - // == Model Init == + // === 用户 Init === + // 调用子类实现的初始化方法 + _logger.Debug("Calling user Init()"); + Init(); + _logger.Debug("User Init() completed"); + + // === 模型初始化阶段 === + // 在此阶段初始化所有注册的模型组件 EnterPhase(ArchitecturePhase.BeforeModelInit); _logger.Info($"Initializing {_mModels.Count} models"); @@ -307,17 +311,15 @@ public abstract class Architecture( foreach (var model in _mModels) { _logger.Debug($"Initializing model: {model.GetType().Name}"); - if (model is IAsyncInitializable asyncModel) - await asyncModel.InitializeAsync(); - else - model.Init(); + await InitializeComponentAsync(model, asyncMode); } _mModels.Clear(); EnterPhase(ArchitecturePhase.AfterModelInit); _logger.Info("All models initialized"); - // == System Init == + // === 系统初始化阶段 === + // 在此阶段初始化所有注册的系统组件 EnterPhase(ArchitecturePhase.BeforeSystemInit); _logger.Info($"Initializing {_mSystems.Count} systems"); @@ -325,24 +327,26 @@ public abstract class Architecture( foreach (var system in _mSystems) { _logger.Debug($"Initializing system: {system.GetType().Name}"); - if (system is IAsyncInitializable asyncSystem) - await asyncSystem.InitializeAsync(); - else - system.Init(); + await InitializeComponentAsync(system, asyncMode); } _mSystems.Clear(); EnterPhase(ArchitecturePhase.AfterSystemInit); _logger.Info("All systems initialized"); - // == Finalize == + // === 初始化完成阶段 === + // 冻结IOC容器并标记架构为就绪状态 Container.Freeze(); - _mInited = true; + _logger.Info("IOC container frozen"); + + _mInitialized = true; EnterPhase(ArchitecturePhase.Ready); TypeEventSystem.Send(new ArchitectureEvents.ArchitectureLifecycleReadyEvent()); + _logger.Info($"Architecture {GetType().Name} is ready - all components initialized"); } + /// /// 注册一个系统到架构中。 /// 若当前未初始化,则暂存至待初始化列表;否则立即初始化该系统。 @@ -362,13 +366,13 @@ public abstract class Architecture( system.SetContext(Context); Container.RegisterPlurality(system); _allSystems.Add(system); - if (!_mInited) + if (!_mInitialized) { _mSystems.Add(system); } else { - _logger.Debug($"Immediately initializing system: {typeof(TSystem).Name}"); + _logger.Trace($"Immediately initializing system: {typeof(TSystem).Name}"); system.Init(); } @@ -396,13 +400,13 @@ public abstract class Architecture( setArchitectureMethod?.Invoke(model, [this]); Container.RegisterPlurality(model); - if (!_mInited) + if (!_mInitialized) { _mModels.Add(model); } else { - _logger.Debug($"Immediately initializing model: {typeof(TModel).Name}"); + _logger.Trace($"Immediately initializing model: {typeof(TModel).Name}"); model.Init(); }