Record exact-SHA scale and extended G samples, same-source blur measurements and retained negative saved/input results. Keep STEP20C NEXT and production multi-output unsupported; no production integration. Preserve immutable probe fixtures, offline regressions and compact artifact provenance. Fourteen consumed runs / 125 output captures, independent final audit accepted. Saved timeout/GPU lifetime, whole-H1 return, input after camera mutation and all-frame damage remain open.
168 lines
9.5 KiB
Python
168 lines
9.5 KiB
Python
"""Bounded offline validator regression tests and retained E3 evidence audit."""
|
|
import hashlib
|
|
import json
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from PIL import Image
|
|
import fx_validators as v
|
|
|
|
|
|
class FXTests(unittest.TestCase):
|
|
def test_fx_close_command_drives_actual_c_handler(self):
|
|
source = Path(__file__).with_name('fx-client.c').resolve()
|
|
lab_clients = Path('/home/fy59/swayfx-mosaic-targeted-7gge6h8b/clients')
|
|
harness = r'''#define main fx_client_program_main
|
|
#include "SOURCE_PATH"
|
|
#undef main
|
|
int main(void) {
|
|
mode = "glass";
|
|
running = 1;
|
|
close_received = false;
|
|
handle_command("close-extra\n");
|
|
if (!running || close_received) return 1;
|
|
handle_command("close");
|
|
if (!running || close_received) return 2;
|
|
top_close(NULL, NULL);
|
|
if (running || close_received) return 3;
|
|
running = 1;
|
|
layer_close(NULL, NULL);
|
|
if (running || close_received) return 4;
|
|
running = 1;
|
|
stop(SIGTERM);
|
|
if (running || close_received) return 5;
|
|
running = 1;
|
|
handle_command("close\n");
|
|
if (running || !close_received) return 6;
|
|
return 0;
|
|
}
|
|
'''.replace('SOURCE_PATH', str(source))
|
|
cflags = shlex.split(subprocess.run(
|
|
['pkg-config', '--cflags', '--libs', 'wayland-client'],
|
|
check=True, capture_output=True, text=True).stdout)
|
|
with tempfile.TemporaryDirectory(prefix='fx-close-test-') as temp:
|
|
harness_path = Path(temp) / 'fx-close-harness.c'
|
|
binary = Path(temp) / 'fx-close-harness'
|
|
harness_path.write_text(harness)
|
|
compile_argv = [
|
|
'cc', '-std=c17', '-O2', '-I', str(lab_clients),
|
|
str(harness_path), str(lab_clients / 'xdg-shell-protocol.c'),
|
|
str(lab_clients / 'layer-shell-protocol.c'), '-o', str(binary),
|
|
*cflags,
|
|
]
|
|
subprocess.run(compile_argv, check=True, capture_output=True, text=True)
|
|
result = subprocess.run([str(binary)], check=False, capture_output=True, text=True)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertEqual(result.stderr.count('CLOSE_RECEIVED '), 1)
|
|
self.assertIn('CLOSE_RECEIVED ', result.stderr)
|
|
|
|
def test_whole_difference_survives_exact_center(self):
|
|
a=Image.new('RGB',(8,8)); b=a.copy(); b.putpixel((7,7),(2,3,4))
|
|
result=v.restoration(a,b,b,{'center':[1,1,4,4]})
|
|
self.assertEqual(result['wholeH1']['pixels'],1)
|
|
self.assertEqual(result['wholeH1']['bbox'],[7,7,8,8])
|
|
self.assertTrue(result['rois']['center']['AA']['exact'])
|
|
self.assertEqual(result['wholeH1']['perchannel'][2]['total_abs'],4)
|
|
|
|
def test_masks_negative_and_padding_rejected(self):
|
|
a=Image.new('RGB',(8,8))
|
|
for box in ([-1,0,4,4],[0,0,9,4],[0,0,0,4]):
|
|
with self.assertRaises(ValueError):v.diff(a,a,box)
|
|
with self.assertRaises(ValueError):v.diff(a,a,mask=[1,1,7,7])
|
|
|
|
def test_classic_requires_nonempty_actual_filter_and_same_source(self):
|
|
a=Image.new('RGB',(8,8)); b=Image.new('RGB',(8,8),(80,80,80))
|
|
for x in range(8):
|
|
for y in range(8):a.putpixel((x,y),(255,255,255) if x%2 else (0,0,0))
|
|
for same,path,witness in ((False,{'nonempty_region':True},True),
|
|
(True,{'nonempty_region':False},True),(True,{'nonempty_region':True},False)):
|
|
self.assertEqual(v.classic(a,b,[0,0,8,8],same,path,witness)['status'],'CLASSIC_BLUR_NOT_EXERCISED')
|
|
self.assertEqual(v.classic(a,a,[0,0,8,8],True,{'nonempty_region':True},True)['status'],'CLASSIC_BLUR_NOT_EXERCISED')
|
|
self.assertEqual(v.classic(a,b,[0,0,8,8],True,{'nonempty_region':True},True)['status'],'EXERCISED')
|
|
|
|
def test_unexercised_cache_rejected(self):
|
|
self.assertEqual(v.cache('')['status'],'NOT OBSERVABLE')
|
|
self.assertEqual(v.cache('OPT_BLUR_USE output=HEADLESS-1 optimized=1 chosen=(nil)')['status'],'CACHE_NOT_EXERCISED')
|
|
trace='OPT_DIRTY_SET node=0xa enabled=1 size=800x600\nOPT_REGEN_ATTEMPT output=HEADLESS-1 node=0xa dirty=1\nOPT_REGEN_RESULT output=HEADLESS-1 node=0xa result=1\nOPT_DIRTY_CLEAR output=HEADLESS-1 node=0xa dirty=0\nOPT_BLUR_USE output=HEADLESS-1 optimized=1 chosen=0xb'
|
|
self.assertEqual(v.cache(trace)['status'],'NATIVE_OBSERVED')
|
|
self.assertEqual(v.cache(trace.replace('node=0xa result=1','node=0xc result=1'))['status'],'CACHE_NOT_EXERCISED')
|
|
|
|
def test_false_normalclose_rejected(self):
|
|
log='CLIENT_COMMIT ns=1\nRELEASE ns=2 buffer=3\nCLOSE_RECEIVED ns=3\nOBJECTS_DESTROYED ns=4\nEXIT ns=5 error=0 commits=1 normalclose=1 releases=1\n'
|
|
self.assertEqual(v.normalclose(log,0,True,True)['status'],'PASS')
|
|
for text,code,alive,gone in ((log,1,True,True),(log,0,False,True),(log,0,True,False),
|
|
(log.replace('error=0','error=32'),0,True,True),
|
|
(log.replace('OBJECTS_DESTROYED','CLIENT_COMMIT'),0,True,True),
|
|
(log.replace('RELEASE ns=2 buffer=3\n',''),0,True,True)):
|
|
self.assertEqual(v.normalclose(text,code,alive,gone)['status'],'FAIL')
|
|
|
|
|
|
def audit(historical, lab):
|
|
"""Read HIST only; write new offline evidence solely below the supplied LAB."""
|
|
hist, lab=Path(historical).resolve(),Path(lab).resolve()
|
|
v.require(hist!=lab and not lab.is_relative_to(hist),'separate new LAB')
|
|
out=lab/'results'/'fx-offline';out.mkdir(parents=True,exist_ok=True)
|
|
results={}
|
|
for variant in ('pristine','mosaic'):
|
|
run=hist/'matrix'/('E3-'+variant)/'run'
|
|
result=v.validate_run(run)
|
|
geom=json.loads((run/'glass-geometry.json').read_text())
|
|
origin=geom['origin']; win=geom['owner']['window_rect']
|
|
x=max(-800,origin[0]);r=min(0,origin[0]+win['width']);y=max(0,origin[1]);t=min(600,origin[1]+win['height'])
|
|
center=[(x+r)//2+800,(y+t)//2];roi=[center[0]-32,center[1]-32,center[0]+32,center[1]+32]
|
|
result['historical_center_reconstructed_from_retained_geometry']=roi
|
|
result['historical_center']=v.diff(run/'optimized-A-H1.png',run/'optimized-A-return-H1.png',roi)
|
|
events=[json.loads(l) for l in (run/'events.jsonl').read_text().splitlines()]
|
|
close=[e for e in events if e['kind']=='client-request' and e.get('command','').startswith('close')]
|
|
exitrows={name:[l for l in (run/(name+'.log')).read_text().splitlines() if l.startswith('EXIT ')] for name in ('glass','background')}
|
|
terminal=json.loads((run/'terminal-ipc.json').read_text())
|
|
trace=(run/'compositor.log').read_text(errors='replace')
|
|
result['shutdown_chronology']=dict(last_capture_ns=max(e['end_ns'] for e in events if e['kind']=='capture'),
|
|
explicit_client_close_requests=close,client_exits=exitrows,terminal_ipc=terminal,
|
|
compositor_shutdown_lines=[l for l in trace.splitlines() if any(k in l.lower() for k in ('shutting down','destroying','exiting'))][-12:],
|
|
inference='error32 after failed equality and runner teardown; normal close NOT EXERCISED; GPU/client lifetime defect NOT PROVEN',
|
|
exact_native_compositor_exit_ns='NOT OBSERVABLE in retained log; stderr textual shutdown order alone is insufficient')
|
|
result['input_files']={p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(run.iterdir()) if p.is_file()}
|
|
(out/(variant+'.json')).write_text(json.dumps(result,indent=2)+'\n');results[variant]=result
|
|
(out/'AUDIT.md').write_text('''# Experimental retained E3 FX audit
|
|
|
|
HIST is read-only. Shared validators report complete RGB differences, including
|
|
bbox and per-channel count/sum/max. Central exact RGB does not establish whole
|
|
H1 restoration. Old client exits error32 follow runner teardown after the
|
|
whole-image predicate failed; no explicit normal close was sent. Native exact
|
|
compositor-exit ns is absent, so no precise GPU lifetime chronology is invented.
|
|
|
|
The old filler0 is static-reference (opaque XRGB 0x00303038), under floating
|
|
glass at (-650,200); it hides the striped wallpaper. The classic off/on entire
|
|
H1/H2 images are identical. Actual classic filtering was NOT EXERCISED. The
|
|
new client substitutes a transparent ARGB filler and retains source bytes.
|
|
Glass ARGB 0x88442010 has alpha 136 and RGB 68/32/16 <= alpha (premultiplied).
|
|
Background XRGB has 8-pixel alternating stripes. No opaque region is requested
|
|
by the client. Background is a layer-shell background on HEADLESS-1; floating
|
|
glass is above tiled transparent filler. RGB off/on comparison with immutable
|
|
sources distinguishes filter activity from compositing alone.
|
|
|
|
Source-selected blur: sway/desktop/output.c could_container_overlap returns
|
|
true for floating/animated owners; should_optimize_blur = blur_xray OR NOT
|
|
could_container_overlap. SceneFX fx_pass.c fx_render_pass_add_blur calls
|
|
get_main_buffer_blur for classic, then logs chosen before final texture draw.
|
|
A chosen pointer proves neither nonempty final filter region nor pixel effect.
|
|
Classic EXERCISED requires same SHM source and actual changing RGB plus reduced
|
|
adjacent contrast in the prospectively fixed center. Without that evidence the
|
|
status remains CLASSIC_BLUR_NOT_EXERCISED. OPT native events are cache-only;
|
|
client wire buffer IDs cannot be equated to output/cache pointers. Aggregate
|
|
native trace presence cannot establish causal A/B/A dirty chronology.
|
|
|
|
No compositor launched. Complete frame/damage/GPU lifetime is NOT PROVEN.
|
|
''')
|
|
return {k:dict(wholeH1=r['optimized']['wholeH1'],center=r['historical_center'],
|
|
classic=r['classic_H1']) for k,r in results.items()}
|
|
|
|
|
|
if __name__=='__main__':
|
|
if len(sys.argv)==4 and sys.argv[1]=='--audit':
|
|
print(json.dumps(audit(sys.argv[2],sys.argv[3]),indent=2))
|
|
else:unittest.main()
|