from pathlib import Path r=Path(__file__).parent s=Path('Client/Assets/Scripts/GampPlay/FishingB/FishingUpdateSys.cs').read_text(encoding='utf-8-sig') def method(sig): a=s.index(sig);i=s.index('{',a)+1;n=1 while n:n+=(s[i]=='{')-(s[i]=='}');i+=1 return s[a:i] code='''using System;using System.Threading.Tasks;using game.PlayerDataStorage; int checks=0;void Check(bool b,string m){checks++;if(!b)throw new Exception(m);} Controller Fresh(){PlayerKVGameplayLoop.Reset();UIManager.Instance=new();return new();} var c=Fresh();c.PiercingFish(3);c.PiercingFish(3);c.End();c.End(); Check(c.Strikes==1&&c.Endings==1&&UIManager.Instance.Calls==1,"actual wrappers suppress repeated drawing/end"); Check(PlayerKVGameplayLoop.Scope==null,"UI await never holds ending scope"); c.Disable();UIManager.Instance.Response.SetResult(new object());await Task.Delay(20); Check(c.Destroyed==1&&PlayerKVGameplayLoop.Faults==1,"actual Disable during UI await disposes stale returned panel"); c=Fresh();c.PiercingFish(3);c.Dead();Check(PlayerKVGameplayLoop.Cancelled,"actual Destroy revokes controller"); c=Fresh();c.PiercingFish(3);c.fishingData.IsCaughtFish=false;c.End();Check(c.Endings==0&&UIManager.Instance.Calls==0,"natural failure cannot invoke reward core"); foreach(int version in new[]{1,2}){c=Fresh();PlayerKVGameplayLoop.Version=version;c.PiercingFish(3);Check(c.Strikes==0&&PlayerKVGameplayLoop.Faults==1,"old permits do not draw");} c=Fresh();c.PiercingFish(3);PlayerKVGameplayLoop.Confirmed=true;c.Disable();Check(PlayerKVGameplayLoop.Faults==0,"confirmed controller normal disable no second fault"); c=Fresh();PlayerKVGameplayLoop.Enabled=false;c.PiercingFish(3);c.End();Check(c.Strikes==1&&c.Endings==1,"ordinary PF wrapper takes original core"); Console.WriteLine($"PASS actual controller source wrappers/lifecycle {checks} assertions; core/render/storage services are spies; no gameplay or network"); public class Controller { public int Strikes,Endings,Destroyed;public FishingData fishingData=new();bool isFail;IDisposable disposables,_comboDisposable;object _hpcoroutine; public void End()=>FishEnd();public void Disable()=>OnDisable();public void Dead()=>OnDestroy(); void PiercingFishCore(int zone){Strikes++;}void FishEndCore(){Endings++;}void Destroy(object p){Destroyed++;} void StopSoundCombo(){}void StopRodAudio(){}void StopNotReeling(){}void StopDrawingSplash(){}void AudioRelease(){}void SpSkillFxRelease(){}void StopAllCoroutines(){} ''' for sig in ['public void PiercingFish(int waitingZone)','void FishEnd()','async Task ShowKVRewardPanelAsync()','private void OnDisable()','private void OnDestroy()']:code+=method(sig)+'\n' code+='''} public class FishingData{public bool IsCaughtFish=true;}public enum UITypes{FishingRewardPanel} public class UIManager{public static UIManager Instance=new();public int Calls;public TaskCompletionSource Response=new();public Task ShowUI(UITypes type){Calls++;return Response.Task;}} public static class GContext{public static Container container=new();}public class Container{public T Resolve() where T:new()=>new();}public class SoundMgr{public void StopActiveFishingLoopSounds(float x){}} namespace game.PlayerDataStorage {public static class PlayerKVGameplayLoop{ public static bool Enabled,Drawing,Ending,Cancelled,Confirmed;public static int Version,Faults;public static string Scope; public static void Reset(){Enabled=true;Drawing=Ending=Cancelled=Confirmed=false;Version=3;Faults=0;Scope=null;} public static bool RewardRunCurrent=>!Cancelled; public static bool BeginRewardDrawing(object o){if(Version!=3||Cancelled)throw new Exception("no draw");if(Drawing)return false;Drawing=true;return true;} public static bool BeginRewardEnding(object o,bool success){if(Ending)return false;if(!success)throw new Exception("failed catch");Ending=true;return true;} public static IDisposable BeginRewardSegment(string n){Scope=n;return new Batch();}class Batch:IDisposable{public void Dispose(){Scope=null;}} public static void Fault(string s){Faults++;}public static void CancelReward(object o,string r){if(Cancelled)return;Cancelled=true;if(!Confirmed)Fault(r);} }} ''' (r/'ControllerTests.cs.txt').write_text(code,encoding='utf-8') (r/'ControllerTests.csproj').write_text('''Exenet9.0falseUNITY_EDITOR$(MSBuildThisFileDirectory)../MinimalLoginTests/NuGet.Config''',encoding='utf-8')