#nullable enable using System.Collections; using Cysharp.Threading.Tasks; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; namespace ShrinkEventBus.PlayMode.Tests { internal readonly struct MonoLifecycleEvent : IShrinkEvent { public MonoLifecycleEvent(int value) => Value = value; public int Value { get; } } internal readonly struct AwakeToDestroyEvent : IShrinkEvent { public AwakeToDestroyEvent(int value) => Value = value; public int Value { get; } } [ShrinkEventSubscriber( DefaultBus = "game", Lifetime = ShrinkSubscriberLifetime.AwakeToDestroy)] internal sealed class AwakeToDestroyTarget : MonoBehaviour { public static int Sum { get; set; } public static int AwakeCalls { get; set; } public static int DestroyCalls { get; set; } private void Awake() => AwakeCalls++; private void OnDestroy() => DestroyCalls++; [ShrinkSubscribe] private void OnEvent(AwakeToDestroyEvent value) => Sum += value.Value; } [ShrinkEventSubscriber(Lifetime = ShrinkSubscriberLifetime.AwakeToDestroy)] internal abstract class AbstractAwakeToDestroyTarget : MonoBehaviour { public static int Sum { get; set; } [ShrinkSubscribe] private void OnEvent(AwakeToDestroyEvent value) => Sum += value.Value; } internal sealed class ConcreteAwakeToDestroyTarget : AbstractAwakeToDestroyTarget { } [ShrinkEventSubscriber(DefaultBus = "game")] internal sealed class MonoLifecycleTarget : MonoBehaviour { public int Sum { get; private set; } [ShrinkSubscribe] private void OnEvent(MonoLifecycleEvent value) => Sum += value.Value; } public sealed class ShrinkMonoEventScopePlayModeTests { [UnityTest] public IEnumerator AwakeToDestroyLifetimeIsInjectedWithoutManualBinding() { AwakeToDestroyTarget.Sum = 0; AwakeToDestroyTarget.AwakeCalls = 0; AwakeToDestroyTarget.DestroyCalls = 0; var gameObject = new GameObject("ShrinkEventBus-AwakeToDestroy"); var target = gameObject.AddComponent(); yield return null; Assert.IsInstanceOf(target); Assert.AreEqual(1, AwakeToDestroyTarget.AwakeCalls); yield return EventBus.PostAsync(new AwakeToDestroyEvent(3)).ToCoroutine(); Assert.AreEqual(3, AwakeToDestroyTarget.Sum); gameObject.SetActive(false); yield return null; yield return EventBus.PostAsync(new AwakeToDestroyEvent(5)).ToCoroutine(); Assert.AreEqual(8, AwakeToDestroyTarget.Sum, "AwakeToDestroy subscriptions must survive OnDisable."); Object.Destroy(gameObject); yield return null; Assert.AreEqual(1, AwakeToDestroyTarget.DestroyCalls); yield return EventBus.PostAsync(new AwakeToDestroyEvent(7)).ToCoroutine(); Assert.AreEqual(8, AwakeToDestroyTarget.Sum, "The generated OnDestroy release must remove the subscription."); } [UnityTest] public IEnumerator AwakeToDestroyLifetimeSupportsAbstractSubscriberBasesWithoutLifecycleMethods() { AbstractAwakeToDestroyTarget.Sum = 0; var gameObject = new GameObject("ShrinkEventBus-AbstractAwakeToDestroy"); var target = gameObject.AddComponent(); yield return null; Assert.IsInstanceOf(target); yield return EventBus.PostAsync(new AwakeToDestroyEvent(11)).ToCoroutine(); Assert.AreEqual(11, AbstractAwakeToDestroyTarget.Sum); Object.Destroy(gameObject); yield return null; yield return EventBus.PostAsync(new AwakeToDestroyEvent(13)).ToCoroutine(); Assert.AreEqual(11, AbstractAwakeToDestroyTarget.Sum); } [UnityTest] public IEnumerator OnEnableAttachesAndOnDisableReleases() { var gameObject = new GameObject("ShrinkMonoEventScope-PlayMode"); gameObject.SetActive(false); var target = gameObject.AddComponent(); gameObject.AddComponent(); gameObject.SetActive(true); yield return null; yield return EventBus.PostAsync(new MonoLifecycleEvent(3)).ToCoroutine(); Assert.AreEqual(3, target.Sum); gameObject.SetActive(false); yield return null; yield return EventBus.PostAsync(new MonoLifecycleEvent(5)).ToCoroutine(); Assert.AreEqual(3, target.Sum); Object.Destroy(gameObject); } } }