GeWuYou f984f4a600 refactor(core): 优化核心组件的线程安全性和错误处理
- 重构 AsyncLogAppender 的 Flush 方法,添加超时控制和 SpinWait 优化
- 为 BindableProperty 添加线程安全锁保护,确保并发访问的安全性
- 在 BindableProperty 中实现回调外部调用以避免死锁问题
- 为 EasyEvents 使用 ConcurrentDictionary 替代 Dictionary 提高并发性能
- 添加协程调度器异常处理回调机制,防止异常传播导致调度器崩溃
- 为 FileAppender 添加初始化异常处理和资源清理逻辑
- 补充完整的单元测试覆盖并发场景下的线程安全性验证
2026-03-04 11:04:59 +08:00

170 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

using GFramework.Core.events;
using NUnit.Framework;
namespace GFramework.Core.Tests.events;
/// <summary>
/// EasyEvents功能测试类用于验证事件系统的注册、触发和参数传递功能
/// </summary>
[TestFixture]
public class EasyEventsTests
{
/// <summary>
/// 测试用例初始化方法在每个测试方法执行前设置EasyEvents实例
/// </summary>
[SetUp]
public void SetUp()
{
_easyEvents = new EasyEvents();
}
private EasyEvents _easyEvents = null!;
/// <summary>
/// 测试单参数事件的功能,验证事件能够正确接收并传递int类型参数
/// </summary>
[Test]
public void Get_EventT_Should_Trigger_With_Parameter()
{
var receivedValue = 0;
var @event = EasyEvents.GetOrAdd<Event<int>>();
@event.Register(value => { receivedValue = value; });
// 触发事件并传递参数42
@event.Trigger(42);
Assert.That(receivedValue, Is.EqualTo(42));
}
/// <summary>
/// 测试双参数事件的功能,验证事件能够正确接收并传递int和string类型的参数
/// </summary>
[Test]
public void Get_EventTTK_Should_Trigger_With_Two_Parameters()
{
var receivedInt = 0;
var receivedString = string.Empty;
var @event = EasyEvents.GetOrAdd<Event<int, string>>();
@event.Register((i, s) =>
{
receivedInt = i;
receivedString = s;
});
// 触发事件并传递两个参数整数100和字符串"hello"
@event.Trigger(100, "hello");
Assert.That(receivedInt, Is.EqualTo(100));
Assert.That(receivedString, Is.EqualTo("hello"));
}
/// <summary>
/// 测试并发场景下GetOrAdd的线程安全性
/// </summary>
[Test]
public void GetOrAdd_Should_Be_Thread_Safe()
{
const int threadCount = 10;
const int iterationsPerThread = 100;
var tasks = new Task[threadCount];
var exceptions = new List<Exception>();
for (var i = 0; i < threadCount; i++)
{
tasks[i] = Task.Run(() =>
{
try
{
for (var j = 0; j < iterationsPerThread; j++)
{
var @event = _easyEvents.GetOrAddEvent<Event<int>>();
Assert.That(@event, Is.Not.Null);
}
}
catch (Exception ex)
{
lock (exceptions)
{
exceptions.Add(ex);
}
}
});
}
Task.WaitAll(tasks);
Assert.That(exceptions, Is.Empty, $"并发测试中发生异常: {string.Join(", ", exceptions.Select(e => e.Message))}");
}
/// <summary>
/// 测试并发场景下AddEvent的行为
/// </summary>
[Test]
public void AddEvent_Should_Throw_When_Already_Registered()
{
_easyEvents.AddEvent<Event<int>>();
Assert.Throws<ArgumentException>(() => _easyEvents.AddEvent<Event<int>>());
}
/// <summary>
/// 测试并发场景下多个不同事件类型的注册
/// </summary>
[Test]
public void Concurrent_Registration_Of_Different_Event_Types_Should_Work()
{
const int threadCount = 5;
var tasks = new Task[threadCount];
var exceptions = new List<Exception>();
// 每个线程注册不同类型的事件
for (var i = 0; i < threadCount; i++)
{
var index = i;
tasks[i] = Task.Run(() =>
{
try
{
switch (index)
{
case 0:
_easyEvents.GetOrAddEvent<Event<int>>();
break;
case 1:
_easyEvents.GetOrAddEvent<Event<string>>();
break;
case 2:
_easyEvents.GetOrAddEvent<Event<bool>>();
break;
case 3:
_easyEvents.GetOrAddEvent<Event<int, string>>();
break;
case 4:
_easyEvents.GetOrAddEvent<Event<double>>();
break;
}
}
catch (Exception ex)
{
lock (exceptions)
{
exceptions.Add(ex);
}
}
});
}
Task.WaitAll(tasks);
Assert.That(exceptions, Is.Empty);
// 验证所有事件都已注册
Assert.That(_easyEvents.GetEvent<Event<int>>(), Is.Not.Null);
Assert.That(_easyEvents.GetEvent<Event<string>>(), Is.Not.Null);
Assert.That(_easyEvents.GetEvent<Event<bool>>(), Is.Not.Null);
Assert.That(_easyEvents.GetEvent<Event<int, string>>(), Is.Not.Null);
Assert.That(_easyEvents.GetEvent<Event<double>>(), Is.Not.Null);
}
}