GFramework/GFramework.Core.Tests/State/StateMachineTests.cs
GeWuYou fb14d7122c docs(style): 更新文档中的命名空间导入格式
- 将所有小写的命名空间导入更正为首字母大写格式
- 统一 GFramework 框架的命名空间引用规范
- 修复 core、ecs、godot 等模块的命名空间导入错误
- 标准化文档示例代码中的 using 语句格式
- 确保所有文档中的命名空间引用保持一致性
- 更新 global using 语句以匹配正确的命名空间格式
2026-03-10 07:18:49 +08:00

604 lines
19 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Reflection;
using GFramework.Core.Abstractions.State;
using GFramework.Core.State;
using NUnit.Framework;
namespace GFramework.Core.Tests.State;
/// <summary>
/// 测试状态机功能的单元测试类
/// </summary>
[TestFixture]
public class StateMachineTests
{
/// <summary>
/// 在每个测试方法执行前初始化状态机实例
/// </summary>
[SetUp]
public void SetUp()
{
_stateMachine = new StateMachine();
}
private StateMachine _stateMachine = null!;
/// <summary>
/// 验证当没有活动状态时当前状态应为null
/// </summary>
[Test]
public void Current_Should_BeNull_When_NoState_Active()
{
Assert.That(_stateMachine.Current, Is.Null);
}
/// <summary>
/// 验证注册状态后,状态会被添加到状态字典中
/// </summary>
[Test]
public void Register_Should_AddState_To_StatesDictionary()
{
var state = new TestStateV2();
_stateMachine.Register(state);
Assert.That(_stateMachine.ContainsState<TestStateV2>(), Is.True);
}
/// <summary>
/// 验证异步注销后状态应从字典中移除
/// </summary>
[Test]
public async Task UnregisterAsync_Should_RemoveState_FromDictionary()
{
var state = new TestStateV2();
_stateMachine.Register(state);
await _stateMachine.UnregisterAsync<TestStateV2>();
Assert.That(_stateMachine.ContainsState<TestStateV2>(), Is.False);
}
/// <summary>
/// 验证异步注销活动异步状态时调用OnExitAsync
/// </summary>
[Test]
public async Task UnregisterAsync_WhenStateIsActive_WithAsyncState_Should_Invoke_OnExitAsync()
{
var state = new TestAsyncState();
_stateMachine.Register(state);
await _stateMachine.ChangeToAsync<TestAsyncState>();
await _stateMachine.UnregisterAsync<TestAsyncState>();
Assert.That(state.ExitCalled, Is.True);
Assert.That(state.ExitTo, Is.Null);
Assert.That(_stateMachine.Current, Is.Null);
}
/// <summary>
/// 验证异步切换检查未注册状态返回false
/// </summary>
[Test]
public async Task CanChangeToAsync_WhenStateNotRegistered_Should_ReturnFalse()
{
var result = await _stateMachine.CanChangeToAsync<TestStateV2>();
Assert.That(result, Is.False);
}
/// <summary>
/// 验证异步切换检查已注册状态返回true
/// </summary>
[Test]
public async Task CanChangeToAsync_WhenStateRegistered_Should_ReturnTrue()
{
var state = new TestStateV2();
_stateMachine.Register(state);
var result = await _stateMachine.CanChangeToAsync<TestStateV2>();
Assert.That(result, Is.True);
}
/// <summary>
/// 验证异步切换检查使用异步状态时调用CanTransitionToAsync
/// </summary>
[Test]
public async Task CanChangeToAsync_WithAsyncState_Should_Call_CanTransitionToAsync()
{
var state1 = new TestAsyncState();
var state2 = new TestStateV3();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestAsyncState>();
await _stateMachine.CanChangeToAsync<TestStateV3>();
Assert.That(state1.CanTransitionToCallCount, Is.EqualTo(1));
}
/// <summary>
/// 验证异步状态切换能够正确设置当前状态
/// </summary>
[Test]
public async Task ChangeToAsync_Should_SetCurrentState()
{
var state = new TestStateV2();
_stateMachine.Register(state);
await _stateMachine.ChangeToAsync<TestStateV2>();
Assert.That(_stateMachine.Current, Is.SameAs(state));
}
/// <summary>
/// 验证异步状态切换对于异步状态调用OnEnterAsync
/// </summary>
[Test]
public async Task ChangeToAsync_Should_Invoke_OnEnterAsync_ForAsyncState()
{
var state = new TestAsyncState();
_stateMachine.Register(state);
await _stateMachine.ChangeToAsync<TestAsyncState>();
Assert.That(state.EnterCalled, Is.True);
Assert.That(state.EnterFrom, Is.Null);
}
/// <summary>
/// 验证异步状态切换对于同步状态调用OnEnter
/// </summary>
[Test]
public async Task ChangeToAsync_Should_Invoke_OnEnter_ForSyncState()
{
var state = new TestStateV2();
_stateMachine.Register(state);
await _stateMachine.ChangeToAsync<TestStateV2>();
Assert.That(state.EnterCalled, Is.True);
Assert.That(state.EnterFrom, Is.Null);
}
/// <summary>
/// 验证异步状态切换当存在当前异步状态时调用OnExitAsync
/// </summary>
[Test]
public async Task ChangeToAsync_When_CurrentStateExists_WithAsyncState_Should_Invoke_OnExitAsync()
{
var state1 = new TestAsyncState();
var state2 = new TestStateV3();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestAsyncState>();
await _stateMachine.ChangeToAsync<TestStateV3>();
Assert.That(state1.ExitCalled, Is.True);
Assert.That(state1.ExitTo, Is.SameAs(state2));
}
/// <summary>
/// 验证异步状态切换当存在当前同步状态时调用OnExit
/// </summary>
[Test]
public async Task ChangeToAsync_When_CurrentStateExists_WithSyncState_Should_Invoke_OnExit()
{
var state1 = new TestStateV2();
var state2 = new TestStateV3();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestStateV2>();
await _stateMachine.ChangeToAsync<TestStateV3>();
Assert.That(state1.ExitCalled, Is.True);
Assert.That(state1.ExitTo, Is.SameAs(state2));
}
/// <summary>
/// 验证异步状态切换到相同状态时不应调用回调方法
/// </summary>
[Test]
public async Task ChangeToAsync_ToSameState_Should_NotInvoke_Callbacks()
{
var state = new TestStateV2();
_stateMachine.Register(state);
await _stateMachine.ChangeToAsync<TestStateV2>();
var enterCount = state.EnterCallCount;
var exitCount = state.ExitCallCount;
await _stateMachine.ChangeToAsync<TestStateV2>();
Assert.That(state.EnterCallCount, Is.EqualTo(enterCount));
Assert.That(state.ExitCallCount, Is.EqualTo(exitCount));
}
/// <summary>
/// 验证异步状态切换到未注册状态时应抛出InvalidOperationException
/// </summary>
[Test]
public void ChangeToAsync_ToUnregisteredState_Should_ThrowInvalidOperationException()
{
Assert.ThrowsAsync<InvalidOperationException>(async () => await _stateMachine.ChangeToAsync<TestStateV2>());
}
/// <summary>
/// 验证异步状态切换当当前状态拒绝转换时不应发生状态变化
/// </summary>
[Test]
public async Task ChangeToAsync_WhenCurrentStateDeniesTransition_Should_NotChange()
{
var state1 = new TestStateV2 { AllowTransition = false };
var state2 = new TestStateV3();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestStateV2>();
var oldState = _stateMachine.Current;
var result = await _stateMachine.ChangeToAsync<TestStateV3>();
Assert.That(result, Is.False);
Assert.That(_stateMachine.Current, Is.SameAs(oldState));
Assert.That(_stateMachine.Current, Is.SameAs(state1));
Assert.That(state2.EnterCalled, Is.False);
}
/// <summary>
/// 验证异步状态切换当当前异步状态拒绝转换时调用CanTransitionToAsync
/// </summary>
[Test]
public async Task ChangeToAsync_WhenCurrentStateDeniesTransition_WithAsyncState_Should_Call_CanTransitionToAsync()
{
var state1 = new TestAsyncState { AllowTransition = false };
var state2 = new TestStateV3();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestAsyncState>();
await _stateMachine.ChangeToAsync<TestStateV3>();
Assert.That(state1.CanTransitionToCallCount, Is.EqualTo(1));
}
/// <summary>
/// 验证异步回退能够返回到上一个状态
/// </summary>
[Test]
public async Task GoBackAsync_Should_ReturnTo_PreviousState()
{
var state1 = new TestStateV2();
var state2 = new TestStateV3();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestStateV2>();
await _stateMachine.ChangeToAsync<TestStateV3>();
var result = await _stateMachine.GoBackAsync();
Assert.That(result, Is.True);
Assert.That(_stateMachine.Current, Is.SameAs(state1));
}
/// <summary>
/// 验证异步回退当没有历史记录时返回false
/// </summary>
[Test]
public async Task GoBackAsync_WhenNoHistory_Should_ReturnFalse()
{
var result = await _stateMachine.GoBackAsync();
Assert.That(result, Is.False);
}
/// <summary>
/// 验证异步回退调用正确的状态转换方法
/// </summary>
[Test]
public async Task GoBackAsync_Should_Invoke_Correct_Transition_Methods()
{
var state1 = new TestAsyncState();
var state2 = new TestStateV3();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestAsyncState>();
await _stateMachine.ChangeToAsync<TestStateV3>();
await _stateMachine.GoBackAsync();
Assert.That(state2.ExitCalled, Is.True);
Assert.That(state1.EnterCallCount, Is.EqualTo(2));
}
/// <summary>
/// 验证从同步状态切换到异步状态能够正常工作
/// </summary>
[Test]
public async Task ChangeToAsync_FromSyncToAsyncState_Should_Work()
{
var state1 = new TestStateV2();
var state2 = new TestAsyncState();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestStateV2>();
await _stateMachine.ChangeToAsync<TestAsyncState>();
Assert.That(state1.ExitCalled, Is.True);
Assert.That(state2.EnterCalled, Is.True);
Assert.That(_stateMachine.Current, Is.SameAs(state2));
}
/// <summary>
/// 验证从异步状态切换到同步状态能够正常工作
/// </summary>
[Test]
public async Task ChangeToAsync_FromAsyncToSyncState_Should_Work()
{
var state1 = new TestAsyncState();
var state2 = new TestStateV2();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
await _stateMachine.ChangeToAsync<TestAsyncState>();
await _stateMachine.ChangeToAsync<TestStateV2>();
Assert.That(state1.ExitCalled, Is.True);
Assert.That(state2.EnterCalled, Is.True);
Assert.That(_stateMachine.Current, Is.SameAs(state2));
}
/// <summary>
/// 验证多次异步状态转换应正确调用回调方法
/// </summary>
[Test]
public async Task MultipleAsyncStateChanges_Should_Invoke_Callbacks_Correctly()
{
var state1 = new TestAsyncState();
var state2 = new TestStateV2();
var state3 = new TestStateV3();
_stateMachine.Register(state1);
_stateMachine.Register(state2);
_stateMachine.Register(state3);
await _stateMachine.ChangeToAsync<TestAsyncState>();
await _stateMachine.ChangeToAsync<TestStateV2>();
await _stateMachine.ChangeToAsync<TestStateV3>();
Assert.That(state1.EnterCalled, Is.True);
Assert.That(state1.ExitCalled, Is.True);
Assert.That(state2.EnterCalled, Is.True);
Assert.That(state2.ExitCalled, Is.True);
Assert.That(state3.EnterCalled, Is.True);
Assert.That(state3.ExitCalled, Is.False);
}
}
/// <summary>
/// 测试状态类V2版本实现IState接口用于测试
/// </summary>
public sealed class TestStateV2 : IState
{
public bool AllowTransition { get; set; } = true;
public bool EnterCalled { get; private set; }
public bool ExitCalled { get; private set; }
public int EnterCallCount { get; private set; }
public int ExitCallCount { get; private set; }
public IState? EnterFrom { get; private set; }
public IState? ExitTo { get; private set; }
/// <summary>
/// 进入状态时的回调方法
/// </summary>
/// <param name="from">从哪个状态进入</param>
public void OnEnter(IState? from)
{
EnterCalled = true;
EnterCallCount++;
EnterFrom = from;
}
/// <summary>
/// 离开状态时的回调方法
/// </summary>
/// <param name="to">离开到哪个状态</param>
public void OnExit(IState? to)
{
ExitCalled = true;
ExitCallCount++;
ExitTo = to;
}
/// <summary>
/// 判断是否可以转换到目标状态
/// </summary>
/// <param name="target">目标状态</param>
/// <returns>是否允许转换</returns>
public bool CanTransitionTo(IState target)
{
return AllowTransition;
}
}
/// <summary>
/// 测试状态类V3版本实现IState接口用于测试
/// </summary>
public sealed class TestStateV3 : IState
{
public bool EnterCalled { get; private set; }
public bool ExitCalled { get; private set; }
public int EnterCallCount { get; private set; }
public int ExitCallCount { get; private set; }
public IState? EnterFrom { get; private set; }
public IState? ExitTo { get; private set; }
/// <summary>
/// 进入状态时的回调方法
/// </summary>
/// <param name="from">从哪个状态进入</param>
public void OnEnter(IState? from)
{
EnterCalled = true;
EnterCallCount++;
EnterFrom = from;
}
/// <summary>
/// 离开状态时的回调方法
/// </summary>
/// <param name="to">离开到哪个状态</param>
public void OnExit(IState? to)
{
ExitCalled = true;
ExitCallCount++;
ExitTo = to;
}
/// <summary>
/// 判断是否可以转换到目标状态
/// </summary>
/// <param name="target">目标状态</param>
/// <returns>是否允许转换</returns>
public bool CanTransitionTo(IState target)
{
return true;
}
}
/// <summary>
/// 测试状态类V4版本实现IState接口用于测试
/// </summary>
public sealed class TestStateV4 : IState
{
public bool EnterCalled { get; private set; }
public bool ExitCalled { get; private set; }
public int EnterCallCount { get; private set; }
public int ExitCallCount { get; private set; }
public IState? EnterFrom { get; private set; }
public IState? ExitTo { get; private set; }
/// <summary>
/// 进入状态时的回调方法
/// </summary>
/// <param name="from">从哪个状态进入</param>
public void OnEnter(IState? from)
{
EnterCalled = true;
EnterCallCount++;
EnterFrom = from;
}
/// <summary>
/// 离开状态时的回调方法
/// </summary>
/// <param name="to">离开到哪个状态</param>
public void OnExit(IState? to)
{
ExitCalled = true;
ExitCallCount++;
ExitTo = to;
}
/// <summary>
/// 判断是否可以转换到目标状态
/// </summary>
/// <param name="target">目标状态</param>
/// <returns>是否允许转换</returns>
public bool CanTransitionTo(IState target)
{
return true;
}
}
/// <summary>
/// 异步测试状态类同时实现IState和IAsyncState接口用于测试异步状态功能
/// </summary>
public sealed class TestAsyncState : IState, IAsyncState
{
public bool AllowTransition { get; set; } = true;
public bool EnterCalled { get; private set; }
public bool ExitCalled { get; private set; }
public int EnterCallCount { get; private set; }
public int ExitCallCount { get; private set; }
public IState? EnterFrom { get; private set; }
public IState? ExitTo { get; private set; }
public int CanTransitionToCallCount { get; private set; }
/// <summary>
/// 异步进入状态时的回调方法
/// </summary>
/// <param name="from">从哪个状态进入</param>
public async Task OnEnterAsync(IState? from)
{
await Task.Delay(1);
EnterCalled = true;
EnterCallCount++;
EnterFrom = from;
}
/// <summary>
/// 异步离开状态时的回调方法
/// </summary>
/// <param name="to">离开到哪个状态</param>
public async Task OnExitAsync(IState? to)
{
await Task.Delay(1);
ExitCalled = true;
ExitCallCount++;
ExitTo = to;
}
/// <summary>
/// 异步判断是否可以转换到目标状态
/// </summary>
/// <param name="target">目标状态</param>
/// <returns>是否允许转换</returns>
public async Task<bool> CanTransitionToAsync(IState target)
{
await Task.Delay(1);
CanTransitionToCallCount++;
return AllowTransition;
}
/// <summary>
/// 进入状态时的回调方法(同步版本,抛出异常表示不应被调用)
/// </summary>
/// <param name="from">从哪个状态进入</param>
public void OnEnter(IState? from)
{
throw new InvalidOperationException("Sync OnEnter should not be called for async state");
}
/// <summary>
/// 离开状态时的回调方法(同步版本,抛出异常表示不应被调用)
/// </summary>
/// <param name="to">离开到哪个状态</param>
public void OnExit(IState? to)
{
throw new InvalidOperationException("Sync OnExit should not be called for async state");
}
/// <summary>
/// 判断是否可以转换到目标状态(同步版本,抛出异常表示不应被调用)
/// </summary>
/// <param name="target">目标状态</param>
/// <returns>是否允许转换</returns>
public bool CanTransitionTo(IState target)
{
throw new InvalidOperationException("Sync CanTransitionTo should not be called for async state");
}
}
/// <summary>
/// 状态机扩展方法类
/// </summary>
public static class StateMachineExtensions
{
/// <summary>
/// 检查状态机是否包含指定类型的状态
/// </summary>
/// <typeparam name="T">要检查的状态类型</typeparam>
/// <param name="stateMachine">状态机实例</param>
/// <returns>如果包含指定类型的状态则返回true否则返回false</returns>
public static bool ContainsState<T>(this StateMachine stateMachine) where T : IState
{
return stateMachine.GetType().GetField("States", BindingFlags.NonPublic | BindingFlags.Instance)?
.GetValue(stateMachine) is Dictionary<Type, IState> states &&
states.ContainsKey(typeof(T));
}
}