swayfx-mosaic/tooling/probes/step20c-targeted-diagnostic/test_diagnostic.py
fy59 ed9375797e test(mosaic): record targeted STEP20C partial closure
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.
2026-10-14 07:00:33 +02:00

144 lines
11 KiB
Python

"""Offline only: real A/G wire and IPC, synthetic native identity mutations."""
import copy,json,re,unittest,tempfile
from unittest.mock import patch
from diagnostic import *
TRACE='''SD_CREATE ns=101 node=0x1 source_node=0x2 source=0x3 tree=0x4 parent=0x5 locks_before=1 locks_after=2 dest=636,573 src=0,0,636,573 transform=0
SD_TREE ns=102 owner=10 tree=0x4 parent=0x5 content=0x6 saved_enabled=1 content_enabled=0
OPT_BUFFER_DRAW ns=103 output=HEADLESS-1 node=0x1 buffer=0x3 surface=(nil) global=-318,25 dest=636x573 source=636x573 draw=482,30,770,570
SD_SAMPLE ns=104 node=0x1 source=0x3 live_source=0x3 output=HEADLESS-1 global=-318,25 dest=636,573 src=0,0,636,573 transform=0 enabled=1 node_enabled=1 viewport=-800,0,800,600 timeline=(nil) point=0 direct=0
SD_ANCESTOR ns=105 sample_ns=104 node=0x1 output=HEADLESS-1 tree=0x5 global=-800,0 enabled=1 clip_valid=1 clip=30,30,740,540
Transaction 0xaa timed out (1 waiting)
SD_REMOVE ns=199 owner=10 tree=0x4 content=0x6
SD_NODE_DESTROY ns=200 node=0x1 source=0x3
SD_RELEASE ns=201 node=0x1 source=0x3 locks=0 gpu_complete=NOT_PROVEN
SD_BUFFER_DESTROY ns=202 node=0x1 source=0x3 gpu_complete=NOT_PROVEN
'''
class DiagnosticTests(unittest.TestCase):
def real(self,rid='saved-A'):
run=plans.DEFAULT_LAB/'runs'/rid/'run'
before=(run/'owner.log').read_text().split('COMMAND hold_next_configure DONE')[0]
full=(run/'owner.log').read_text();cut=full.index('COMMAND hold_next_configure DONE');after=full[cut:]
prior=int(re.findall(r'ACK ns=\d+ serial=(\d+)',before)[-1]);events=[json.loads(l) for l in (run/'events.jsonl').read_text().splitlines()]
request=next(e['ns'] for e in events if e['kind']=='ipc-request' and e['args']==['gaps inner current set 30'])
a=gate.read(run/'owner-owner-snapshot.json');b=gate.read(run/'saved-requested-pending-snapshot.json')
return [a,b,10,6,before,after,prior,request]
def test_real_A_G(self):
for rid in ('saved-A','saved-G'):
p=pending_geometry(*self.real(rid));self.assertEqual(p['held']['serial'],39);self.assertEqual(p['held']['requested_size'],[636,513])
def test_fictitious_gaps(self):
args=self.real();workspace(args[1]['tree'],'1')['gaps']={'inner':30}
with self.assertRaises(ValueError):pending_geometry(*args)
def test_wrong_workspace(self):
args=self.real();args[3]=999
with self.assertRaises(ValueError):pending_geometry(*args)
def test_wrong_owner(self):
args=self.real();args[2]=9
with self.assertRaises(ValueError):pending_geometry(*args)
def test_wrong_requested_size(self):
args=self.real();args[5]=args[5].replace('636x513','636x514')
with self.assertRaises(ValueError):pending_geometry(*args)
def test_unreceived_serial(self):
args=self.real();args[5]=args[5].replace('.configure(39)','.configure(40)')
with self.assertRaises(ValueError):pending_geometry(*args)
def test_ack_rejected(self):
args=self.real();args[5]+='\nxdg_surface#3.ack_configure(39)\n'
with self.assertRaises(ValueError):pending_geometry(*args)
def test_stale_command(self):
args=self.real();args[7]=10**18
with self.assertRaises(ValueError):pending_geometry(*args)
def test_native_positive(self):
self.assertEqual(saved_identity(TRACE,10,100,True)['gpu_completion'],'NOT PROVEN')
def test_native_mutations(self):
mutations=[TRACE.replace('source_node=0x2','source_node=0x1'),TRACE.replace('SD_SAMPLE ns=104 node=0x1','SD_SAMPLE ns=104 node=0x9'),TRACE.replace('live_source=0x3 output=HEADLESS-1','live_source=0x3 output=HEADLESS-2'),TRACE.replace('SD_SAMPLE ns=104 node=0x1 source=0x3','SD_SAMPLE ns=104 node=0x1 source=0x9'),TRACE.replace('OPT_BUFFER_DRAW ns=103','OPT_BUFFER_DRAW ns=203'),TRACE.replace('gpu_complete=NOT_PROVEN','gpu_complete=PROVEN'),TRACE.replace('SD_NODE_DESTROY','OTHER'),TRACE.replace('SD_CREATE','OTHER'),TRACE.replace('global=-318,25 dest=636,573','global=10,25 dest=636,573'),TRACE.replace('sample_ns=104','sample_ns=105'),TRACE.replace('SD_BUFFER_DESTROY ns=202','SD_BUFFER_DESTROY ns=103')]
for text in mutations:
with self.subTest(text=text),self.assertRaises(ValueError):saved_identity(text,10,100,True)
def test_creation_window_and_reuse_negatives(self):
mutations=[TRACE.replace('SD_CREATE ns=101','SD_CREATE ns=90'),
TRACE.replace('SD_CREATE ns=101','SD_CREATE ns=102'),
TRACE.replace('SD_TREE ns=102','SD_TREE ns=104'),
TRACE.replace('OPT_BUFFER_DRAW ns=103','OPT_BUFFER_DRAW ns=102'),
TRACE.replace('owner=10','owner=11'),
TRACE.replace('SD_CREATE ns=101', 'SD_CREATE ns=101',1)+'SD_CREATE ns=300 node=0x1 source_node=0x9 source=0x8 tree=0x7 parent=0x5\n',
TRACE+'SD_TREE ns=301 owner=11 tree=0x4 parent=0x5 content=0x6 saved_enabled=1 content_enabled=0\n']
for text in mutations:
with self.subTest(text=text),self.assertRaises(ValueError):saved_identity(text,10,100)
def test_removal_positive_synchronous_before_runner_observation(self):
identity=saved_identity(TRACE,10,100)
proof=saved_removal(TRACE,identity,210)
self.assertEqual(proof['nodes'][0]['ns'],'200')
self.assertEqual(proof['gpu_completion'],'NOT PROVEN')
def test_removal_exact_window_negatives(self):
identity=saved_identity(TRACE,10,100)
mutations=[TRACE.replace('SD_NODE_DESTROY ns=200','SD_NODE_DESTROY ns=90'),
TRACE.replace('SD_NODE_DESTROY ns=200 node=0x1','SD_NODE_DESTROY ns=200 node=0x9'),
TRACE.replace('SD_REMOVE ns=199 owner=10','SD_REMOVE ns=199 owner=11'),
TRACE.replace('SD_REMOVE ns=199 owner=10 tree=0x4','SD_REMOVE ns=199 owner=10 tree=0x9'),
TRACE.replace('content=0x6\nSD_NODE_DESTROY','content=0x9\nSD_NODE_DESTROY'),
TRACE.replace('Transaction 0xaa timed out (1 waiting)\n',''),
TRACE.replace('Transaction 0xaa timed out (1 waiting)\n','')+'Transaction 0xaa timed out (1 waiting)\n',
TRACE.replace('SD_NODE_DESTROY ns=200','SD_NODE_DESTROY ns=220'),
TRACE.replace('SD_REMOVE ns=199','SD_REMOVE ns=220'),
TRACE.replace('SD_NODE_DESTROY ns=200 node=0x1 source=0x3','SD_NODE_DESTROY ns=200 node=0x1 source=0x9')]
for text in mutations:
with self.subTest(text=text),self.assertRaises(ValueError):saved_removal(text,identity,210)
# A destruction emitted only by normal explicit close is too late.
with self.assertRaises(ValueError):saved_removal(TRACE,identity,199)
def test_removal_not_global_count(self):
identity=saved_identity(TRACE,10,100)
text=TRACE.replace('SD_NODE_DESTROY ns=200 node=0x1','SD_NODE_DESTROY ns=200 node=0x9')
text+='SD_NODE_DESTROY ns=201 node=0x8 source=0x3\n'
with self.assertRaises(ValueError):saved_removal(text,identity,210)
def test_absent_release_is_still_not_gpu_proof(self):
text='\n'.join(l for l in TRACE.splitlines() if 'SD_RELEASE' not in l and 'SD_BUFFER_DESTROY' not in l)
identity=saved_identity(text,10,100,True)
self.assertEqual(identity['gpu_completion'],'NOT PROVEN')
self.assertEqual(saved_removal(text,identity,210)['gpu_completion'],'NOT PROVEN')
def test_false_gpu_release_remains_rejected(self):
with self.assertRaises(ValueError):saved_identity(TRACE.replace('gpu_complete=NOT_PROVEN','gpu_complete=PROVEN'),10,100,True)
def budget(self):
state=copy.deepcopy(gate.read(plans.DEFAULT_LAB/'budget.json'))
# Model a fixed fourteen-token final state offline, without touching lab.
for rid,spec in plans.RUNS.items():
if rid not in state['runs']:
state['runs'][rid]=dict(campaign=spec['campaign'],planned=spec['limits'],reserved=dict.fromkeys(spec['limits'],0),consumed_ns=1,freeze_sha256='offline')
state['used']['run']+=1;state['campaign_runs'][spec['campaign']]+=1
return state
def test_budget_known_extension(self):
plans.RUNS[SPEC['id']]=SPEC
try:self.assertEqual(gate.validate(self.budget())['used']['run'],14)
finally:del plans.RUNS[SPEC['id']]
def test_budget_unknown_and_caps(self):
plans.RUNS[SPEC['id']]=SPEC
try:
state=self.budget()
for mutate in (lambda s:s['runs'].__setitem__('retry',copy.deepcopy(s['runs'][SPEC['id']])),lambda s:s['runs'][SPEC['id']]['planned'].__setitem__('capture',17),lambda s:s['used'].__setitem__('capture',175),lambda s:s['campaign_used'].__setitem__('saved',31),lambda s:s['campaign_runs'].__setitem__('saved',4)):
s=copy.deepcopy(state);mutate(s)
with self.assertRaises(ValueError):gate.validate(s)
finally:del plans.RUNS[SPEC['id']]
def test_registration_before_after_verify(self):
with tempfile.TemporaryDirectory(prefix='saved-diag-offline-') as folder:
path=Path(folder);(path/'diagnostic-registration.json').write_text(json.dumps(dict(scope=SCOPE,spec=SPEC)))
with patch.object(gate,'verify') as verify:
with registration(path):self.assertEqual(plans.RUNS[SPEC['id']],SPEC)
self.assertNotIn(SPEC['id'],plans.RUNS);self.assertEqual(verify.call_count,2)
def test_registration_duplicate_rejected(self):
with tempfile.TemporaryDirectory(prefix='saved-diag-offline-') as folder:
path=Path(folder);(path/'diagnostic-registration.json').write_text(json.dumps(dict(scope=SCOPE,spec=SPEC)))
with registration(path,False):
with self.assertRaises(ValueError):
with registration(path,False):pass
def test_missing_persisted_registration_rejected(self):
with tempfile.TemporaryDirectory(prefix='saved-diag-offline-') as folder:
with self.assertRaises(FileNotFoundError):
with registration(Path(folder),False):pass
def test_retry_build_token_rejected(self):
with tempfile.TemporaryDirectory(prefix='saved-diag-offline-') as folder:
token=Path(folder)/'build.token.json';gate.durable(token,dict(kind='build'),True)
with self.assertRaises(FileExistsError):gate.durable(token,dict(kind='build'),True)
def test_no_extra_buffer_ref_or_synchronization(self):
patch=(HERE/'saved-passive.patch').read_text();added='\n'.join(l[1:] for l in patch.splitlines() if l.startswith('+') and not l.startswith('+++'))
for token in ('wlr_buffer_lock(', 'wlr_buffer_unlock(', 'wlr_scene_buffer_set_buffer(', 'ack_configure(', 'wait_timeline(', 'read_pixels('):self.assertNotIn(token,added)
if __name__=='__main__':unittest.main(verbosity=2)