Complete BigTask
Sync changed files to Google Drive / sync (push) Successful in 1m14s

This commit is contained in:
admin_d committed 2026-09-14 19:06:26 +05:00
1 parent a5ee0bec73
commit 15a968a2f7
6 files changed
+290 -37

No files matched your search

+77 -5
View File
@@ -1,9 +1,10 @@
from __future__ import annotations
import hashlib, json, os, shutil, subprocess, sys, tempfile, unittest, zipfile
import hashlib, json, os, shutil, subprocess, sys, tempfile, unittest, xml.etree.ElementTree as ET, zipfile
from pathlib import Path
from unittest.mock import patch
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'tools'))
from common import ROOT, COMMIT, ProbeError, git_blob, safe_relative, sha256, write_json, run_command, classify
from source_resolution import build_source_resolution, load_source_contract, validate_source_contract
from source_fetch import selected, enumerate_blobs, verify
from source_audit import inventory
from generate import make_target, imports_source, asset_inventory, COMPILER_OPTIONS
@@ -22,6 +23,62 @@ for i,good in enumerate(('src/common/KM_Points.pas','Maps/Тест/Тест.map'
def test(self,good=good):self.assertEqual(safe_relative(good),good)
setattr(PathsTests,f'test_accept_path_{i:02d}',test)
class SourceContractTests(unittest.TestCase):
def contract(self, value):
temp = tempfile.TemporaryDirectory(); self.addCleanup(temp.cleanup)
path = Path(temp.name) / 'extra-paths.json'; path.write_text(json.dumps(value))
return path
def test_loads_normalized_targets_and_deduplicated_paths(self):
path = self.contract({'schema':'kam-source-resolution/1','adaptations':['src/common/KM_Example.pas'],
'source_paths':['tools','tools'],'library_paths':[str(ROOT/'config')]})
loaded = load_source_contract(path)
self.assertEqual(loaded['adaptation_targets'], ('src/common/KM_Example.pas',))
self.assertEqual(loaded['source_paths'], ((ROOT/'tools').resolve(),))
self.assertEqual(loaded['library_paths'], ((ROOT/'config').resolve(),))
def test_rejects_legacy_and_unsafe_contracts(self):
for value in ({'source_paths':[],'library_paths':[]},
{'schema':'kam-source-resolution/1','adaptations':['src\\bad.pas'],'source_paths':[],'library_paths':[]}):
with self.subTest(value=value), self.assertRaises(ProbeError): load_source_contract(self.contract(value))
class SourceOwnershipTests(unittest.TestCase):
def setUp(self):
self.tmp=tempfile.TemporaryDirectory();self.root=Path(self.tmp.name);self.vendor=self.root/'vendor';self.sparse=self.root/'port-source/kam_remake'
self.vendor.joinpath('src').mkdir(parents=True);self.vendor.joinpath('src/KM_Test.pas').write_text('unit KM_Test; interface implementation end.')
self.receipt={'files':[{'path':'src/KM_Test.pas'}]};self.inventory={'units':{'km_test':['src/KM_Test.pas']}}
def tearDown(self):self.tmp.cleanup()
def validate(self, contract):
with patch('source_resolution.ROOT',self.root):
return validate_source_contract(contract,self.vendor,self.receipt,self.inventory)
def test_registered_adaptation_has_vendor_identity_and_unit(self):
self.sparse.joinpath('src').mkdir(parents=True);self.sparse.joinpath('src/KM_Test.pas').write_text('unit km_test; interface implementation end.')
result=self.validate({'adaptation_targets':('src/KM_Test.pas',),'source_paths':(),'library_paths':()})
self.assertEqual(result['adaptations'][0]['unit_name'],'km_test');self.assertEqual(result['vendor_unit_keys'],frozenset({'km_test'}))
def test_rejects_unregistered_sparse_and_generic_shadowing(self):
self.sparse.mkdir(parents=True);self.sparse.joinpath('other.inc').write_text('x')
with self.assertRaises(ProbeError):self.validate({'adaptation_targets':(),'source_paths':(),'library_paths':()})
shutil.rmtree(self.sparse);generic=self.root/'generic';generic.mkdir();generic.joinpath('KM_Test.pas').write_text('not a unit')
with self.assertRaises(ProbeError):self.validate({'adaptation_targets':(),'source_paths':(generic,),'library_paths':()})
class CompilerSourceResolutionTests(unittest.TestCase):
def test_orders_deduplicates_and_guards_application_vendor_units(self):
with tempfile.TemporaryDirectory() as t:
root = Path(t); app = root/'app'; vendor = root/'vendor'; generic = root/'generic'; library = root/'library'
for path in (app, vendor/'src', vendor/'ext', generic, library): path.mkdir(parents=True, exist_ok=True)
(vendor/'src/KM_Vendor.pas').write_text('unit KM_Vendor; interface implementation end.')
first, second = root/'adaptations/z', root/'adaptations/a'
first.mkdir(parents=True); second.mkdir(parents=True)
validated = {'vendor_unit_keys': frozenset({'km_vendor'}), 'adaptations': (
{'target':'z.pas', 'project_file':first/'z.pas'}, {'target':'A.pas', 'project_file':second/'A.pas'},
{'target':'a2.pas', 'project_file':second/'a2.pas'}),
'source_paths': (generic, second), 'library_paths': (library,)}
result = build_source_resolution(validated, vendor, app)
self.assertEqual(result['adaptation_dirs'], (second.resolve(), first.resolve()))
self.assertEqual(result['compiler_source_paths'][:4],
(app.resolve(), second.resolve(), first.resolve(), generic.resolve()))
self.assertEqual(result['library_paths'], (library.resolve(),))
(app/'KM_VENDOR.pas').write_text('malformed declaration')
with self.assertRaises(ProbeError): build_source_resolution(validated, vendor, app)
class SourceSelectionTests(unittest.TestCase):
def test_known_git_blob(self):
self.assertEqual(git_blob(b'hello\n'),'ce013625030ba8dba906f756967f9e9ca394464a')
@@ -177,10 +234,25 @@ class GenerationTests(unittest.TestCase):
code,units=imports_source(PROFILES[1]);self.assertIn('KMPointRight(P)',code);self.assertIn('KM_Points',units)
def test_actual_stream_calls(self):
code,units=imports_source(PROFILES[2]);self.assertIn('TKMemoryStreamBinary.Create',code);self.assertIn('S.ReadW(U)',code)
def test_upstream_manifest_uses_resolved_compiler_order(self):
with tempfile.TemporaryDirectory() as t:
root=Path(t);vendor=root/'vendor';generic=root/'generic';library=root/'library';adaptation=root/'adaptation'
for path in (vendor/'src',generic,library,adaptation):path.mkdir(parents=True,exist_ok=True)
(vendor/'src/KM_Points.pas').write_text('unit KM_Points; interface implementation end.')
validated={'vendor_unit_keys':frozenset({'km_points'}),'adaptations':(
{'target':'src/KM_Points.pas','project_file':adaptation/'KM_Points.pas'},),
'source_paths':(generic,),'library_paths':(library,)}
p=root/'target';rec=make_target(p,PROFILES[1],'aarch64','id',vendor,validated)
self.assertEqual(rec['source_paths'][:3],[str((p/'code').resolve()),str(adaptation.resolve()),str(generic.resolve())])
xml=ET.parse(p/'CastleEngineManifest.xml')
self.assertEqual([node.attrib['value'] for node in xml.findall('./compiler_options/search_paths/path')][:3],
['code',os.path.relpath(adaptation,p).replace(os.sep,'/'),os.path.relpath(generic,p).replace(os.sep,'/')])
self.assertEqual([node.attrib['value'] for node in xml.findall('./compiler_options/library_paths/path')],
[os.path.relpath(library,p).replace(os.sep,'/')])
def test_manifest_no_standalone_target(self):
with tempfile.TemporaryDirectory() as t:
p=Path(t)/'target'
rec=make_target(p,PROFILES[0],'aarch64','test-build',None,{'source_paths':[],'library_paths':[]})
rec=make_target(p,PROFILES[0],'aarch64','test-build',None,None)
xml=(p/'CastleEngineManifest.xml').read_text()
self.assertNotIn('standalone_source',xml);self.assertNotIn('test_shims',xml)
self.assertIn('game_units="GameInitialize"',xml)
@@ -190,7 +262,7 @@ class GenerationTests(unittest.TestCase):
def test_decision_profile_uses_existing_generator_with_alternate_game_unit(self):
with tempfile.TemporaryDirectory() as t:
profile=next(x for x in PROFILES if x['id']=='oq_host_callback')
p=Path(t)/'target';rec=make_target(p,profile,'aarch64','id',None,{'source_paths':[],'library_paths':[]})
p=Path(t)/'target';rec=make_target(p,profile,'aarch64','id',None,None)
xml=(p/'CastleEngineManifest.xml').read_text()
self.assertIn('game_units="DecisionHostCallback"',xml)
self.assertEqual(rec['game_unit'],'DecisionHostCallback')
@@ -204,13 +276,13 @@ class GenerationTests(unittest.TestCase):
self.assertEqual([x['path'] for x in rows],['Maps/one.txt'])
def test_fresh_target_required(self):
with tempfile.TemporaryDirectory() as t:
with self.assertRaises(ProbeError):make_target(Path(t),PROFILES[0],'aarch64','id',None,{'source_paths':[],'library_paths':[]})
with self.assertRaises(ProbeError):make_target(Path(t),PROFILES[0],'aarch64','id',None,None)
def test_optional_assets_preserved(self):
with tempfile.TemporaryDirectory() as t:
r=Path(t)/'assets';r.mkdir();(r/'Data').mkdir();raw=bytes(range(256))
(r/'Data/sample.dat').write_bytes(raw)
entries=asset_inventory(r)
p=Path(t)/'target';make_target(p,PROFILES[0],'aarch64','id',None,{'source_paths':[],'library_paths':[]},r,entries)
p=Path(t)/'target';make_target(p,PROFILES[0],'aarch64','id',None,None,r,entries)
self.assertEqual((p/'data/game/Data/sample.dat').read_bytes(),raw)
def test_asset_symlinks_rejected(self):
with tempfile.TemporaryDirectory() as t: