Build independent base gameplay and source-bound native verification
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
"""Synthetic identity regression using actual Enforce inventory/count method bodies.
|
||||
|
||||
This checks source policy only; it does not simulate native inventory transfers.
|
||||
"""
|
||||
import unittest
|
||||
from policy_harness import ROOT, body, run
|
||||
|
||||
|
||||
def translated(source):
|
||||
for old, new in [
|
||||
('InventoryStorageManagerComponent inventory', 'InventoryStorageManagerComponent* inventory'),
|
||||
('notnull array<IEntity> items', 'Array<Entity*>& items'),
|
||||
('out array<IEntity> items', 'Array<Entity*>& items'),
|
||||
('array<IEntity>', 'Array<Entity*>'),
|
||||
('IEntity item', 'Entity* item'),
|
||||
('foreach (', 'for ('),
|
||||
('inventory.GetItems', 'inventory->GetItems'),
|
||||
('item.GetPrefabData()', 'item->GetPrefabData()'),
|
||||
('.GetPrefabName()', '->GetPrefabName()'),
|
||||
('ERR_Inventory.', 'ERR_Inventory::'),
|
||||
('int count;', 'int count = 0;'),
|
||||
]:
|
||||
source = source.replace(old, new)
|
||||
return source
|
||||
|
||||
|
||||
class InventoryEnumerationPolicy(unittest.TestCase):
|
||||
def test_duplicate_references_distinct_rounds_and_cross_inventory_union(self):
|
||||
helper = ROOT / 'addons/core/Scripts/Game/ERR/ERR_Inventory.c'
|
||||
mortar = ROOT / 'addons/ai/Scripts/Game/ERR/ERR_MortarBattery.c'
|
||||
methods = '\n'.join(translated(body(helper, signature)) for signature in (
|
||||
'static int AppendUnique(', 'static int Items('))
|
||||
count = translated(body(mortar, 'int InventoryShells('))
|
||||
run(r'''
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
template <typename T> struct Array : std::vector<T> {
|
||||
using std::vector<T>::vector;
|
||||
bool Contains(T v) const { return std::find(this->begin(),this->end(),v)!=this->end(); }
|
||||
void Insert(T v) { this->push_back(v); }
|
||||
void Clear() { this->clear(); }
|
||||
int Count() const { return this->size(); }
|
||||
};
|
||||
struct Prefab { std::string name; std::string GetPrefabName() { return name; } };
|
||||
struct Entity { Prefab* prefab; Prefab* GetPrefabData() { return prefab; } };
|
||||
struct InventoryStorageManagerComponent {
|
||||
Array<Entity*> raw;
|
||||
void GetItems(Array<Entity*>& out) { out = raw; }
|
||||
};
|
||||
const std::string SHELL = "mortar-shell";
|
||||
struct ERR_Inventory {
|
||||
''' + methods + '\n};\n' + count + r'''
|
||||
int main() {
|
||||
Prefab shell{SHELL}, watch{"watch"};
|
||||
Entity roundA{&shell}, roundB{&shell}, watchA{&watch}, missingPrefab{nullptr};
|
||||
InventoryStorageManagerComponent one{{&roundA,&roundA,nullptr,&roundB,&watchA,&watchA,&missingPrefab}};
|
||||
Array<Entity*> unique{&watchA};
|
||||
assert(ERR_Inventory::Items(&one,unique)==4);
|
||||
assert(unique[0]==&roundA && unique[1]==&roundB && unique[2]==&watchA && unique[3]==&missingPrefab);
|
||||
assert(InventoryShells(&one)==2); // Different shells share one prefab, not one identity.
|
||||
assert(one.raw.size()==7 && one.raw[0]==one.raw[1]); // Enumeration remains untouched.
|
||||
InventoryStorageManagerComponent two{{&roundB,&roundB,&roundA}};
|
||||
assert(ERR_Inventory::AppendUnique(&two,unique)==4); // Cross-inventory repeated identities.
|
||||
assert(ERR_Inventory::AppendUnique(nullptr,unique)==4);
|
||||
assert(ERR_Inventory::Items(nullptr,unique)==0 && unique.empty());
|
||||
assert(InventoryShells(nullptr)==0);
|
||||
InventoryStorageManagerComponent repeated{{&roundA,&roundA,&roundA}};
|
||||
assert(InventoryShells(&repeated)==1);
|
||||
InventoryStorageManagerComponent empty{{nullptr,&missingPrefab,&watchA}};
|
||||
assert(InventoryShells(&empty)==0);
|
||||
}
|
||||
''')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user