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;
}
}
}