Files
EscapistsReintroduced/tests/test_finite_magazine_reservation.py
T
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

60 lines
3.7 KiB
Python

"""Execute the finite identity reservation used by the real infantry selector."""
import os,re
from pathlib import Path
from policy_harness import ROOT,run
def translated_reservation():
p=Path(os.environ.get('ERR_FINITE_RESERVATION_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_FiniteMagazineReservation.c'))
source=p.read_text()
for forbidden in ('Spawn','DeleteEntity','SetAmmoCount','TryMove','TrySwap','GetOrigin'):
assert forbidden not in source,forbidden
code=re.sub(r'//[^\n]*','',source).replace('protected ','').replace('ref ','').replace('class ','struct ')
code=code.replace('array<IEntity>','Array<IEntity>*').replace('void ERR_FiniteMagazineReservation(','ERR_FiniteMagazineReservation(')
for name in ('m_Original','m_Spent'):code=code.replace(name+'.',name+'->')
code=re.sub(r'\b(bool|IEntity|Array<IEntity>\*) (\w+);',r'\1 \2{};',code)
return code.replace('\n}\n','\n};\n')
if __name__=='__main__':
run(r'''
#include <cassert>
#include <vector>
#include <algorithm>
struct Entity{};using IEntity=Entity*;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;
int Count(){return this->size();}void Insert(T v){this->push_back(v);}
bool Contains(T v){return std::find(this->begin(),this->end(),v)!=this->end();}
void RemoveItem(T v){this->erase(std::remove(this->begin(),this->end(),v),this->end());}};
'''+translated_reservation()+r'''
int main(){
Entity a,b,foreign;
{Array<IEntity> stock{&a,&b},spent;
ERR_FiniteMagazineReservation infantry(&stock,&spent,&a),mounted(&stock,&spent,&a);
assert(infantry.Reserve()&&!infantry.Reserve()&&!mounted.Reserve()&&spent.Count()==1);
assert(infantry.ReleaseUnsubmitted()&&spent.Count()==0);
// A failed handle is terminal; a new attempt can claim the released identity.
assert(!mounted.Reserve());ERR_FiniteMagazineReservation next(&stock,&spent,&a);
assert(next.Reserve()&&!infantry.ReleaseUnsubmitted()&&!infantry.Commit()&&spent.Count()==1);
assert(next.Commit()&&!next.ReleaseUnsubmitted()&&!next.Commit());
ERR_FiniteMagazineReservation replay(&stock,&spent,&a),other(&stock,&spent,&b);
assert(!replay.Reserve()&&other.Reserve()&&other.Commit()&&spent.Count()==2);}
// An abandoned uncertain claim stays spent; destruction never implies rollback.
{Array<IEntity> stock{&a},spent;{ERR_FiniteMagazineReservation pending(&stock,&spent,&a);assert(pending.Reserve());}
ERR_FiniteMagazineReservation retry(&stock,&spent,&a);assert(!retry.Reserve()&&spent.Count()==1);}
for(int invalid=0;invalid<5;invalid++){
Array<IEntity> stock{&a},spent;
if(invalid==4){for(int i=0;i<96;i++)stock.Insert(&b);}
ERR_FiniteMagazineReservation r(invalid==0?nullptr:&stock,invalid==1?nullptr:&spent,invalid==2?nullptr:invalid==3?&foreign:&a);
assert(!r.Reserve()&&!r.Commit()&&!r.ReleaseUnsubmitted()&&spent.Count()==0);}
{Array<IEntity> stock,spent;std::vector<Entity> entities(97);
for(int i=0;i<96;i++)stock.Insert(&entities[i]);
for(int i=0;i<96;i++){ERR_FiniteMagazineReservation r(&stock,&spent,&entities[i]);assert(r.Reserve()&&r.Commit());}
assert(spent.Count()==96);ERR_FiniteMagazineReservation replay(&stock,&spent,&entities[0]);assert(!replay.Reserve());
stock.Insert(&entities[96]);ERR_FiniteMagazineReservation overflow(&stock,&spent,&entities[96]);assert(!overflow.Reserve());}
// The helper never changes the original stock set or grants release before reservation.
{Array<IEntity> stock{&a},spent;ERR_FiniteMagazineReservation r(&stock,&spent,&a);
assert(!r.ReleaseUnsubmitted()&&!r.Commit());assert(r.Reserve()&&r.ReleaseUnsubmitted());
assert(stock.Count()==1&&stock[0]==&a&&spent.Count()==0);}
}
''')
print('PASS: actual shared finite identity reservation; competing consumers, submission permanence, stale cleanup and96-item bound')