"""Shared offline/live predicates: facts precede scientific classification. CONTRACT: full RGB comparisons have no mask/tolerance; grabs require real logs. INVARIANT: dismissal, client destruction, clipping and layout remain distinct. """ import re from model import Rect,Space,Provenance,Output,popup,crossing,local_clip_to_global,require from pixels import color_geometry,COLORS from PIL import Image,ImageChops class Defect(RuntimeError): """A directly measured scientific contract failure, distinct from fixture ambiguity.""" def paint(im,target): im=im.convert('RGB');assert im.size==(800,600) mask=Image.new('L',im.size);mask.putdata([255 if p==COLORS[target] else 0 for p in im.get_flattened_data()]) return dict(count=sum(mask.histogram()[1:]),bbox=mask.getbbox()) def point(im,target,origin): box=paint(im,target)['bbox'];assert box,('no measured paint',target) # WHY: A may be occluded by B; bbox center alone is not a valid hit point. x1,y1,x2,y2=box for y in range(y1+10,y2-10,5): for x in range(x1+10,x2-10,5): if all(im.getpixel((xx,yy))==COLORS[target] for yy in range(y-8,y+9) for xx in range(x-8,x+9)): return [x+origin,y] raise AssertionError('no solid measured interior '+target) def equal(a,b): assert a.size==b.size==(800,600) rows=[(i%800,i//800,x,y) for i,(x,y) in enumerate(zip(a.get_flattened_data(),b.get_flattened_data())) if x!=y] return dict(equal=not rows,changed=len(rows),bbox=ImageChops.difference(a,b).getbbox(),pixels=rows) def reference(a,b): result=equal(a,b) assert result['equal'] and all(c==(48,48,56) for c in a.get_flattened_data()),'unstable reference' return result def configured(text,name): rows=re.findall(rf'{name} ROLE_CONFIGURE x=(-?\d+) y=(-?\d+) w=(\d+) h=(\d+)',text) assert rows,(name,'no configure') return list(map(int,rows[-1])) def geometry(text,origin,provenance=None,axis=0,boundary=0): a=configured(text,'POPUP_A');b=configured(text,'POPUP_B') require(provenance is not None,'popup-owner-provenance-required') p=provenance top=Rect(Space.GLOBAL,*origin,0,0,p) ids={n:list(map(int,re.search(rf'{n} CREATE wl=(\d+) xdg=(\d+) role=(\d+)',text).groups())) for n in ('TOPLEVEL','POPUP_A','POPUP_B')} result={} parent=top for key,name,parent_name,c in [('A','POPUP_A','TOPLEVEL',a),('B','POPUP_B','POPUP_A',b)]: parent_id=int(re.search(rf'{name} CREATE .*parent_xdg=(\d+)',text)[1]) ack=int(re.findall(rf'{name} XDG_CONFIGURE serial=(\d+) ack',text)[-1]) parent=popup(parent,Rect(Space.POPUP,*c,p),parent_id,ids[parent_name][1],ack,name+' MAP ' in text) result[key]=parent.values() result.update(configured_A=a,configured_B=b,origin=origin,provenance=dict(owner=p.owner,workspace=p.workspace,generation=p.generation,evidence=p.evidence,state=p.state), crossing_axis=axis,crossing_boundary=boundary,crossing={key:(result[key][axis]=2 assert request.start() POPUP_A -> POPUP_B',status='PASS') def pair(lines,target): events=[] for line in lines: m=re.fullmatch(r'POINTER_BUTTON target=(\w+) serial=(\d+) button=(\d+) state=(\d+)',line);assert m events.append([m[1],*map(int,m.groups()[1:])]) if len(events)!=2 or any(e[0]!=target for e in events) or [e[2:] for e in events]!=[[273,1],[273,0]]: raise Defect('wrong actual button recipient/pair: '+str(events)) assert 0