from pathlib import Path import hashlib,json,subprocess,datetime,re R=Path(__file__).resolve().parent.parent;T=R/'.local/native-qa/lifecycle-palette-20260913-01';S=R/'.local/native-qa/studio-dd39e1f' SOURCE='dd39e1fdc13f04f18975a70736d2ed7f752a6df2';FULL='e44f07565cc7599d5c5a15a6b5599b4f5d105718';MID='f55519317214c972ac3ba04e0ec82501eea4e1f6';TEST='crates/oa-desktop/src/style/tests/readonly.rs' def sha(b):return hashlib.sha256(b).hexdigest() def read(p):return json.loads(p.read_bytes()) def check(p,x): b=p.read_bytes();assert sha(b)==x['sha256'],str(p) if 'bytes' in x:assert len(b)==x['bytes'],str(p) def git(*a,stdin=None):return subprocess.check_output(['git',*a],cwd=R,input=stdin) m=read(S/'manifest.json');l=read(T/'launch.json');c=read(T/'closure.json') assert git('rev-parse','HEAD').decode().strip()==SOURCE assert m['source']==l['source']==SOURCE and m['full_test_source']==l['full_test_source']==FULL assert l['full_suite_reuse']==m['full_suite_reuse'] and m['full_suite_reuse']['policy']=='exact-readonly-iterator-testfix-dd39-v1' assert c['pid']==l['pid'] and c['exit']==0 and l['utc']()\n .0\n .iter()\n' assert before.count(needle)==2 and before.replace(needle,replacement)==mid w=mid for call in [b'near(pixel, style.selection, 3)',b'near(pixel, style.value, 12)']: assert w.count(call)==1;w=w.replace(call,call.replace(b'near(pixel',b'near(*pixel')) assert w==after for src,rows in [(SOURCE,m['inputs']),(FULL,read(S/'full-e44f075.inputs.json')['inputs'])]: assert len(rows)==538 and len({x['path'] for x in rows})==538 blob=git('cat-file','--batch',stdin=''.join(f"{src}:{x['path']}\n" for x in rows).encode());off=0 for x in rows: end=blob.index(b'\n',off);_,typ,n=blob[off:end].split();n=int(n);assert typ==b'blob';b=blob[end+1:end+1+n] assert n==x['bytes'] and sha(b)==x['sha256'];off=end+n+2 if src==SOURCE:assert (R/x['path']).read_bytes()==b,x['path'] assert off==len(blob) for name,x in m['artifacts'].items():check(S/name,x) assert m['artifacts']['libunwind.dll']['sha256']=='aa282fa47bb2c4e210cea5e470cdbdca58568f0be0bc73e404d86ab9cd95ef6a' for mode,name in [('full','full-e44f075'),('style','palette-dd39e1f'),('clippy','clippy-dd39e1f'),('gui','gui-dd39e1f')]: result=read(S/(name+'.result.json'));assert result==m['results'][mode] assert result['exit']==0 and not result['changed_inputs'] and result['source']==(FULL if mode=='full' else SOURCE) if mode in ['full','style']: summaries=re.findall(r'test result: ok\. (\d+) passed; (\d+) failed; (\d+) ignored; (\d+) measured; (\d+) filtered out;', (S/(name+'.log')).read_text()) assert summaries==([('874','0','0','0','0')] if mode=='full' else [('10','0','0','0','864')]) core=read(T/'topology-admission-42511f0-r1/verified-current-core-config-inputs.json');assert len(core)==110 current={x['path']:x for x in m['inputs']} for x in core:assert x==current[x['path']];check(R/x['path'],x) for name,key in [('stage-palette-gui-testfix.py','stage_verifier'),('lifecycle-palette-native-testfix-session.py','launcher'),('lifecycle-palette-native-session.py','original_launcher')]:check(R/'.local'/name,l['provenance'][key]) check(S/'manifest.json',l['provenance']['stage_manifest']);check(T/'preparation.json',l['provenance']['preparation_manifest']) assert (T/'stdout.log').stat().st_size==(T/'stderr.log').stat().st_size==0 o={'utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'source':SOURCE,'full_source':FULL,'raw_current_inputs_verified':538,'raw_full_inputs_verified':538,'exact_test_only_delta_verified':True,'core_inputs_verified':110,'staged_artifact_count_verified':len(m['artifacts']),'stage_manifest_sha256':sha((S/'manifest.json').read_bytes()),'exe':m['artifacts']['arcadia-studio.exe'],'runtime':m['artifacts']['libunwind.dll'],'closed_native_exit':c['exit'],'empty_native_logs':True,'scope':'Read-only source/raw blob/current disk/staged artifact/closed gate verification. No compiler/native invocation; full874 remains e44 with exact approved dd39 test-only delta.'} with (R/'.local/native-dd39-source-binding-20260913.json').open('x',encoding='utf-8') as f:json.dump(o,f,indent=2) print(json.dumps(o,indent=2))