Files
EscapistsReintroduced/tests/test_shared_map_ownership.py
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

119 lines
8.3 KiB
Python

"""Execute actual shared-map ownership/callback cleanup with explicit API doubles.
No Enforce compilation, rendering, remote client or multiplayer claim.
"""
import re
from policy_harness import ROOT, body, run
source = ROOT/'addons/core/Scripts/Game/ERR/30_Map/ERRM_SharedMapComponent.c'
def adapt(signature):
text = body(source, signature).replace('protected ', '').replace('override ', '')
text = text.replace('MapConfiguration config', 'Config* config')
text = text.replace('IEntity owner', 'Entity* owner').replace('PlayerController playerController', 'PlayerController* playerController')
text = text.replace(' = null;', ' = nullptr;').replace('SCR_MapEntity.GetMapInstance()', 'Map::current')
text = text.replace('SCR_MapEntity.GetOnMapOpenComplete().Remove(OnMapRendererReady);', 'RemoveReadyListener();')
text = text.replace('GetGame().', 'GetGame()->').replace('GetCallqueue().', 'GetCallqueue()->')
for field in ['m_ESMMapEntity', 'm_ESMMapConfig', 'm_ESMRootWidget', 'm_ESMRenderTarget', 'm_ESMAtlas', 'playerController']:
text = text.replace(field+'.', field+'->')
for callback in ['TryCaptureMapSurface', 'CenterMapOnHolder', 'FinishMapCapture', 'PreserveNativeMapPriority']:
text = text.replace('.Remove('+callback+')', '.Remove(this, &Capture::'+callback+')')
text = text.replace('->Remove('+callback+')', '->Remove(this, &Capture::'+callback+')')
text = text.replace('CallLater('+callback+',', 'CallLater(this, &Capture::'+callback+',')
return text.replace('super.OnDelete(owner)', 'NativeDelete(owner)')
methods = '\n'.join(adapt(signature) for signature in [
'protected bool OwnsMapCapture(', 'protected void OnMapRendererReady(', 'protected void ReleaseMapCapture(',
'protected void FinishMapCapture(', 'protected void StopMapCapture(',
'protected void RestoreCharacterCamera(', 'protected override void OnDelete('])
run(r'''
#include <cassert>
#include <vector>
#include <algorithm>
struct Capture;struct Entity{};
struct Widget {bool enabled=true,visible=true,removed=false;void SetEnabled(bool x){enabled=x;}void SetVisible(bool x){visible=x;}void RemoveFromHierarchy(){removed=true;}};
struct Config {Widget* RootWidgetRef=nullptr;};
struct PlayerController {bool camera=false;int writes=0;void SetCharacterCameraRenderActive(bool x){camera=x;writes++;}};
struct Queue {
using Entry=std::pair<Capture*,void(Capture::*)()>;std::vector<Entry> pending;
void Remove(Capture* owner,void(Capture::*callback)()){pending.erase(std::remove(pending.begin(),pending.end(),Entry(owner,callback)),pending.end());}
void CallLater(Capture* owner,void(Capture::*callback)(),int,bool){pending.emplace_back(owner,callback);}
int Count(Capture* owner){return std::count_if(pending.begin(),pending.end(),[=](const Entry& e){return e.first==owner;});}
};
struct Game {Queue queue;PlayerController controller;Queue* GetCallqueue(){return &queue;}PlayerController* GetPlayerController(){return &controller;}};
Game game;Game* currentGame=&game;Game* GetGame(){return currentGame;}
struct Map {static Map* current;bool opened=true;Config* config=nullptr;int closes=0;
bool IsOpen(){return opened;}Config* GetMapConfig(){return config;}
void CloseMap(){closes++;opened=false;game.controller.SetCharacterCameraRenderActive(true);}
};Map* Map::current=nullptr;
struct Atlas {void Release(bool){}};
struct Capture {
Atlas* m_ESMAtlas=nullptr;
bool m_bESMInHand=true,m_bESMOwnsMapRenderer=false;
Map* m_ESMMapEntity=nullptr;Config* m_ESMMapConfig=nullptr;
Widget* m_ESMRootWidget=nullptr;Widget* m_ESMRenderTarget=nullptr;Entity* m_ESMCharacterOwner=nullptr;
static constexpr int MAP_RETRY_DELAY_MS=250,MAP_CAPTURE_DELAY_MS=500;bool readyListener=false;int centres=0;int completed=0,nativeDeletes=0,styleRestores=0;
void StopPaperInput(){}void RemoveReadyListener(){readyListener=false;}void TryCaptureMapSurface(){}void CenterMapOnHolder(){centres++;}void PreserveNativeMapPriority(){}
void RestorePaperMapStyle(){assert(OwnsMapCapture());styleRestores++;}
void CompleteMapModeSwitch(){completed++;}void NativeDelete(Entity*){nativeDeletes++;}
'''+methods+r'''
void Bind(Map& map,Config& config,Widget& root,Widget& target){
m_ESMMapEntity=&map;m_ESMMapConfig=&config;m_ESMRootWidget=&root;m_ESMRenderTarget=&target;
readyListener=true;config.RootWidgetRef=&root;map.config=&config;map.opened=true;Map::current=&map;m_bESMOwnsMapRenderer=true;
}
};
int main(){Map map;Config mine,foreign;Widget root,target,foreignRoot;foreign.RootWidgetRef=&foreignRoot;
Capture normal;normal.Bind(map,mine,root,target);assert(normal.OwnsMapCapture());
normal.RestoreCharacterCamera();assert(game.controller.camera&&game.controller.writes==1);
normal.FinishMapCapture();assert(map.closes==1&&!target.enabled&&!target.visible&&!normal.m_bESMOwnsMapRenderer&&!normal.m_ESMMapConfig&&normal.completed==1);
normal.FinishMapCapture();assert(map.closes==1&&normal.completed==1&&normal.styleRestores==1); // late duplicate has no authority
Capture replaced;replaced.Bind(map,mine,root,target);map.config=&foreign;game.controller.camera=false;
int writes=game.controller.writes,closes=map.closes;
replaced.RestoreCharacterCamera();assert(game.controller.writes==writes&&!game.controller.camera);
replaced.FinishMapCapture();assert(map.closes==closes&&map.opened&&replaced.completed==0&&!target.enabled);
assert(game.queue.Count(&replaced)==1&&replaced.styleRestores==0); // defer vanilla focus until foreign UI closes
replaced.StopMapCapture();assert(game.queue.Count(&replaced)==0&&map.opened&&game.controller.writes==writes);
Capture reused;reused.Bind(map,mine,root,target);mine.RootWidgetRef=&foreignRoot;
assert(map.config==reused.m_ESMMapConfig&&!reused.OwnsMapCapture()); // same native object was reused
reused.FinishMapCapture();assert(map.closes==closes&&reused.completed==0);reused.StopMapCapture();
Capture oldWorld;oldWorld.Bind(map,mine,root,target);Map newWorld;Map::current=&newWorld;
oldWorld.StopMapCapture();assert(map.closes==closes&&game.controller.writes==writes);
Capture spare;Widget spareRoot,spareTarget;spare.m_ESMRootWidget=&spareRoot;spare.m_ESMRenderTarget=&spareTarget;
spare.OnDelete(nullptr);assert(spare.nativeDeletes==1&&spareRoot.removed&&!spareTarget.enabled);
assert(game.controller.writes==writes&&!game.controller.camera&&map.closes==closes);
Capture owned,other;owned.Bind(map,mine,root,target);
game.queue.CallLater(&owned,&Capture::TryCaptureMapSurface,250,false);
game.queue.CallLater(&owned,&Capture::CenterMapOnHolder,100,false);
game.queue.CallLater(&owned,&Capture::PreserveNativeMapPriority,100,true);
game.queue.CallLater(&owned,&Capture::FinishMapCapture,500,false);
game.queue.CallLater(&other,&Capture::FinishMapCapture,500,false);
owned.OnDelete(nullptr);assert(game.queue.Count(&owned)==0&&game.queue.Count(&other)==1);
assert(map.closes==closes+1&&!map.opened&&root.removed&&!target.enabled);
assert(owned.m_ESMMapEntity==nullptr&&owned.m_ESMMapConfig==nullptr&&owned.m_ESMRootWidget==nullptr&&owned.nativeDeletes==1);
owned.FinishMapCapture();assert(owned.completed==0&&map.closes==closes+1);
Capture shuttingDown;shuttingDown.Bind(map,mine,root,target);currentGame=nullptr;
writes=game.controller.writes;closes=map.closes;shuttingDown.OnDelete(nullptr);
assert(game.controller.writes==writes&&map.closes==closes&&shuttingDown.nativeDeletes==1);
currentGame=&game;
Capture waiting;waiting.Bind(map,mine,root,target);
game.queue.CallLater(&waiting,&Capture::FinishMapCapture,5000,false);
waiting.OnMapRendererReady(&foreign);assert(waiting.centres==0&&waiting.readyListener);
waiting.OnMapRendererReady(&mine);
assert(waiting.centres==1&&!waiting.readyListener&&game.queue.Count(&waiting)==1);
waiting.StopMapCapture();assert(!waiting.readyListener&&game.queue.Count(&waiting)==0);
Capture cancelled;cancelled.Bind(map,mine,root,target);cancelled.StopMapCapture();
cancelled.OnMapRendererReady(&mine);assert(cancelled.centres==0&&!cancelled.readyListener);
}
''')
center = body(source, 'protected void DrawSelectedPaperPage(')
assert center.index('!OwnsMapCapture()') < center.index('.ZoomPanSmooth(')
capture = body(source, 'protected void TryCaptureMapSurface(')
assert capture.index('m_ESMMapConfig = mapConfig;') < capture.index('.OpenMap(mapConfig)')
assert capture.index('if (!OwnsMapCapture())') < capture.index('RestoreCharacterCamera();')
print('PASS: actual shared-map callbacks bind exact map/config/root, preserve replacement UI/camera, defer focus, and remove only owned delayed work')