using System;
using System.Collections.Generic;
using NUnit.Framework;
using BaseGames.Core.Events;
using BaseGames.Core;
using UObject = UnityEngine.Object;
using UnityEngine;
namespace BaseGames.Tests.EditMode
{
///
/// SO 事件系统 + ServiceLocator 单元测试(EditMode,无需 Play Mode)。
///
[TestFixture]
public class EventSystemTests
{
// ── EventSubscription ────────────────────────────────────────────────
[Test]
public void EventSubscription_Dispose_CallsUnsubscribeAction()
{
bool called = false;
var sub = new EventSubscription(() => called = true);
sub.Dispose();
Assert.IsTrue(called, "Dispose 应调用 unsubscribe action");
}
[Test]
public void EventSubscription_Dispose_NullAction_DoesNotThrow()
{
var sub = new EventSubscription(null);
Assert.DoesNotThrow(() => sub.Dispose());
}
// ── CompositeDisposable ──────────────────────────────────────────────
[Test]
public void CompositeDisposable_Clear_DisposesAllItems()
{
var composite = new CompositeDisposable();
int count = 0;
composite.Add(new EventSubscription(() => count++));
composite.Add(new EventSubscription(() => count++));
composite.Add(new EventSubscription(() => count++));
composite.Clear();
Assert.AreEqual(3, count, "Clear 应 Dispose 所有已添加的订阅");
}
[Test]
public void CompositeDisposable_Clear_TwiceSafe()
{
var composite = new CompositeDisposable();
int count = 0;
composite.Add(new EventSubscription(() => count++));
composite.Clear();
composite.Clear(); // 第二次 Clear 不应重复 Dispose
Assert.AreEqual(1, count, "Clear 两次不应重复 Dispose");
}
[Test]
public void CompositeDisposable_Dispose_SameAsClear()
{
var composite = new CompositeDisposable();
bool disposed = false;
composite.Add(new EventSubscription(() => disposed = true));
((IDisposable)composite).Dispose();
Assert.IsTrue(disposed);
}
[Test]
public void AddTo_ReturnsOriginalSubscription()
{
var composite = new CompositeDisposable();
bool called = false;
var sub = new EventSubscription(() => called = true);
var returned = sub.AddTo(composite);
returned.Dispose();
Assert.IsTrue(called, "AddTo 返回的订阅 Dispose 后应触发原 unsubscribe");
}
[Test]
public void AddTo_Collection_AddsToList()
{
var list = new List();
bool called = false;
var sub = new EventSubscription(() => called = true);
sub.AddTo(list);
Assert.AreEqual(1, list.Count, "AddTo(ICollection) 应将订阅添加到列表");
list[0].Dispose();
Assert.IsTrue(called);
}
// ── IntEventChannelSO ────────────────────────────────────────────────
[Test]
public void IntEventChannel_Raise_CallsAllSubscribers()
{
var channel = ScriptableObject.CreateInstance();
try
{
int sum = 0;
channel.OnEventRaised += v => sum += v;
channel.OnEventRaised += v => sum += v;
channel.Raise(5);
Assert.AreEqual(10, sum, "两个订阅者应各收到一次事件");
}
finally
{
UObject.DestroyImmediate(channel);
}
}
[Test]
public void IntEventChannel_Raise_NoSubscribers_DoesNotThrow()
{
var channel = ScriptableObject.CreateInstance();
try
{
Assert.DoesNotThrow(() => channel.Raise(42));
}
finally
{
UObject.DestroyImmediate(channel);
}
}
[Test]
public void IntEventChannel_UnsubscribeAfterDispose()
{
var channel = ScriptableObject.CreateInstance();
try
{
int callCount = 0;
void Handler(int v) => callCount++;
channel.OnEventRaised += Handler;
channel.Raise(1);
Assert.AreEqual(1, callCount);
channel.OnEventRaised -= Handler;
channel.Raise(1);
Assert.AreEqual(1, callCount, "取消订阅后不应再收到事件");
}
finally
{
UObject.DestroyImmediate(channel);
}
}
// ── VoidEventChannelSO ───────────────────────────────────────────────
[Test]
public void VoidEventChannel_Raise_CallsSubscriber()
{
var channel = ScriptableObject.CreateInstance();
try
{
bool received = false;
channel.OnEventRaised += () => received = true;
channel.Raise();
Assert.IsTrue(received);
}
finally
{
UObject.DestroyImmediate(channel);
}
}
// ── ServiceLocator ───────────────────────────────────────────────────
[SetUp]
public void SetUp() => ServiceLocator.Reset();
[TearDown]
public void TearDown() => ServiceLocator.Reset();
[Test]
public void ServiceLocator_RegisterAndGet_ReturnsInstance()
{
var mock = new MockService();
ServiceLocator.Register(mock);
var retrieved = ServiceLocator.Get();
Assert.AreSame(mock, retrieved);
}
[Test]
public void ServiceLocator_GetUnregistered_ThrowsInvalidOperation()
{
Assert.Throws(
() => ServiceLocator.Get());
}
[Test]
public void ServiceLocator_GetOrDefault_Unregistered_ReturnsDefault()
{
var result = ServiceLocator.GetOrDefault();
Assert.IsNull(result);
}
[Test]
public void ServiceLocator_GetOrDefault_Registered_ReturnsInstance()
{
var mock = new MockService();
ServiceLocator.Register(mock);
var result = ServiceLocator.GetOrDefault();
Assert.AreSame(mock, result);
}
[Test]
public void ServiceLocator_RegisterIfAbsent_DoesNotOverwrite()
{
var first = new MockService();
var second = new MockService();
ServiceLocator.Register(first);
ServiceLocator.RegisterIfAbsent(second);
Assert.AreSame(first, ServiceLocator.Get(),
"RegisterIfAbsent 不应覆盖已存在的注册");
}
[Test]
public void ServiceLocator_Unregister_RemovesService()
{
ServiceLocator.Register(new MockService());
ServiceLocator.Unregister();
Assert.Throws(
() => ServiceLocator.Get());
}
[Test]
public void ServiceLocator_Unregister_WithWrongInstance_DoesNotRemove()
{
var first = new MockService();
var second = new MockService();
ServiceLocator.Register(first);
ServiceLocator.Unregister(second); // 不同实例,不应移除
Assert.AreSame(first, ServiceLocator.Get(),
"错误实例的 Unregister 不应移除已注册的服务");
}
[Test]
public void ServiceLocator_OverrideForTest_ReplacesService()
{
var original = new MockService { Value = 1 };
var replacement = new MockService { Value = 99 };
ServiceLocator.Register(original);
ServiceLocator.OverrideForTest(replacement);
Assert.AreEqual(99, ServiceLocator.Get().Value);
}
// ── Test Helpers ─────────────────────────────────────────────────────
private interface IMockService { int Value { get; set; } }
private class MockService : IMockService { public int Value { get; set; } }
}
}