92 lines
7.2 KiB
Python
92 lines
7.2 KiB
Python
"""Isolated-control provenance/evidence helper tests. Never invoke native runner."""
|
|
import hashlib
|
|
from pathlib import Path
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'tools'))
|
|
import headless_inventory_control as control
|
|
|
|
GUID='0FFD909E91FC329B'
|
|
PROJECT='GameProject { ID "ERRIC_Control" GUID "'+GUID+'" Dependencies { "58D0FB3206B6F859" } }\n'
|
|
def loaded(guids=('5614BBCCBB55ED1C','58D0FB3206B6F859',GUID)):
|
|
return '00 ENGINE : Loaded addons:\n'+''.join("00 ENGINE : gproj: '/private/%s/addon.gproj' guid: '%s'\n"%(i,g) for i,g in enumerate(guids))+'00 SCRIPT : Module: Game; defines: "WORKBENCH"\n'
|
|
def complete(errors=2):
|
|
return loaded()+''.join('00 GUI (E): '+control.EXPECTED_ERROR+'\n' for _ in range(errors))+'00 SCRIPT : ERR SMOKE PASS: vanilla inventory control: actual native Inventory20Menu opened\n00 SCRIPT : ERR SMOKE COMPLETE: vanilla inventory control\n'
|
|
|
|
class InventoryControl(unittest.TestCase):
|
|
def test_authored_fixture_has_exact_vanilla_only_dependency_and_hashes(self):
|
|
files,guid=control.fixture_inventory(control.FIXTURE)
|
|
self.assertEqual(guid,GUID);self.assertIn('README.md',files)
|
|
self.assertIn('Scripts/Game/ERRIC/ERRIC_InventoryControl.c',files)
|
|
for path,digest in files.items():self.assertEqual(digest,hashlib.sha256((control.FIXTURE/path).read_bytes()).hexdigest())
|
|
|
|
def test_fixture_refuses_extra_projects_dependencies_or_identity_aliases(self):
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
root=Path(temp);project=root/'addon.gproj';project.write_text(PROJECT)
|
|
control.fixture_inventory(root)
|
|
bads=[PROJECT.replace('58D0FB3206B6F859','2528466DE220D91C'), PROJECT.replace('"58D0FB3206B6F859"','"58D0FB3206B6F859" "2528466DE220D91C"'),PROJECT+'Dependencies { "58D0FB3206B6F859" }',PROJECT+'GUID "ABCDABCDABCDABCD"',PROJECT.replace('"58D0FB3206B6F859"','"58D0FB3206B6F859" arbitrary'),PROJECT.replace('Dependencies','Dependencies { Dependencies')]
|
|
bads.extend(PROJECT.replace(GUID,guid) for guid in ('5614BBCCBB55ED1C','58D0FB3206B6F859','2528466DE220D91C','5192138CB9D5B911','D31039C7C53E348A','FA804F411BDDF2C6','9AAEE9041D79809E'))
|
|
for bad in bads:
|
|
project.write_text(bad)
|
|
with self.subTest(bad=bad),self.assertRaises(ValueError):control.fixture_inventory(root)
|
|
project.write_text(PROJECT);(root/'nested').mkdir();extra=root/'nested/hidden.gproj';extra.write_text(PROJECT)
|
|
with self.assertRaises(ValueError):control.fixture_inventory(root)
|
|
extra.unlink();extra=root/'runtime.exe';extra.write_bytes(b'not source')
|
|
with self.assertRaises(ValueError):control.fixture_inventory(root)
|
|
|
|
def test_symlink_roots_files_and_directories_refused(self):
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
root=Path(temp)/'fixture';root.mkdir();(root/'addon.gproj').write_text(PROJECT)
|
|
alias=Path(temp)/'alias';alias.symlink_to(root,target_is_directory=True)
|
|
with self.assertRaises(ValueError):control.fixture_inventory(alias)
|
|
link=root/'linked.c';link.symlink_to(root/'addon.gproj')
|
|
with self.assertRaises(ValueError):control.fixture_inventory(root)
|
|
link.unlink();link.symlink_to(Path(temp),target_is_directory=True)
|
|
with self.assertRaises(ValueError):control.fixture_inventory(root)
|
|
|
|
def test_known_hidden_harness_remapping_does_not_launch(self):
|
|
source=(control.ROOT/'tools/headless_workbench.py').read_text()
|
|
with patch.object(control.subprocess,'run',side_effect=AssertionError('No native launch')):
|
|
mapped=control.harness_source(source)
|
|
self.assertIn('ERRIC_InventoryControlPlugin',mapped);self.assertIn('Worlds/ERRIC_Inventory.ent',mapped)
|
|
self.assertIn("'-errPlay', '1', '-erricInventoryControl', '1'",mapped)
|
|
self.assertNotIn('ERR_ValidateBaseWorldPlugin',mapped);self.assertNotIn('Worlds/ERR_Everon.ent',mapped)
|
|
compile(mapped,'isolated-harness','exec')
|
|
for token in ('ERR_ValidateBaseWorldPlugin','Worlds/ERR_Everon.ent',"'-errPlay', '1'"):
|
|
with self.assertRaises(ValueError):control.harness_source(source.replace(token,'CHANGED'))
|
|
|
|
def test_reproduction_keeps_both_raw_errors_and_never_claims_clean_runtime(self):
|
|
evidence=control.control_evidence(complete(),GUID)
|
|
self.assertTrue(evidence['only_vanilla_and_control_loaded']);self.assertTrue(evidence['stock_slot_error_reproduced'])
|
|
self.assertFalse(evidence['native_clean']);self.assertFalse(evidence['successor_or_release_qualified'])
|
|
self.assertEqual(evidence['target_gui_error_count'],2);self.assertTrue(evidence['target_gui_pair_observed']);self.assertEqual(evidence['other_engine_errors'],[])
|
|
self.assertEqual(evidence['native_report']['engine_errors'],['00 GUI (E): '+control.EXPECTED_ERROR]*2)
|
|
self.assertEqual(evidence['native_report']['smoke_completions'],1)
|
|
for count in (0,1,3):self.assertFalse(control.control_evidence(complete(count),GUID)['stock_slot_error_reproduced'])
|
|
|
|
def test_target_pair_is_additive_and_does_not_hide_other_engine_errors(self):
|
|
extra=['00 WORLD (E): stock vehicle '+str(i) for i in range(7)]
|
|
evidence=control.control_evidence(complete()+'\n'.join(extra)+'\n',GUID)
|
|
self.assertTrue(evidence['target_gui_pair_observed']);self.assertEqual(evidence['target_gui_error_count'],2)
|
|
self.assertEqual(evidence['other_engine_errors'],extra)
|
|
self.assertFalse(evidence['stock_slot_error_reproduced']);self.assertFalse(evidence['native_clean'])
|
|
for text in (complete(1),complete(3),complete().replace('Loaded addons:','Available addons:'),complete()+'ERR SMOKE FAIL: bad',complete()+'ERR SMOKE COMPLETE: duplicate'):
|
|
self.assertFalse(control.control_evidence(text,GUID)['target_gui_pair_observed'])
|
|
wrong_channel=control.control_evidence(complete().replace('GUI (E)','WORLD (E)'),GUID)
|
|
self.assertEqual(wrong_channel['target_gui_error_count'],0);self.assertFalse(wrong_channel['target_gui_pair_observed']);self.assertEqual(len(wrong_channel['other_engine_errors']),2)
|
|
|
|
def test_addon_isolation_uses_all_exact_loaded_blocks(self):
|
|
bads=[complete().replace('Loaded addons:','Available addons:'),complete().replace(loaded(),loaded(('5614BBCCBB55ED1C','58D0FB3206B6F859'))),complete().replace(loaded(),loaded(('5614BBCCBB55ED1C','58D0FB3206B6F859',GUID, '2528466DE220D91C'))),complete().replace(loaded(),loaded(('58D0FB3206B6F859','58D0FB3206B6F859',GUID))),complete()+loaded(('5614BBCCBB55ED1C','58D0FB3206B6F859','2528466DE220D91C')),complete().replace("00 ENGINE : gproj:","00 SCRIPT : gproj:",1)]
|
|
for bad in bads:
|
|
evidence=control.control_evidence(bad,GUID);self.assertFalse(evidence['only_vanilla_and_control_loaded']);self.assertFalse(evidence['stock_slot_error_reproduced'])
|
|
self.assertTrue(control.control_evidence(complete()+loaded(),GUID)['stock_slot_error_reproduced'])
|
|
|
|
def test_completion_cannot_hide_native_script_or_fixture_failure(self):
|
|
for suffix in ('\n00 SCRIPT : ERR SMOKE FAIL: vanilla inventory control: wrong body','\n00 SCRIPT (E): invalid operation','\n00 SCRIPT : Virtual Machine Exception','\n00 RESOURCES (E): unexpected resource error','\nCould not find endscreen','\nERR SMOKE COMPLETE: duplicate'):
|
|
self.assertFalse(control.control_evidence(complete()+suffix,GUID)['stock_slot_error_reproduced'],suffix)
|
|
self.assertFalse(control.control_evidence(complete().replace('ERR SMOKE COMPLETE:','NOT COMPLETE:'),GUID)['stock_slot_error_reproduced'])
|
|
|
|
if __name__=='__main__':unittest.main()
|