Initial import
This commit is contained in:
commit
dfcc43c72d
34 files changed
+3057
No files matched your search
Executable
+103
@@ -0,0 +1,103 @@
|
||||
"""End-to-end orchestration tests with EXPLICIT compiler test doubles.
|
||||
These tests validate failure propagation/artifact checks. They DO NOT compile Pascal.
|
||||
Synthetic source receipts/ELF/APK exist only in temporary directories.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json, os, shutil, subprocess, sys, tempfile, unittest
|
||||
from pathlib import Path
|
||||
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'tools'))
|
||||
from common import ROOT, COMMIT, sha256, git_blob, write_json
|
||||
from runner import PROFILES
|
||||
|
||||
MOCK=r'''#!/usr/bin/env python3
|
||||
# TEST DOUBLE. NOT A COMPILER. NEVER SHIPPED AS AN ANDROID ARTIFACT.
|
||||
import json,os,sys,zipfile
|
||||
from pathlib import Path
|
||||
sys.path.insert(0,str(Path(__file__).parent))
|
||||
from helpers import elf
|
||||
if '--version' in sys.argv:
|
||||
print('TEST_DOUBLE_NOT_A_REAL_COMPILER');raise SystemExit(0)
|
||||
if '--os=android' not in sys.argv or not any(a.startswith('--cpu=') for a in sys.argv):
|
||||
print('TEST ERROR: missing explicit Android target');raise SystemExit(97)
|
||||
cpu=next(a.split('=',1)[1] for a in sys.argv if a.startswith('--cpu='))
|
||||
t=json.loads(Path('target.json').read_text());ident=t['profile']
|
||||
if sys.argv[-1]=='compile':
|
||||
if ident==os.environ.get('PROBE_FAKE_FAIL'):
|
||||
print("KM_Test.pas(9,1) Fatal: Can't find unit Graphics used by KM_Test");raise SystemExit(1)
|
||||
if ident==os.environ.get('PROBE_FAKE_ZERO_NO_SO'):
|
||||
print('TEST DOUBLE: misleading zero return without output');raise SystemExit(0)
|
||||
Path('lib'+t['name']+'.so').write_bytes(elf(cpu))
|
||||
for unit in t['units']:
|
||||
Path(unit.lower()+'.ppu').write_bytes(b'PPU_TEST_DOUBLE')
|
||||
print('Compiling '+unit+'.pas (TEST DOUBLE ONLY)')
|
||||
elif sys.argv[-1]=='package':
|
||||
library=Path('lib'+t['name']+'.so');data=library.read_bytes()
|
||||
if ident==os.environ.get('PROBE_FAKE_OLD_APK'):data=data[:-1]+b'x'
|
||||
abi={'aarch64':'arm64-v8a','arm':'armeabi-v7a','x86_64':'x86_64'}[cpu]
|
||||
with zipfile.ZipFile(t['name']+'.apk','w') as z:
|
||||
z.writestr('AndroidManifest.xml','TEST DOUBLE ONLY')
|
||||
z.writestr('classes.dex',b'dex\n035\0TEST DOUBLE ONLY')
|
||||
z.writestr('lib/'+abi+'/'+library.name,data)
|
||||
else:raise SystemExit(98)
|
||||
'''
|
||||
|
||||
class PipelineTests(unittest.TestCase):
|
||||
def run_pipeline(self,variables=None,profiles='all',with_source=True):
|
||||
temp=tempfile.TemporaryDirectory();self.addCleanup(temp.cleanup);root=Path(temp.name)
|
||||
for folder in ('tools','config','code'):shutil.copytree(ROOT/folder,root/folder,ignore=shutil.ignore_patterns('__pycache__','toolchain.lock.json'))
|
||||
bindir=root/'mock-bin';bindir.mkdir()
|
||||
shutil.copyfile(Path(__file__).parent/'helpers.py',bindir/'helpers.py')
|
||||
(bindir/'castle-engine').write_text(MOCK.replace('#!/usr/bin/env python3', '#!'+sys.executable+' -S'));(bindir/'castle-engine').chmod(0o755)
|
||||
for tool in ('java','fpc','adb'):
|
||||
(bindir/tool).write_text('#!/bin/sh\necho TEST_DOUBLE_VERSION\n');(bindir/tool).chmod(0o755)
|
||||
(bindir/'apksigner').write_text('#!/bin/sh\necho TEST_DOUBLE_SIGNER\nexit 0\n');(bindir/'apksigner').chmod(0o755)
|
||||
if with_source:
|
||||
src=root/'vendor/kam_remake';src.mkdir(parents=True);files=[]
|
||||
for unit in sorted({u for p in PROFILES for u in p['units']}):
|
||||
p=src/'src'/f'{unit}.pas';p.parent.mkdir(exist_ok=True)
|
||||
p.write_text(f'unit {unit}; interface implementation end.\n')
|
||||
files.append({'path':p.relative_to(src).as_posix(),'size':p.stat().st_size,
|
||||
'sha256':sha256(p),'git_blob_sha1':git_blob(p.read_bytes())})
|
||||
write_json(src/'SOURCE_RECEIPT.json',{'schema':'TEST_DOUBLE_RECEIPT','commit':COMMIT,
|
||||
'repository':'reyandme/kam_remake','source_only':True,'files':files})
|
||||
env={**os.environ,'PATH':str(bindir)+os.pathsep+os.environ['PATH'],**(variables or {})}
|
||||
p=subprocess.run([sys.executable,'-S',str(root/'tools/runner.py'),'--local','--no-fetch','--profiles',profiles],
|
||||
cwd=root,env=env,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True,timeout=60)
|
||||
latest=json.loads((root/'reports/LATEST.json').read_text())
|
||||
result=json.loads((root/latest['directory']/'result.json').read_text())
|
||||
return p,result,root
|
||||
def test_all_stages_can_reach_build_gate_with_test_double(self):
|
||||
p,r,root=self.run_pipeline()
|
||||
self.assertEqual(p.returncode,0,p.stdout);self.assertEqual(r['gate'],'ANDROID_BUILD_GATE_PASS')
|
||||
self.assertEqual(len(r['profiles']),17);self.assertEqual(r['runtime'],'NOT_RUN')
|
||||
self.assertEqual(len(list((root/'dist').rglob('*.apk'))),5)
|
||||
self.assertIn('TEST_DOUBLE',r['toolchain']['castle']['output'])
|
||||
def test_core_failure_does_not_stop_later_probes(self):
|
||||
p,r,_=self.run_pipeline({'PROBE_FAKE_FAIL':'simulation'})
|
||||
self.assertEqual(p.returncode,1,p.stdout);self.assertEqual(r['gate'],'NOT_BUILDABLE_AS_IS')
|
||||
self.assertEqual(next(x for x in r['profiles'] if x['id']=='scripting')['compile_link']['status'],'PASS')
|
||||
self.assertEqual(next(x for x in r['profiles'] if x['id']=='simulation')['compile_link']['status'],'FAIL')
|
||||
def test_zero_exit_without_library_is_not_a_pass(self):
|
||||
p,r,_=self.run_pipeline({'PROBE_FAKE_ZERO_NO_SO':'core_types'},'core_types')
|
||||
self.assertEqual(p.returncode,1,p.stdout)
|
||||
self.assertEqual(r['profiles'][1]['compile_link']['status'],'FAIL')
|
||||
def test_stale_apk_library_rejected_end_to_end(self):
|
||||
p,r,_=self.run_pipeline({'PROBE_FAKE_OLD_APK':'integration'})
|
||||
self.assertEqual(p.returncode,1,p.stdout)
|
||||
self.assertEqual(next(x for x in r['profiles'] if x['id']=='integration')['package']['status'],'FAIL')
|
||||
self.assertTrue(any('stale' in e for e in next(x for x in r['profiles'] if x['id']=='integration')['errors']))
|
||||
def test_partial_selection_never_means_whole_architecture_ready(self):
|
||||
p,r,_=self.run_pipeline(profiles='core_types')
|
||||
self.assertEqual(p.returncode,2,p.stdout);self.assertEqual(r['gate'],'PARTIAL_SELECTION')
|
||||
def test_missing_sources_do_not_block_independent_platform_probe(self):
|
||||
p,r,_=self.run_pipeline(profiles='core_types',with_source=False)
|
||||
self.assertEqual(p.returncode,2,p.stdout);self.assertEqual(r['gate'],'BLOCKED')
|
||||
self.assertEqual(r['profiles'][0]['compile_link']['status'],'PASS')
|
||||
self.assertEqual(r['profiles'][1]['compile_link']['status'],'BLOCKED')
|
||||
def test_no_resource_data_in_evidence(self):
|
||||
import zipfile
|
||||
p,r,root=self.run_pipeline(profiles='platform')
|
||||
evidence=next((root/'reports/runs').rglob('evidence.zip'))
|
||||
with zipfile.ZipFile(evidence) as z:
|
||||
self.assertFalse(any(Path(n).suffix in ('.apk','.so','.dat','.map','.rxx') for n in z.namelist()))
|
||||
if __name__=='__main__':unittest.main()
|
||||
Reference in new issue
Block a user