test(coroutine): 添加协程相关功能的单元测试

- 添加 CommandCoroutineExtensions 的完整测试用例
- 添加 QueryCoroutineExtensions 的完整测试用例
- 添加 WaitForEvent 的完整测试用例
- 添加 WaitForEventWithTimeout 的完整测试用例
- 添加 Moq 依赖包用于模拟测试
- 实现多种协程指令和扩展方法的功能验证
- 包含超时处理、异常处理和事件触发等场景测试
- [release ci]
This commit is contained in:
GeWuYou 2026-02-01 11:04:50 +08:00
parent 9ac4ac0534
commit ec8f72932b
6 changed files with 1705 additions and 0 deletions

View File

@ -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>

View File

@ -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());
}
}

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

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

View 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"));
}
}

View 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);
// 作用域内部不验证
}
}