using game; using game.PlayerDataStorage; int assertions=0, failures=0; void Check(bool ok,string message){assertions++; if(!ok) throw new Exception(message);} foreach(bool enabled in new[]{false,true}) foreach(var status in Enum.GetValues()) { try { PlayerKVGameplayLoop.Enabled=enabled; PlayerKVShutdownRegistry.Process.Status=status; UnityEditor.EditorApplication.isPlaying=true; UnityEditor.EditorApplication.StopCount=0; RootCtx.DestroyCount=0; var act=new Probe{DestoryRoot=Array.Empty()}; act.Started(); Check(RootCtx.DestroyCount==1 && act.BaseStarted,"original cleanup and base callback kept"); #if UNITY_EDITOR && !SERVICE_HOST if(enabled) { Check(act.Invoked==null,"test exit must not schedule Unload/PreBoot"); Check(UnityEditor.EditorApplication.StopCount==(status==PlayerKVShutdownStatus.Ready?1:0),"stop only after registry Ready"); Check(PlayerKVShutdownRegistry.Process.Status==status && PlayerKVGameplayLoop.Enabled,"barrier and once-per-process state retained"); } else #endif { Check(act.Invoked=="Unload" && act.Delay==2f,"ordinary exit preserves delayed Unload"); Check(UnityEditor.EditorApplication.StopCount==0,"ordinary exit never stops editor"); } Console.WriteLine($"PASS enabled={enabled} status={status}"); } catch(Exception e){failures++; Console.WriteLine($"FAIL enabled={enabled} status={status}: {e.Message}");} } Console.WriteLine($"RESULT actual-QuitAct groups=10 assertions={assertions} failures={failures} requests=0"); return failures==0?0:1; class Probe:QuitAct { public void Started()=>base.OnStarted(); } namespace game.PlayerDataStorage { public enum PlayerKVShutdownStatus {Ready,Pending,Failed,Cancelled,Input} public class PlayerKVShutdownRegistry {public static PlayerKVShutdownRegistry Process=new();public PlayerKVShutdownStatus Status;public Task ObserveAsync(int ms)=>Task.FromResult(Status);} public static class PlayerKVGameplayLoop {public static bool Enabled;public static Task CloseAsync()=>Task.CompletedTask;} } namespace UnityEditor {public static class EditorApplication {static bool playing;public static int StopCount;public static bool isPlaying{get=>playing;set{playing=value;if(!value)StopCount++;}}}} namespace asap.core {public class InjectAttribute:Attribute{}public interface IConfig{void Rest();}} namespace game {public interface IUserService{void Logout();}public class PlayFabUserService:IUserService {public void Logout(){} public Task CloseKVForValidationAsync()=>Task.CompletedTask;public Task ObserveKVQuitAsync()=>Task.CompletedTask;}public static class QuitStage{public static bool Logout;}} namespace Game {public class AGameAct {public bool BaseStarted;public string Invoked;public float Delay;protected virtual void OnDestroy(){}protected virtual void OnStarted(){BaseStarted=true;}public virtual Task StartAsync()=>Task.FromResult(true);public void Invoke(string s,float n){Invoked=s;Delay=n;}}} namespace GameCore {public static class Timer{public static void CancelAllRegisteredTimers(){}public static void Destroy(){}}} namespace UnityEngine {public class GameObject{public static void Destroy(object x){}public static void DestroyImmediate(object x){}public static GameObject Find(string x)=>null;}public static class Debug{public static void LogException(Exception e){}public static void Log(object message){}}} public interface IRTService{Task ReleaseAsync();}public interface IAdsService{void Release();} public class SoundMgr{public void Release(){}} public static class LoadingScreen{public static void Hide(){}} public class UIManager{public static UIManager Instance=new();public UnityEngine.GameObject gameObject=new();} public class GameEventMgr{public static GameEventMgr Instance=new();public void Clear(){}} public class RedPointManager{public static RedPointManager Instance=new();public void Init(){}} public static class RootCtx{public static int DestroyCount;public static void DestoryRootCtx(){DestroyCount++;}}