import os import re import sys import tempfile import types import unittest from pathlib import Path ROOT=Path(__file__).resolve().parents[2] def script(name):return (ROOT/'src/platform/ScriptLib'/name).read_text().split('R"PY(',1)[1].rsplit(')PY";',1)[0] class Widget(object): def __init__(self,*a,**k):self.shown=False;self.text='';self.pos=(0,0);self.size=(0,0) def __getattr__(self,name):return lambda *a,**k:None def Show(self):self.shown=True def Hide(self):self.shown=False def IsShow(self):return self.shown def SetText(self,text):self.text=text def SetPosition(self,x,y):self.pos=(x,y) def GetGlobalPosition(self):return self.pos def SetSize(self,w,h):self.size=(w,h) def GetWidth(self):return self.size[0] def GetHeight(self):return self.size[1] class Action(Widget): def __init__(self,parent,x,y,width,text,event): Widget.__init__(self);self.pos=(x,y);self.event=event;self.text=text;self.shown=True class DisplayTests(unittest.TestCase): def setUp(self): self.cwd=os.getcwd();self.tmp=tempfile.TemporaryDirectory();os.chdir(self.tmp.name) self.state=dict(effect=None,name=True,damage=True,parts={});self.opened=[] mods={n:types.ModuleType(n) for n in ('background','game','grp','systemSetting','ui','mt_biolog','mt_wiki')} bg=mods['background'];bg.PART_TREE=3;bg.PART_CLOUD=2;bg.SetVisiblePart=lambda part,on:self.state['parts'].__setitem__(part,on) ss=mods['systemSetting'];ss.SetEffectLevel=lambda v:self.state.__setitem__('effect',v) ss.IsAlwaysShowName=lambda:self.state['name'];ss.SetAlwaysShowNameFlag=lambda v:self.state.__setitem__('name',bool(v)) ss.IsShowDamage=lambda:self.state['damage'];ss.SetShowDamageFlag=lambda v:self.state.__setitem__('damage',bool(v)) mods['game'].GameWindow=type('GameWindow',(),dict(Open=lambda s:self.opened.append(1),Close=lambda s:None)) grp=mods['grp'];grp.SetColor=grp.RenderBar=grp.RenderBox=lambda *a:None mods['ui'].Window=Widget mods['mt_biolog'].Action=Action;mods['mt_biolog'].Text=lambda p,x,y,t='',c=0:Widget();mods['mt_biolog'].L=lambda t:t if isinstance(t,str) else t.decode('utf-8') mods['mt_wiki'].B=lambda t,limit=0:t self.previous={n:sys.modules.get(n) for n in mods} sys.modules.update(mods) def load(self): self.ns={'__name__':'mt_display'};exec(script('DisplayScript.inc'),self.ns);return self.ns def tearDown(self): os.chdir(self.cwd);self.tmp.cleanup() for name,value in self.previous.items(): if value is None:sys.modules.pop(name,None) else:sys.modules[name]=value def test_defaults_and_config(self): ns=self.load();self.assertEqual(self.state['effect'],0) o=ns['Options']();self.assertEqual(o.Dump(),'0 1 1') for bad in ('5 1 1','1 2 1','a 1 1','1 1','10 1 1','-1 1 1'): o=ns['Options']();o.Load(bad);self.assertEqual(o.Dump(),'0 1 1') Path('mt_display.cfg').write_text('3 0 1') self.load();self.assertEqual(self.state['effect'],3) # applied at boot, before any map def test_game_window_applies_and_saves(self): Path('mt_display.cfg').write_text('2 0 1') ns=self.load();game=sys.modules['game'];gw=game.GameWindow();gw.interface=types.SimpleNamespace(wndMiniMap=Widget()) game.GameWindow.Open(gw);self.assertEqual(self.opened,[1]) self.assertEqual(self.state['parts'],{3:0,2:1});self.assertEqual(ns['g_entry'].pos,(4,252)) w=ns['g_window'];w.Open() self.assertIn('【',w.levelButtons[2].text);self.assertNotIn('【',w.levelButtons[0].text) self.assertNotIn('【',w.treeButton.text);self.assertIn('【',w.cloudButton.text) w.levelButtons[4].event();self.assertEqual(self.state['effect'],4) w.ToggleTree();w.ToggleCloud();self.assertEqual(self.state['parts'],{3:1,2:0}) self.assertEqual(Path('mt_display.cfg').read_text(),'4 1 0') w.ToggleName();w.ToggleDamage();self.assertEqual((self.state['name'],self.state['damage']),(False,False)) self.assertNotIn('【',w.nameButton.text) game.GameWindow.Close(gw);self.assertIsNone(ns['g_window']);self.assertIsNone(ns['g_entry']) def test_levels_match_native_rule(self): ns=self.load();text=(ROOT/'src/port/UserInterface/InstanceBase.cpp').read_text(encoding='latin1') body=text.split('void CInstanceBase::__UpdateEffectShown()',1)[1].split('\n}\n',1)[0] self.assertEqual(len(ns['LEVELS']),5);self.assertIn('iLevel == 4',body) self.assertIn('IsEnemy() || IsStone()',body);self.assertIn('IsPC()',body) system=(ROOT/'src/port/UserInterface/PythonSystem.h').read_text(encoding='latin1') self.assertIn('iLevel > 4',system) render=(ROOT/'src/port/GameLib/MapOutdoorRender.cpp').read_text(encoding='latin1') blocker=render.split('void CMapOutdoor::RenderPCBlocker()',1)[1].split('\n}\n',1)[0] self.assertIn('TREE_OBJECT != pInstance->GetType()',blocker) # near trees are drawn as PC blockers if __name__=='__main__':unittest.main()