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