mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-23 11:14:30 +08:00
test(coroutine): 添加协程相关功能的单元测试
- 添加 CommandCoroutineExtensions 的完整测试用例 - 添加 QueryCoroutineExtensions 的完整测试用例 - 添加 WaitForEvent 的完整测试用例 - 添加 WaitForEventWithTimeout 的完整测试用例 - 添加 Moq 依赖包用于模拟测试 - 实现多种协程指令和扩展方法的功能验证 - 包含超时处理、异常处理和事件触发等场景测试 - [release ci]
This commit is contained in:
parent
9ac4ac0534
commit
ec8f72932b
@ -7,6 +7,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1"/>
|
||||
<PackageReference Include="Moq" Version="4.20.72"/>
|
||||
<PackageReference Include="NUnit" Version="4.4.0"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
@ -0,0 +1,429 @@
|
||||
using GFramework.Core.Abstractions.architecture;
|
||||
using GFramework.Core.Abstractions.command;
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.Abstractions.events;
|
||||
using GFramework.Core.Abstractions.rule;
|
||||
using GFramework.Core.coroutine.extensions;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// CommandCoroutineExtensions的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - SendCommandCoroutineWithErrorHandler扩展方法
|
||||
/// - SendCommandAndWaitEventCoroutine扩展方法
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class CommandCoroutineExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试用的简单命令类
|
||||
/// </summary>
|
||||
private class TestCommand : IAsyncCommand
|
||||
{
|
||||
private IArchitectureContext? _context;
|
||||
public void SetContext(IArchitectureContext context) => _context = context;
|
||||
public IArchitectureContext GetContext() => _context!;
|
||||
|
||||
public Task ExecuteAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的简单事件类
|
||||
/// </summary>
|
||||
private class TestEvent
|
||||
{
|
||||
public string Data { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上下文感知基类的模拟实现
|
||||
/// </summary>
|
||||
private class TestContextAware : IContextAware
|
||||
{
|
||||
public Mock<IArchitectureContext> _mockContext = new();
|
||||
|
||||
public IArchitectureContext GetContext()
|
||||
{
|
||||
return _mockContext.Object;
|
||||
}
|
||||
|
||||
public void SetContext(IArchitectureContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandCoroutineWithErrorHandler应该能正常执行成功的命令
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SendCommandCoroutineWithErrorHandler_Should_Execute_Successful_Command()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
Exception? capturedException = null;
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送命令的模拟行为
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command, ex => capturedException = ex);
|
||||
|
||||
// 迭代协程直到完成
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
if (coroutine.Current is WaitForTask)
|
||||
{
|
||||
// 等待任务完成
|
||||
await Task.Delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.That(capturedException, Is.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandCoroutineWithErrorHandler应该捕获命令执行中的异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SendCommandCoroutineWithErrorHandler_Should_Capture_Command_Exception()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
Exception? capturedException = null;
|
||||
var contextAware = new TestContextAware();
|
||||
var expectedException = new InvalidOperationException("Test exception");
|
||||
|
||||
// 设置上下文发送命令的模拟行为,返回失败的任务
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.FromException(expectedException));
|
||||
|
||||
var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command, ex => capturedException = ex);
|
||||
|
||||
// 迭代协程直到完成
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
if (coroutine.Current is WaitForTask)
|
||||
{
|
||||
// 等待任务完成
|
||||
await Task.Delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.That(capturedException, Is.Not.Null);
|
||||
// 异常被包装为 AggregateException
|
||||
Assert.That(capturedException, Is.TypeOf<AggregateException>());
|
||||
var aggregateException = (AggregateException)capturedException!;
|
||||
Assert.That(aggregateException.InnerException, Is.EqualTo(expectedException));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandCoroutineWithErrorHandler应该处理null错误回调
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SendCommandCoroutineWithErrorHandler_Should_Handle_Null_Error_Handler()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
var contextAware = new TestContextAware();
|
||||
var expectedException = new InvalidOperationException("Test exception");
|
||||
|
||||
// 设置上下文发送命令的模拟行为,返回失败的任务
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.FromException(expectedException));
|
||||
|
||||
// 使用null作为错误处理程序
|
||||
var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command, null);
|
||||
|
||||
// 迭代协程直到完成
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
if (coroutine.Current is WaitForTask)
|
||||
{
|
||||
// 等待任务完成
|
||||
await Task.Delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
// 应该不会抛出异常
|
||||
Assert.Pass();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandAndWaitEventCoroutine应该在事件触发后完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SendCommandAndWaitEventCoroutine_Should_Complete_After_Event_Triggered()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
var contextAware = new TestContextAware();
|
||||
TestEvent? receivedEvent = null;
|
||||
var eventTriggered = false;
|
||||
|
||||
// 创建事件总线模拟
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
Action<TestEvent>? eventCallback = null;
|
||||
eventBusMock.Setup(bus => bus.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(callback => eventCallback = callback);
|
||||
|
||||
// 设置上下文服务以返回事件总线
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.GetService<IEventBus>())
|
||||
.Returns(eventBusMock.Object);
|
||||
|
||||
// 设置命令执行返回完成的任务
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var coroutine = contextAware.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
|
||||
command,
|
||||
ev =>
|
||||
{
|
||||
receivedEvent = ev;
|
||||
eventTriggered = true;
|
||||
});
|
||||
|
||||
// 启动协程并等待命令执行完成
|
||||
coroutine.MoveNext(); // 进入命令发送阶段
|
||||
if (coroutine.Current is WaitForTask)
|
||||
{
|
||||
await Task.Delay(10); // 等待命令任务完成
|
||||
}
|
||||
|
||||
// 此时协程应该在等待事件
|
||||
Assert.That(coroutine.MoveNext(), Is.True); // 等待事件阶段
|
||||
|
||||
// 触发事件
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
eventCallback?.Invoke(testEvent);
|
||||
|
||||
// 现在协程应该完成
|
||||
Assert.That(coroutine.MoveNext(), Is.False);
|
||||
|
||||
Assert.That(eventTriggered, Is.True);
|
||||
Assert.That(receivedEvent, Is.Not.Null);
|
||||
Assert.That(receivedEvent?.Data, Is.EqualTo("TestData"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandAndWaitEventCoroutine应该处理超时情况
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendCommandAndWaitEventCoroutine_Should_Throw_Timeout_Exception()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 创建事件总线模拟
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(bus => bus.Register(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
// 设置上下文服务以返回事件总线
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.GetService<IEventBus>())
|
||||
.Returns(eventBusMock.Object);
|
||||
|
||||
// 设置命令执行返回完成的任务
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
// 对于超时情况,我们期望抛出TimeoutException
|
||||
Assert.Throws<TimeoutException>(() =>
|
||||
{
|
||||
var coroutine = contextAware.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
|
||||
command,
|
||||
null,
|
||||
0.1f); // 0.1秒超时
|
||||
|
||||
// 运行协程直到完成
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
if (coroutine.Current is WaitForEventWithTimeout<TestEvent> timeoutWait)
|
||||
{
|
||||
// 模拟超时
|
||||
timeoutWait.Update(0.2); // 更新时间超过超时限制
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandAndWaitEventCoroutine应该处理null事件回调
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SendCommandAndWaitEventCoroutine_Should_Handle_Null_Event_Callback()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 创建事件总线模拟
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
Action<TestEvent>? eventCallback = null;
|
||||
eventBusMock.Setup(bus => bus.Register(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(callback => eventCallback = callback);
|
||||
|
||||
// 设置上下文服务以返回事件总线
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.GetService<IEventBus>())
|
||||
.Returns(eventBusMock.Object);
|
||||
|
||||
// 设置命令执行返回完成的任务
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
// 使用null作为事件回调
|
||||
var coroutine = contextAware.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
|
||||
command,
|
||||
null); // null回调
|
||||
|
||||
// 启动协程
|
||||
coroutine.MoveNext(); // 进入命令发送阶段
|
||||
if (coroutine.Current is WaitForTask)
|
||||
{
|
||||
await Task.Delay(10); // 等待命令任务完成
|
||||
}
|
||||
|
||||
// 触发事件
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
eventCallback?.Invoke(testEvent);
|
||||
|
||||
// 协程应该能正常完成
|
||||
Assert.That(() => coroutine.MoveNext(), Throws.Nothing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandAndWaitEventCoroutine应该处理命令执行异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SendCommandAndWaitEventCoroutine_Should_Handle_Command_Exception()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
var contextAware = new TestContextAware();
|
||||
var expectedException = new InvalidOperationException("Command execution failed");
|
||||
|
||||
// 创建事件总线模拟
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(bus => bus.Register(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
// 设置上下文服务以返回事件总线
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.GetService<IEventBus>())
|
||||
.Returns(eventBusMock.Object);
|
||||
|
||||
// 设置命令执行返回失败的任务
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.FromException(expectedException));
|
||||
|
||||
var coroutine = contextAware.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
|
||||
command,
|
||||
_ => { });
|
||||
|
||||
// 启动协程 - 命令失败时协程仍然继续
|
||||
coroutine.MoveNext(); // 进入命令发送阶段
|
||||
if (coroutine.Current is WaitForTask)
|
||||
{
|
||||
await Task.Delay(10); // 等待命令任务完成
|
||||
}
|
||||
|
||||
// 命令执行失败后,协程继续执行
|
||||
Assert.Pass();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandCoroutineWithErrorHandler应该返回IEnumerator<IYieldInstruction>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendCommandCoroutineWithErrorHandler_Should_Return_IEnumerator_Of_YieldInstruction()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送命令的模拟行为
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var coroutine = contextAware.SendCommandCoroutineWithErrorHandler(command, ex => { });
|
||||
|
||||
Assert.That(coroutine, Is.InstanceOf<IEnumerator<IYieldInstruction>>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandAndWaitEventCoroutine应该返回IEnumerator<IYieldInstruction>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendCommandAndWaitEventCoroutine_Should_Return_IEnumerator_Of_YieldInstruction()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 创建事件总线模拟
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(bus => bus.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
// 设置上下文服务以返回事件总线
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.GetService<IEventBus>())
|
||||
.Returns(eventBusMock.Object);
|
||||
|
||||
// 设置命令执行返回完成的任务
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendCommandAsync(It.IsAny<IAsyncCommand>()))
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var coroutine = contextAware.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
|
||||
command,
|
||||
ev => { });
|
||||
|
||||
Assert.That(coroutine, Is.InstanceOf<IEnumerator<IYieldInstruction>>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendCommandAndWaitEventCoroutine应该在事件总线为null时处理异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendCommandAndWaitEventCoroutine_Should_Handle_Null_EventBus()
|
||||
{
|
||||
var command = new TestCommand();
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文服务以返回null事件总线
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.GetService<IEventBus>())
|
||||
.Returns((IEventBus?)null);
|
||||
|
||||
// 创建协程
|
||||
var coroutine = contextAware.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
|
||||
command,
|
||||
ev => { });
|
||||
|
||||
// 调用 MoveNext 时应该抛出异常,因为事件总线为null
|
||||
Assert.Throws<ArgumentNullException>(() => coroutine.MoveNext());
|
||||
}
|
||||
}
|
||||
356
GFramework.Core.Tests/coroutine/QueryCoroutineExtensionsTests.cs
Normal file
356
GFramework.Core.Tests/coroutine/QueryCoroutineExtensionsTests.cs
Normal file
@ -0,0 +1,356 @@
|
||||
using GFramework.Core.Abstractions.architecture;
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.Abstractions.query;
|
||||
using GFramework.Core.Abstractions.rule;
|
||||
using GFramework.Core.coroutine.extensions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// QueryCoroutineExtensions的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - SendQueryCoroutine扩展方法
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class QueryCoroutineExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试用的简单查询类
|
||||
/// </summary>
|
||||
private class TestQuery : IQuery<string>
|
||||
{
|
||||
private IArchitectureContext? _context;
|
||||
public string QueryData { get; set; } = string.Empty;
|
||||
public void SetContext(IArchitectureContext context) => _context = context;
|
||||
public IArchitectureContext GetContext() => _context!;
|
||||
public string Do() => QueryData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上下文感知基类的模拟实现
|
||||
/// </summary>
|
||||
private class TestContextAware : IContextAware
|
||||
{
|
||||
public Mock<IArchitectureContext> _mockContext = new Mock<IArchitectureContext>();
|
||||
|
||||
public IArchitectureContext GetContext()
|
||||
{
|
||||
return _mockContext.Object;
|
||||
}
|
||||
|
||||
public void SetContext(IArchitectureContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该能正常执行查询并返回结果
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Execute_Query_And_Return_Result()
|
||||
{
|
||||
var query = new TestQuery { QueryData = "TestQueryData" };
|
||||
string? receivedResult = null;
|
||||
var resultReceived = false;
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送查询的模拟行为
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>()))
|
||||
.Returns("TestResult");
|
||||
|
||||
var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result =>
|
||||
{
|
||||
receivedResult = result;
|
||||
resultReceived = true;
|
||||
});
|
||||
|
||||
// 迭代协程直到完成
|
||||
var moved = coroutine.MoveNext();
|
||||
|
||||
// SendQueryCoroutine立即执行并返回,所以MoveNext应该返回false
|
||||
Assert.That(moved, Is.False);
|
||||
Assert.That(resultReceived, Is.True);
|
||||
Assert.That(receivedResult, Is.EqualTo("TestResult"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该处理不同类型的查询和结果
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Handle_Different_Query_And_Result_Types()
|
||||
{
|
||||
// 使用整数查询和布尔结果
|
||||
var query = new IntQuery { Value = 42 };
|
||||
bool? receivedResult = null;
|
||||
var resultReceived = false;
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送查询的模拟行为
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<bool>(It.IsAny<IntQuery>()))
|
||||
.Returns(true);
|
||||
|
||||
var coroutine = contextAware.SendQueryCoroutine<IntQuery, bool>(query, result =>
|
||||
{
|
||||
receivedResult = result;
|
||||
resultReceived = true;
|
||||
});
|
||||
|
||||
// 迭代协程直到完成
|
||||
var moved = coroutine.MoveNext();
|
||||
|
||||
Assert.That(moved, Is.False);
|
||||
Assert.That(resultReceived, Is.True);
|
||||
Assert.That(receivedResult, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine在null回调时应该抛出异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Throw_When_Null_Result_Callback()
|
||||
{
|
||||
var query = new TestQuery { QueryData = "TestQueryData" };
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送查询的模拟行为
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>()))
|
||||
.Returns("TestResult");
|
||||
|
||||
// 使用null作为结果回调,应该抛出NullReferenceException
|
||||
var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, null!);
|
||||
|
||||
// 迭代协程时应该抛出异常
|
||||
Assert.That(() => coroutine.MoveNext(), Throws.TypeOf<NullReferenceException>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该在查询执行期间调用结果处理回调
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Call_Result_Callback_During_Execution()
|
||||
{
|
||||
var query = new TestQuery { QueryData = "TestQueryData" };
|
||||
string? receivedResult = null;
|
||||
var callCount = 0;
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送查询的模拟行为
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>()))
|
||||
.Returns("ProcessedResult");
|
||||
|
||||
var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result =>
|
||||
{
|
||||
receivedResult = result;
|
||||
callCount++;
|
||||
});
|
||||
|
||||
// 协程应立即执行查询并调用回调
|
||||
coroutine.MoveNext();
|
||||
|
||||
Assert.That(callCount, Is.EqualTo(1));
|
||||
Assert.That(receivedResult, Is.EqualTo("ProcessedResult"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该返回IEnumerator<IYieldInstruction>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Return_IEnumerator_Of_YieldInstruction()
|
||||
{
|
||||
var query = new TestQuery { QueryData = "TestQueryData" };
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送查询的模拟行为
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>()))
|
||||
.Returns("TestResult");
|
||||
|
||||
var coroutine = contextAware.SendQueryCoroutine<TestQuery, string>(query, result => { });
|
||||
|
||||
Assert.That(coroutine, Is.InstanceOf<IEnumerator<IYieldInstruction>>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该在查询抛出异常时处理异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Handle_Query_Exception()
|
||||
{
|
||||
var query = new TestQuery { QueryData = "TestQueryData" };
|
||||
string? receivedResult = null;
|
||||
var contextAware = new TestContextAware();
|
||||
var expectedException = new InvalidOperationException("Query execution failed");
|
||||
|
||||
// 设置上下文发送查询的模拟行为,让它抛出异常
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>()))
|
||||
.Throws(expectedException);
|
||||
|
||||
// 由于SendQueryCoroutine会直接执行查询,这可能导致异常
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
var coroutine =
|
||||
contextAware.SendQueryCoroutine<TestQuery, string>(query, result => { receivedResult = result; });
|
||||
|
||||
// 尝试移动协程,这应该会执行查询并抛出异常
|
||||
coroutine.MoveNext();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该正确传递查询参数
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Pass_Query_Parameters_Correctly()
|
||||
{
|
||||
var query = new TestQuery { QueryData = "PassedQueryData" };
|
||||
string? receivedResult = null;
|
||||
var contextAware = new TestContextAware();
|
||||
TestQuery? capturedQuery = null;
|
||||
|
||||
// 设置上下文发送查询的模拟行为,并捕获传入的查询参数
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>()))
|
||||
.Returns((IQuery<string> q) =>
|
||||
{
|
||||
capturedQuery = (TestQuery)q;
|
||||
return $"Processed_{capturedQuery.QueryData}";
|
||||
});
|
||||
|
||||
var coroutine =
|
||||
contextAware.SendQueryCoroutine<TestQuery, string>(query, result => { receivedResult = result; });
|
||||
|
||||
coroutine.MoveNext();
|
||||
|
||||
Assert.That(capturedQuery, Is.Not.Null);
|
||||
Assert.That(capturedQuery!.QueryData, Is.EqualTo("PassedQueryData"));
|
||||
Assert.That(receivedResult, Is.EqualTo("Processed_PassedQueryData"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该处理复杂对象查询
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Handle_Complex_Object_Query()
|
||||
{
|
||||
var query = new ComplexQuery
|
||||
{
|
||||
Name = "ComplexName",
|
||||
Values = new List<int> { 1, 2, 3 },
|
||||
Metadata = new Dictionary<string, object> { { "key", "value" } }
|
||||
};
|
||||
|
||||
ComplexResult? receivedResult = null;
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
var expectedResult = new ComplexResult
|
||||
{
|
||||
ProcessedName = "Processed_ComplexName",
|
||||
Sum = 6,
|
||||
Count = 3
|
||||
};
|
||||
|
||||
// 设置上下文发送查询的模拟行为
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<ComplexResult>(It.IsAny<ComplexQuery>()))
|
||||
.Returns(expectedResult);
|
||||
|
||||
var coroutine =
|
||||
contextAware.SendQueryCoroutine<ComplexQuery, ComplexResult>(query, result => { receivedResult = result; });
|
||||
|
||||
coroutine.MoveNext();
|
||||
|
||||
Assert.That(receivedResult, Is.Not.Null);
|
||||
Assert.That(receivedResult!.ProcessedName, Is.EqualTo("Processed_ComplexName"));
|
||||
Assert.That(receivedResult.Sum, Is.EqualTo(6));
|
||||
Assert.That(receivedResult.Count, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该处理空字符串结果
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Handle_Empty_String_Result()
|
||||
{
|
||||
var query = new TestQuery { QueryData = "TestQueryData" };
|
||||
string? receivedResult = null;
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送查询的模拟行为,返回空字符串
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>()))
|
||||
.Returns(string.Empty);
|
||||
|
||||
var coroutine =
|
||||
contextAware.SendQueryCoroutine<TestQuery, string>(query, result => { receivedResult = result; });
|
||||
|
||||
coroutine.MoveNext();
|
||||
|
||||
Assert.That(receivedResult, Is.EqualTo(string.Empty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SendQueryCoroutine应该处理null结果
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendQueryCoroutine_Should_Handle_Null_Result()
|
||||
{
|
||||
var query = new TestQuery { QueryData = "TestQueryData" };
|
||||
string? receivedResult = "initial";
|
||||
var contextAware = new TestContextAware();
|
||||
|
||||
// 设置上下文发送查询的模拟行为,返回null
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.SendQuery<string>(It.IsAny<TestQuery>()))
|
||||
.Returns((string)null!);
|
||||
|
||||
var coroutine =
|
||||
contextAware.SendQueryCoroutine<TestQuery, string>(query, result => { receivedResult = result; });
|
||||
|
||||
coroutine.MoveNext();
|
||||
|
||||
Assert.That(receivedResult, Is.Null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于测试的整数查询类
|
||||
/// </summary>
|
||||
internal class IntQuery : IQuery<bool>
|
||||
{
|
||||
private IArchitectureContext? _context;
|
||||
public int Value { get; set; }
|
||||
public void SetContext(IArchitectureContext context) => _context = context;
|
||||
public IArchitectureContext GetContext() => _context!;
|
||||
public bool Do() => Value > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于测试的复杂查询类
|
||||
/// </summary>
|
||||
internal class ComplexQuery : IQuery<ComplexResult>
|
||||
{
|
||||
private IArchitectureContext? _context;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public List<int> Values { get; set; } = new List<int>();
|
||||
public Dictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
|
||||
public void SetContext(IArchitectureContext context) => _context = context;
|
||||
public IArchitectureContext GetContext() => _context!;
|
||||
public ComplexResult Do() => new ComplexResult { ProcessedName = Name, Sum = Values.Sum(), Count = Values.Count };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于测试的复杂结果类
|
||||
/// </summary>
|
||||
internal class ComplexResult
|
||||
{
|
||||
public string ProcessedName { get; set; } = string.Empty;
|
||||
public int Sum { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
270
GFramework.Core.Tests/coroutine/WaitForEventTests.cs
Normal file
270
GFramework.Core.Tests/coroutine/WaitForEventTests.cs
Normal file
@ -0,0 +1,270 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.Abstractions.events;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// WaitForEvent的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - 初始化和基本功能
|
||||
/// - 事件触发处理
|
||||
/// - 资源释放
|
||||
/// - 异常处理
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class WaitForEventTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试用的简单事件类
|
||||
/// </summary>
|
||||
private class TestEvent
|
||||
{
|
||||
public string Data { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent初始状态为未完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Not_Be_Done_Initially()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent应该在事件触发后完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Be_Done_After_Event_Triggered()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
// 触发事件
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent应该保存事件数据
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Save_Event_Data()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.EventData, Is.Not.Null);
|
||||
Assert.That(wait.EventData?.Data, Is.EqualTo("TestData"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent应该在事件触发后保持完成状态
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Remain_Done_After_Event_Triggered()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
|
||||
// 再次触发事件,确认状态不变
|
||||
registeredAction?.Invoke(new TestEvent { Data = "AnotherData" });
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent应该在Dispose后释放资源
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Release_Resources_On_Dispose()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
wait.Dispose();
|
||||
|
||||
unRegisterMock.Verify(x => x.UnRegister(), Times.Once);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent应该处理多次Dispose调用
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Handle_Multiple_Dispose_Calls()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
wait.Dispose();
|
||||
// 第二次调用不应引发异常
|
||||
Assert.DoesNotThrow(() => wait.Dispose());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent应该抛出ArgumentNullException当eventBus为null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Throw_ArgumentNullException_When_EventBus_Is_Null()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new WaitForEvent<TestEvent>(null!));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent的Update方法不影响状态
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Update_Should_Not_Affect_State()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
wait.Update(0.1);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
wait.Update(0.1);
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent实现IYieldInstruction接口
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Implement_IYieldInstruction()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEvent在事件触发后自动注销监听器
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_Should_Unregister_After_Event_Triggered()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
// 事件触发后,Update方法应该注销监听器
|
||||
wait.Update(0.1);
|
||||
|
||||
unRegisterMock.Verify(x => x.UnRegister(), Times.AtLeastOnce);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventEventData为null当没有事件触发
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_EventData_Should_Be_Null_When_No_Event_Triggered()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
Assert.That(wait.EventData, Is.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventEventData在事件触发后不为null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEvent_EventData_Should_Not_Be_Null_After_Event_Triggered()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var wait = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.EventData, Is.Not.Null);
|
||||
}
|
||||
}
|
||||
328
GFramework.Core.Tests/coroutine/WaitForEventWithTimeoutTests.cs
Normal file
328
GFramework.Core.Tests/coroutine/WaitForEventWithTimeoutTests.cs
Normal file
@ -0,0 +1,328 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.Abstractions.events;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// WaitForEventWithTimeout的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - 初始化和基本功能
|
||||
/// - 超时处理
|
||||
/// - 事件提前触发
|
||||
/// - 异常处理
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class WaitForEventWithTimeoutTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试用的简单事件类
|
||||
/// </summary>
|
||||
private class TestEvent
|
||||
{
|
||||
public string Data { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout初始状态为未完成且未超时
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Not_Be_Done_Initially()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 2.0f);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该在超时时完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Be_Done_When_Timeout()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 1.0f);
|
||||
|
||||
// 更新时间超过超时时间
|
||||
wait.Update(1.5);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.IsTimeout, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该在事件触发时完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Be_Done_When_Event_Triggered()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 2.0f);
|
||||
|
||||
// 触发事件
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该在事件触发后保存事件数据
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Save_Event_Data()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 2.0f);
|
||||
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.EventData, Is.Not.Null);
|
||||
Assert.That(wait.EventData?.Data, Is.EqualTo("TestData"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该在超时后返回null事件数据
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Return_Null_EventData_When_Timeout()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 1.0f);
|
||||
|
||||
wait.Update(1.5);
|
||||
|
||||
Assert.That(wait.EventData, Is.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该在事件触发前正确计算超时
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Calculate_Timeout_Correctly()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 2.0f);
|
||||
|
||||
wait.Update(1.0);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
wait.Update(0.5);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
wait.Update(0.6); // 总共2.1秒,超过2.0秒超时时间
|
||||
Assert.That(wait.IsTimeout, Is.True);
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该在事件触发后忽略后续超时
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Ignore_Timeout_After_Event_Triggered()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 1.0f);
|
||||
|
||||
// 触发事件
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
|
||||
// 即使时间超过了超时限制,也不应标记为超时
|
||||
wait.Update(2.0);
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该抛出ArgumentNullException当waitForEvent为null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Throw_ArgumentNullException_When_WaitForEvent_Is_Null()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new WaitForEventWithTimeout<TestEvent>(null!, 1.0f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该正确处理Update方法
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Update_Should_Work_Correctly()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 2.0f);
|
||||
|
||||
// 更新时间但未超过超时时间
|
||||
wait.Update(1.0);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
|
||||
// 触发事件
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout实现IYieldInstruction接口
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Implement_IYieldInstruction()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 2.0f);
|
||||
|
||||
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该处理小超时时间
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Handle_Small_Timeout()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 0.1f);
|
||||
|
||||
wait.Update(0.2);
|
||||
|
||||
Assert.That(wait.IsTimeout, Is.True);
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该处理大超时时间
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Handle_Large_Timeout()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 10.0f);
|
||||
|
||||
wait.Update(5.0);
|
||||
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForEventWithTimeout应该在事件触发后忽略后续超时并保持状态
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForEventWithTimeout_Should_Ignore_Timeout_And_Maintain_State_After_Event_Triggered()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
var waitForEvent = new WaitForEvent<TestEvent>(eventBusMock.Object);
|
||||
var wait = new WaitForEventWithTimeout<TestEvent>(waitForEvent, 1.0f);
|
||||
|
||||
// 触发事件
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
Assert.That(wait.EventData, Is.Not.Null);
|
||||
Assert.That(wait.EventData?.Data, Is.EqualTo("TestData"));
|
||||
|
||||
// 即使时间超过了超时限制,也不应标记为超时,状态应保持不变
|
||||
wait.Update(2.0);
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
Assert.That(wait.EventData, Is.Not.Null);
|
||||
Assert.That(wait.EventData?.Data, Is.EqualTo("TestData"));
|
||||
|
||||
// 再次更新,状态仍应保持不变
|
||||
wait.Update(1.0);
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.IsTimeout, Is.False);
|
||||
Assert.That(wait.EventData, Is.Not.Null);
|
||||
Assert.That(wait.EventData?.Data, Is.EqualTo("TestData"));
|
||||
}
|
||||
}
|
||||
321
GFramework.Core.Tests/events/EventListenerScopeTests.cs
Normal file
321
GFramework.Core.Tests/events/EventListenerScopeTests.cs
Normal file
@ -0,0 +1,321 @@
|
||||
using GFramework.Core.Abstractions.events;
|
||||
using GFramework.Core.events;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.events;
|
||||
|
||||
/// <summary>
|
||||
/// EventListenerScope的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - 初始化和基本功能
|
||||
/// - 事件触发处理
|
||||
/// - 资源释放
|
||||
/// - 多次触发事件
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class EventListenerScopeTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试用的简单事件类
|
||||
/// </summary>
|
||||
private class TestEvent
|
||||
{
|
||||
public string Data { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope初始状态为未触发
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Not_Be_Triggered_Initially()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope初始EventData为null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_EventData_Should_Be_Null_Initially()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
Assert.That(scope.EventData, Is.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope应该在事件触发后标记为已触发
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Be_Triggered_After_Event_Fired()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.False);
|
||||
|
||||
// 触发事件
|
||||
var testEvent = new TestEvent { Data = "TestData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope应该保存事件数据
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Save_Event_Data()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
var testEvent = new TestEvent { Data = "SavedData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(scope.EventData, Is.Not.Null);
|
||||
Assert.That(scope.EventData?.Data, Is.EqualTo("SavedData"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope在Dispose后应该取消注册
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_UnRegister_On_Dispose()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
scope.Dispose();
|
||||
|
||||
unRegisterMock.Verify(x => x.UnRegister(), Times.Once);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope应该在using块结束后自动取消注册
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_UnRegister_When_Using_Block_Ends()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
ExecuteUsingBlockTest(eventBusMock);
|
||||
|
||||
// 验证using块结束后调用了UnRegister
|
||||
unRegisterMock.Verify(x => x.UnRegister(), Times.Once);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope应该在事件触发后保持已触发状态
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Remain_Triggered_After_Event_Fired()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
var testEvent = new TestEvent { Data = "FirstData" };
|
||||
registeredAction?.Invoke(testEvent);
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.True);
|
||||
|
||||
// 再次触发事件,状态应保持已触发
|
||||
registeredAction?.Invoke(new TestEvent { Data = "SecondData" });
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope在多次触发时应该保存最后一次的事件数据
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Save_Last_Event_Data_On_Multiple_Triggers()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
// 第一次触发
|
||||
registeredAction?.Invoke(new TestEvent { Data = "FirstData" });
|
||||
Assert.That(scope.EventData?.Data, Is.EqualTo("FirstData"));
|
||||
|
||||
// 第二次触发,应该覆盖数据
|
||||
registeredAction?.Invoke(new TestEvent { Data = "SecondData" });
|
||||
Assert.That(scope.EventData?.Data, Is.EqualTo("SecondData"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope应该在初始化时注册事件监听器
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Register_Event_Listener_On_Init()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
eventBusMock.Verify(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()), Times.Once);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope应该处理值类型事件
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Handle_Value_Type_Event()
|
||||
{
|
||||
Action<int>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<int>(It.IsAny<Action<int>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<int>>(action => registeredAction = action);
|
||||
|
||||
using var scope = new EventListenerScope<int>(eventBusMock.Object);
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.False);
|
||||
Assert.That(scope.EventData, Is.EqualTo(default(int)));
|
||||
|
||||
registeredAction?.Invoke(42);
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.True);
|
||||
Assert.That(scope.EventData, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope应该处理结构体事件
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Handle_Struct_Event()
|
||||
{
|
||||
Action<StructEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<StructEvent>(It.IsAny<Action<StructEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<StructEvent>>(action => registeredAction = action);
|
||||
|
||||
using var scope = new EventListenerScope<StructEvent>(eventBusMock.Object);
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.False);
|
||||
|
||||
var structEvent = new StructEvent { Id = 123, Value = 456.78f };
|
||||
registeredAction?.Invoke(structEvent);
|
||||
|
||||
Assert.That(scope.IsTriggered, Is.True);
|
||||
Assert.That(scope.EventData.Id, Is.EqualTo(123));
|
||||
Assert.That(scope.EventData.Value, Is.EqualTo(456.78f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope应该是线程安全的(IsTriggered使用volatile)
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task EventListenerScope_IsTriggered_Should_Be_Thread_Safe()
|
||||
{
|
||||
Action<TestEvent>? registeredAction = null;
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object)
|
||||
.Callback<Action<TestEvent>>(action => registeredAction = action);
|
||||
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
// 在另一个线程中触发事件
|
||||
await Task.Run(() => { registeredAction?.Invoke(new TestEvent { Data = "ThreadData" }); });
|
||||
|
||||
// 主线程应该能看到更新后的值
|
||||
Assert.That(scope.IsTriggered, Is.True);
|
||||
Assert.That(scope.EventData?.Data, Is.EqualTo("ThreadData"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证EventListenerScope可以多次Dispose而不抛出异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EventListenerScope_Should_Handle_Multiple_Dispose_Calls()
|
||||
{
|
||||
var eventBusMock = new Mock<IEventBus>();
|
||||
var unRegisterMock = new Mock<IUnRegister>();
|
||||
eventBusMock.Setup(x => x.Register<TestEvent>(It.IsAny<Action<TestEvent>>()))
|
||||
.Returns(unRegisterMock.Object);
|
||||
|
||||
var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
|
||||
// 多次调用Dispose不应抛出异常
|
||||
Assert.DoesNotThrow(() => scope.Dispose());
|
||||
Assert.DoesNotThrow(() => scope.Dispose());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的结构体事件
|
||||
/// </summary>
|
||||
private struct StructEvent
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public float Value { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 辅助方法:执行using块测试
|
||||
/// </summary>
|
||||
/// <param name="eventBusMock">事件总线模拟对象</param>
|
||||
private static void ExecuteUsingBlockTest(Mock<IEventBus> eventBusMock)
|
||||
{
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
// 作用域内部不验证
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user