#!/usr/bin/env python3 """Read-only historical replay and explicitly labelled synthetic negative tests. CONTRACT: exactly the live pure functions are exercised, without compositor launch. INVARIANT: old manifests/snapshots are never written; historical IDs stay here. """ import argparse import ast import copy import json from pathlib import Path import subprocess from PIL import Image from support import save, sha from ipc_predicates import (nodes, validate_reference, validate_outputs, workspace, measure_owner, scene_from_log, client_commit, validate_preflight, validate_camera) from pixels import analyze, compare, interior, painted_geometry, click_points, delivery, button_pair HIST = Path('/home/fy59/swayfx-mosaic-static-followup-nhwejz6u') OLD = Path('/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt') def check(): from verify import verify_inventory for base,pairs in [(HIST,[('source','5f9ab2b6d1ca23ef7743623d1e544237c8b70b62fe6de8164a46f1342e5a8ee2'),('evidence','41cc5e3d305d89e83314723e67d7116c36d1503dd35647894bc898c49343e1a5')]), (OLD,[('source','ed55bd418595ca977c022fa98917a899072cf3b6f0fb15457db823081046e797'),('evidence','e260ce5793ffc9a29ee4bfa2189663feeb6dc025bd46d3f5ddebccd225f3eef1')])]: for name,digest in pairs: manifest=base/'results'/(name+'-sha256.txt');assert sha(manifest)==digest verify_inventory(manifest) snap=json.loads((HIST/'results/preflight-snapshot.json').read_text()) text=(HIST/'run/compositor.log').read_text() ref=next(n for n in nodes(snap['tree']) if n.get('id')==7) target=next(n for n in nodes(snap['tree']) if n.get('id')==9) results=[] def rejected(name,fun): try:fun() except (AssertionError,KeyError,StopIteration,TypeError):results.append({'negative':name,'status':'REJECTED'}) else:raise AssertionError('invalid fixture accepted: '+name) validate_reference(snap,7,ref['pid']) assert ref['border']=='none' and ref['current_border_width']==2 for field,value in [('app_id','wrong'),('pid',1),('visible',False),('shell',None), ('border','normal'),('rect',dict(x=1,y=0,width=800,height=600)), ('window_rect',dict(x=0,y=0,width=799,height=600)), ('deco_rect',dict(x=0,y=0,width=800,height=25))]: s=copy.deepcopy(snap);r=next(n for n in nodes(s['tree']) if n.get('id')==7);r[field]=value rejected(field,lambda s=s:validate_reference(s,7,ref['pid'])) rejected('wrong con_id',lambda:validate_reference(snap,99,ref['pid'])) rejected('wrong workspace',lambda:validate_reference(snap,7,ref['pid'],'1')) rejected('wrong output',lambda:validate_reference(snap,7,ref['pid'],'2','HEADLESS-1')) rejected('actual H1 filler',lambda:validate_reference(snap,8,next(n for n in nodes(snap['tree']) if n.get('id')==8)['pid'])) s=copy.deepcopy(snap);workspace(s['tree'],'2')['nodes']=[] rejected('absent surface',lambda:validate_reference(s,7,ref['pid'])) for field,value in [('rect',dict(x=0,y=0,width=801,height=600)),('scale',1.25),('transform','90')]: s=copy.deepcopy(snap);next(o for o in s['outputs'] if o['name']=='HEADLESS-2')[field]=value rejected('output '+field,lambda s=s:validate_reference(s,7,ref['pid'])) # Earlier border-normal state is an actual IPC reply, not a manufactured state. early=None for line in (HIST/'run/events.jsonl').read_text().splitlines(): e=json.loads(line) if e.get('kind')=='ipc' and list(e.get('args',[]))==['-t','get_tree']: r=next((n for n in nodes(e['result']) if n.get('id')==7),None) if r and r.get('border')=='normal':early=e['result'];break assert early is not None, 'real earlier border-normal observation missing' rejected('actual earlier normal reference',lambda:validate_reference({**snap,'tree':early},7,ref['pid'])) scene=scene_from_log(text,'HEADLESS-1');h2=scene_from_log(text,'HEADLESS-2') measured=measure_owner(text,target);commit=client_commit((HIST/'run/target.log').read_text()) good=validate_preflight(snap,7,ref['pid'],9,target['pid'],scene,h2,measured,commit) calibration=json.loads((HIST/'results/camera-calibration.json').read_text()) validate_camera(calibration['before'],calibration['after'],calibration['delta'],scene) args=[snap,7,ref['pid'],9,target['pid'],scene,h2,measured,commit] for name,index,field,value in [('clip global',5,'global_clip',[0,0,800,600]), ('clip absent',5,'clip_valid',False),('camera',5,'scroll',0), ('scene owner',5,'workspace',999),('H2 clip',6,'clip_valid',True), ('content width',8,'width',635)]: bad=copy.deepcopy(args);bad[index][field]=value rejected(name,lambda bad=bad:validate_preflight(*bad)) for name,mutate in [('focus',lambda s:next(n for n in nodes(s['tree']) if n.get('id')==9).update(focused=False)), ('layout',lambda s:workspace(s['tree'],'1').update(layout='splith'))]: bad=copy.deepcopy(args);mutate(bad[0]);rejected(name,lambda bad=bad:validate_preflight(*bad)) rejected('camera correction',lambda:validate_camera(0,160,159,scene)) historical=Image.open(OLD/'run/h1-mosaic.png').convert('RGB') # The older static campaign used exactly this measured content continuation. paint=painted_geometry(historical,measured,commit) fixed=Image.new('RGB',(800,600),(48,48,56)) points=click_points(historical,fixed,measured) assert points['permitted']==(-159,311) and points['forbidden']==(100,311) assert analyze(fixed)['count']==0 and compare(fixed,fixed.copy())['equal'] edge=fixed.copy();edge.putpixel((799,599),(48,48,57)) diff=compare(fixed,edge);assert not diff['equal'] and diff['changed']==1 rejected('no target paint',lambda:interior(fixed)) sparse=historical.copy();sparse.putpixel((640,311),(0,0,0)) rejected('sparse paint',lambda:painted_geometry(sparse,measured,commit)) blocked=fixed.copy();blocked.putpixel((100,311),(0,0,0)) rejected('H2 point inaccessible',lambda:click_points(historical,blocked,measured)) # Positive and negatives below are SYNTHETIC, not new or historical delivery. pair=['BUTTON ns=1 serial=3 button=273 state=1','BUTTON ns=2 serial=4 button=273 state=0'] assert delivery(dict(target=pair,reference=[],filler=[]),'target') for name,lines in [('none',[]),('missing release',pair[:1]),('extra',pair+pair[:1]), ('malformed',pair+['BUTTON garbage']),('wrong code',[s.replace('273','272') for s in pair]), ('wrong order',pair[::-1]),('zero serial',[pair[0].replace('serial=3','serial=0'),pair[1]]), ('same serial',[pair[0],pair[1].replace('serial=4','serial=3')]), ('backwards time',[pair[0].replace('ns=1','ns=3'),pair[1]])]: rejected('SYNTHETIC button '+name,lambda lines=lines:button_pair(lines)) rejected('SYNTHETIC wrong recipient',lambda:delivery(dict(target=pair,reference=pair,filler=[]),'target')) src=OLD/'mosaic-source' # Source tokens are supporting semantic audit, not behavioral tests. checks=[('sway/commands/border.c',['set_border(container, B_NONE)','if (argc == 2)']), ('sway/ipc-json.c',['json_object_new_int(c->current.border_thickness)','c->pending.content_width','view_is_visible']), ('sway/commands/layout.c',['workspace && workspace->layout == L_MOSAIC','container = NULL;','workspace->layout = new_layout']), ('sway/desktop/transaction.c',['border_width = 0;','wlr_scene_tree_set_clip(child->layers.tiling, NULL)']), ('sway/desktop/output.c',['wlr_scene_node_coords','PI_SCENE output=','PI_OWNER output='])] sources=[] for name,tokens in checks: p=src/name;contents=p.read_text();assert all(t in contents for t in tokens),(p,tokens) sources.append({'file':str(p),'sha256':sha(p),'semantics_checked':tokens}) fields=[('outputs rect/scale/transform','sway/ipc-json.c','actual IPC','PASS','live reread'), ('view App ID/PID/con_id/ancestors/visible','sway/ipc-json.c','actual IPC','PASS','visibility is not pixels'), ('workspace/focus/layout','sway/ipc-json.c; sway/commands/layout.c','actual IPC','PASS','later focus live'), ('border/stored width/deco/window_rect','border.c; ipc-json.c; transaction.c','actual IPC plus earlier normal','PASS','effective RGB live'), ('camera scroll_x/scene tree/clip local/global','transaction.c; output.c; ipc-json.c','actual traces + calibration','PASS','no atomic generation asserted'), ('content dimensions/SHM colors','client-source/saved-client.c','actual COMMIT + historical PNG','PASS','new pixels live'), ('capture coordinates/painted click selection','pixels.py; virtual pointer source','HISTORICAL H1 + SYNTHETIC H2','PASS','new accessible surface verified live'), ('buttons/recipient/count/order/serial','saved-client.c:pointer_button; pointer-daemon.c','SYNTHETIC labelled cases','PASS','new actual client events required'), ('targeted splith return','sway/commands/layout.c','exact candidate source','PASS source audit','layout/clip/render/input live; NOT TESTED offline')] audit=[dict(field=f,source=s,historical_data=h,replay=r,remaining_uncertainty=u, invariant=f+' remains required live') for f,s,h,r,u in fields] runtime_assertions=[] runner=Path(__file__).with_name('run.py').read_text() for node in ast.walk(ast.parse(runner)): if isinstance(node,ast.Assert): runtime_assertions.append({'line':node.lineno,'expression':ast.get_source_segment(runner,node.test), 'authority':'see corresponding field/source table; operational assertions use owned files/processes', 'offline':'shared predicates and historical/synthetic tests above; environment identity checked by prepare', 'uncertainty':'live-only states require new observations; no retrospective PASS'}) audit={'fields':audit,'runner_assertions':sorted(runtime_assertions,key=lambda x:x['line'])} return {'reference':{'marker':'STATIC_REFERENCE_IPC_REPLAY=PASS','snapshot_sha256':sha(HIST/'results/preflight-snapshot.json'), 'accepted_width':2,'reference':good['reference'],'negatives':results}, 'preflight':{'marker':'STATIC_FIXTURE_PREFLIGHT_REPLAY=PASS','actual':good,'paint_HISTORICAL':paint, 'points_HISTORICAL_SYNTHETIC':points,'synthetic_input_tests':'PASS'}, 'audit':audit,'source_audit':sources} if __name__=='__main__': p=argparse.ArgumentParser();p.add_argument('--lab',type=Path);a=p.parse_args() result=check() for path in Path(__file__).parent.glob('*.py'):ast.parse(path.read_text(),filename=str(path)) subprocess.run(['python',str(Path(__file__).with_name('test-budget.py'))],check=True) if a.lab: from verify import verify_inventory verify_inventory(a.lab/'results/fixture-freeze-sha256.txt') save(a.lab,'ipc-replay',result['reference']);save(a.lab,'preflight-replay',result['preflight']) save(a.lab,'assertion-audit',result['audit']);save(a.lab,'source-audit',result['source_audit']) save(a.lab,'offline-gate',{'reference':'PASS','preflight':'PASS','AST':'PASS','ledger':'PASS','source_audit':'PASS'}) print(result['reference']['marker']);print(result['preflight']['marker']) print('PASS',len(result['reference']['negatives']),'negative cases; historical/synthetic evidence labelled')