using GFramework.Core.Abstractions.Pause; using GFramework.Core.Pause; using NUnit.Framework; namespace GFramework.Core.Tests.Pause; /// /// 暂停栈管理器单元测试 /// [TestFixture] public class PauseStackManagerTests { /// /// 在每个测试方法执行前设置测试环境 /// [SetUp] public void SetUp() { _manager = new PauseStackManager(); } /// /// 在每个测试方法执行后清理资源 /// [TearDown] public void TearDown() { _manager.DestroyAsync(); } private PauseStackManager _manager = null!; /// /// 验证Push方法返回有效的令牌 /// [Test] public void Push_Should_ReturnValidToken() { var token = _manager.Push("Test pause"); Assert.That(token.IsValid, Is.True); Assert.That(token.Id, Is.Not.EqualTo(Guid.Empty)); } /// /// 验证Push方法设置暂停状态 /// [Test] public void Push_Should_SetPausedState() { Assert.That(_manager.IsPaused(), Is.False); _manager.Push("Test pause"); Assert.That(_manager.IsPaused(), Is.True); } /// /// 验证Pop方法清除暂停状态 /// [Test] public void Pop_Should_ClearPausedState() { var token = _manager.Push("Test pause"); Assert.That(_manager.IsPaused(), Is.True); _manager.Pop(token); Assert.That(_manager.IsPaused(), Is.False); } /// /// 验证Pop无效令牌返回false /// [Test] public void Pop_WithInvalidToken_Should_ReturnFalse() { var result = _manager.Pop(PauseToken.Invalid); Assert.That(result, Is.False); } /// /// 验证Pop过期令牌返回false /// [Test] public void Pop_WithExpiredToken_Should_ReturnFalse() { var token = _manager.Push("Test pause"); _manager.Pop(token); // 尝试再次 Pop 同一个 Token var result = _manager.Pop(token); Assert.That(result, Is.False); } /// /// 验证多次Push增加深度 /// [Test] public void MultiplePush_Should_IncreaseDepth() { _manager.Push("First"); _manager.Push("Second"); _manager.Push("Third"); Assert.That(_manager.GetPauseDepth(), Is.EqualTo(3)); } /// /// 验证嵌套暂停需要所有Pop才能恢复 /// [Test] public void NestedPause_Should_RequireAllPops() { var token1 = _manager.Push("First"); var token2 = _manager.Push("Second"); _manager.Pop(token1); Assert.That(_manager.IsPaused(), Is.True, "Should still be paused after first pop"); _manager.Pop(token2); Assert.That(_manager.IsPaused(), Is.False, "Should be unpaused after all pops"); } /// /// 验证Pop非栈顶令牌可以正常工作 /// [Test] public void Pop_WithNonTopToken_Should_Work() { var token1 = _manager.Push("First"); var token2 = _manager.Push("Second"); var token3 = _manager.Push("Third"); // Pop 中间的 token var result = _manager.Pop(token2); Assert.That(result, Is.True); Assert.That(_manager.GetPauseDepth(), Is.EqualTo(2)); Assert.That(_manager.IsPaused(), Is.True); // 验证剩余的令牌仍然有效 Assert.That(_manager.Pop(token1), Is.True); Assert.That(_manager.Pop(token3), Is.True); Assert.That(_manager.IsPaused(), Is.False); } /// /// 验证不同组独立工作 /// [Test] public void DifferentGroups_Should_BeIndependent() { _manager.Push("Global pause", PauseGroup.Global); _manager.Push("Gameplay pause", PauseGroup.Gameplay); Assert.That(_manager.IsPaused(PauseGroup.Global), Is.True); Assert.That(_manager.IsPaused(PauseGroup.Gameplay), Is.True); Assert.That(_manager.IsPaused(PauseGroup.Audio), Is.False); } /// /// 验证Pop只影响正确的组 /// [Test] public void Pop_Should_OnlyAffectCorrectGroup() { var globalToken = _manager.Push("Global"); var gameplayToken = _manager.Push("Gameplay", PauseGroup.Gameplay); _manager.Pop(globalToken); Assert.That(_manager.IsPaused(), Is.False); Assert.That(_manager.IsPaused(PauseGroup.Gameplay), Is.True); // 验证 gameplayToken 仍然有效并且可以被正常弹出 Assert.That(_manager.Pop(gameplayToken), Is.True); Assert.That(_manager.IsPaused(PauseGroup.Gameplay), Is.False); } /// /// 验证GetPauseReasons返回所有原因 /// [Test] public void GetPauseReasons_Should_ReturnAllReasons() { _manager.Push("Menu opened"); _manager.Push("Dialog shown"); _manager.Push("Inventory opened"); var reasons = _manager.GetPauseReasons(); Assert.That(reasons.Count, Is.EqualTo(3)); Assert.That(reasons, Does.Contain("Menu opened")); Assert.That(reasons, Does.Contain("Dialog shown")); Assert.That(reasons, Does.Contain("Inventory opened")); } /// /// 验证空栈GetPauseReasons返回空列表 /// [Test] public void GetPauseReasons_WithEmptyStack_Should_ReturnEmptyList() { var reasons = _manager.GetPauseReasons(); Assert.That(reasons, Is.Empty); } /// /// 验证Push触发状态变化事件 /// [Test] public void Push_Should_TriggerEventWhenStateChanges() { bool eventTriggered = false; PauseGroup? eventGroup = null; bool? eventIsPaused = null; _manager.OnPauseStateChanged += (group, isPaused) => { eventTriggered = true; eventGroup = group; eventIsPaused = isPaused; }; _manager.Push("Test", PauseGroup.Gameplay); Assert.That(eventTriggered, Is.True); Assert.That(eventGroup, Is.EqualTo(PauseGroup.Gameplay)); Assert.That(eventIsPaused, Is.True); } /// /// 验证Pop在栈变空时触发事件 /// [Test] public void Pop_Should_TriggerEventWhenStackBecomesEmpty() { var token = _manager.Push("Test"); bool eventTriggered = false; _manager.OnPauseStateChanged += (group, isPaused) => { eventTriggered = true; Assert.That(isPaused, Is.False); }; _manager.Pop(token); Assert.That(eventTriggered, Is.True); } /// /// 验证多次Push只触发一次事件 /// [Test] public void MultiplePush_Should_OnlyTriggerEventOnce() { int eventCount = 0; _manager.OnPauseStateChanged += (_, _) => eventCount++; _manager.Push("First"); _manager.Push("Second"); Assert.That(eventCount, Is.EqualTo(1)); } /// /// 验证处理器在状态变化时被通知 /// [Test] public void Handler_Should_BeNotifiedOnStateChange() { var mockHandler = new MockPauseHandler(); _manager.RegisterHandler(mockHandler); _manager.Push("Test", PauseGroup.Global); Assert.That(mockHandler.CallCount, Is.EqualTo(1)); Assert.That(mockHandler.LastGroup, Is.EqualTo(PauseGroup.Global)); Assert.That(mockHandler.LastIsPaused, Is.True); } /// /// 验证处理器在恢复时被通知 /// [Test] public void Handler_Should_BeNotifiedOnResume() { var mockHandler = new MockPauseHandler(); _manager.RegisterHandler(mockHandler); var token = _manager.Push("Test"); mockHandler.Reset(); _manager.Pop(token); Assert.That(mockHandler.CallCount, Is.EqualTo(1)); Assert.That(mockHandler.LastIsPaused, Is.False); } /// /// 验证多个处理器按优先级顺序调用 /// [Test] public void MultipleHandlers_Should_BeCalledInPriorityOrder() { var calls = new List(); var handler1 = new MockPauseHandler { Priority = 10 }; var handler2 = new MockPauseHandler { Priority = 5 }; var handler3 = new MockPauseHandler { Priority = 15 }; handler1.OnCall = () => calls.Add(1); handler2.OnCall = () => calls.Add(2); handler3.OnCall = () => calls.Add(3); _manager.RegisterHandler(handler1); _manager.RegisterHandler(handler2); _manager.RegisterHandler(handler3); _manager.Push("Test"); Assert.That(calls, Is.EqualTo(new[] { 2, 1, 3 })); } /// /// 验证PauseScope在Dispose时自动恢复 /// [Test] public void PauseScope_Should_AutoResumeOnDispose() { using (_manager.PauseScope("Test")) { Assert.That(_manager.IsPaused(), Is.True); } Assert.That(_manager.IsPaused(), Is.False); } /// /// 验证嵌套PauseScope正常工作 /// [Test] public void NestedPauseScope_Should_Work() { using (_manager.PauseScope("Outer")) { Assert.That(_manager.GetPauseDepth(), Is.EqualTo(1)); using (_manager.PauseScope("Inner")) { Assert.That(_manager.GetPauseDepth(), Is.EqualTo(2)); } Assert.That(_manager.GetPauseDepth(), Is.EqualTo(1)); } Assert.That(_manager.GetPauseDepth(), Is.EqualTo(0)); } /// /// 验证ClearGroup移除指定组的所有暂停 /// [Test] public void ClearGroup_Should_RemoveAllPausesForGroup() { _manager.Push("First", PauseGroup.Gameplay); _manager.Push("Second", PauseGroup.Gameplay); _manager.Push("Third", PauseGroup.Audio); _manager.ClearGroup(PauseGroup.Gameplay); Assert.That(_manager.IsPaused(PauseGroup.Gameplay), Is.False); Assert.That(_manager.IsPaused(PauseGroup.Audio), Is.True); } /// /// 验证ClearAll移除所有暂停 /// [Test] public void ClearAll_Should_RemoveAllPauses() { _manager.Push("First", PauseGroup.Global); _manager.Push("Second", PauseGroup.Gameplay); _manager.Push("Third", PauseGroup.Audio); _manager.ClearAll(); Assert.That(_manager.IsPaused(PauseGroup.Global), Is.False); Assert.That(_manager.IsPaused(PauseGroup.Gameplay), Is.False); Assert.That(_manager.IsPaused(PauseGroup.Audio), Is.False); } /// /// 验证并发Push是线程安全的 /// [Test] public void ConcurrentPush_Should_BeThreadSafe() { var tasks = new List(); var tokens = new List(); var lockObj = new object(); for (int i = 0; i < 100; i++) { var index = i; tasks.Add(Task.Run(() => { var token = _manager.Push($"Pause {index}"); lock (lockObj) { tokens.Add(token); } })); } Task.WaitAll(tasks.ToArray()); Assert.That(_manager.GetPauseDepth(), Is.EqualTo(100)); Assert.That(tokens.Count, Is.EqualTo(100)); } /// /// 验证并发Pop是线程安全的 /// [Test] public void ConcurrentPop_Should_BeThreadSafe() { var tokens = new List(); for (int i = 0; i < 100; i++) { tokens.Add(_manager.Push($"Pause {i}")); } var tasks = tokens.Select(token => Task.Run(() => _manager.Pop(token))).ToList(); Task.WaitAll(tasks.ToArray()); Assert.That(_manager.GetPauseDepth(), Is.EqualTo(0)); Assert.That(_manager.IsPaused(), Is.False); } /// /// 测试用的暂停处理器实现 /// private class MockPauseHandler : IPauseHandler { public int CallCount { get; private set; } public PauseGroup? LastGroup { get; private set; } public bool? LastIsPaused { get; private set; } public Action? OnCall { get; set; } public int Priority { get; set; } = 0; public void OnPauseStateChanged(PauseGroup group, bool isPaused) { CallCount++; LastGroup = group; LastIsPaused = isPaused; OnCall?.Invoke(); } public void Reset() { CallCount = 0; LastGroup = null; LastIsPaused = null; } } }