Files
TheTree_mock/tools/test_p08_normal_placement_evidence.py
T

1106 lines
58 KiB
Python

#!/usr/bin/env python3
"""Whole-checker positive and semantic corruption tests; synthetic proof only."""
import copy
import re
import importlib.util
import json
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import patch
from types import SimpleNamespace
import shutil
import contextlib
import io
import os
import gzip
import hashlib
import struct
sys.dont_write_bytecode = True
ROOT = Path(__file__).resolve().parents[1]
def module(name, path):
spec = importlib.util.spec_from_file_location(name, path)
value = importlib.util.module_from_spec(spec)
spec.loader.exec_module(value)
return value
C = module('placement_checker_test', ROOT/'tools/check_p08_normal_placement_evidence.py')
F = module('placement_synthetic_fixture', ROOT/'tests/fixtures/p08-03/checker_fixture.py')
D = module('placement_collector_test', ROOT/'tools/run_p08_normal_placement.py')
def check_passive_endpoint_types(passive, placement):
"""Focused source contract; native parsing and startup remain pending."""
if not re.search(r'^extends RefCounted\s*$', placement, re.M):
raise ValueError('placement inheritance')
observer = passive.split('class InputObserver extends Node:', 1)[1].split('\nfunc ', 1)[0]
field = re.search(r'^\tvar endpoint:\s*(\w+)', observer, re.M)
if not field or field[1] not in ('RefCounted', 'Object', 'Variant'):
raise ValueError('incompatible RefCounted endpoint')
if 'endpoint != null' not in observer:
raise ValueError('missing null guard')
if not re.search(r'input_observer\.endpoint\s*=\s*self', passive):
raise ValueError('missing root endpoint')
modal = passive.split('func button(', 1)[1].split('\nfunc ', 1)[0]
if not re.search(r'input_observer\.endpoint\s*=\s*null', modal):
raise ValueError('modal endpoint must remain null')
if 'input_observer.queue_free()' not in modal or 'input_observer = old_observer' not in modal:
raise ValueError('modal cleanup/restore')
if 'null_modal_receipts_only' not in passive:
raise ValueError('missing modal attribution')
class PassiveTypingTests(unittest.TestCase):
def setUp(self):
self.passive = (ROOT/'tests/fixtures/p08-03/passive_placement.gd').read_text()
self.placement = (ROOT/'tests/fixtures/p08-03/placement.gd').read_text()
def test_actual_fixture_types(self):
check_passive_endpoint_types(self.passive, self.placement)
def test_node_endpoint_rejected(self):
with self.assertRaisesRegex(ValueError, 'incompatible RefCounted'):
check_passive_endpoint_types(self.passive.replace('endpoint: RefCounted', 'endpoint: Node'), self.placement)
def test_missing_guard_rejected(self):
with self.assertRaisesRegex(ValueError, 'null guard'):
check_passive_endpoint_types(self.passive.replace('endpoint != null', 'true'), self.placement)
def test_modal_dispatch_rejected(self):
with self.assertRaisesRegex(ValueError, 'modal endpoint'):
check_passive_endpoint_types(self.passive.replace('input_observer.endpoint = null', 'input_observer.endpoint = self'), self.placement)
def test_modal_cleanup_rejected(self):
with self.assertRaisesRegex(ValueError, 'cleanup/restore'):
check_passive_endpoint_types(self.passive.replace('input_observer.queue_free()', 'pass'), self.placement)
class EvidenceTests(unittest.TestCase):
def setUp(self):
scratch = ROOT/'tests/fixtures/p08-03/.scratch'
scratch.mkdir(exist_ok=True)
self.temp = tempfile.TemporaryDirectory(dir=scratch)
self.addCleanup(self.temp.cleanup)
self.root, self.out = F.make(Path(self.temp.name), ROOT, C)
def edit(self, path, callback):
p = self.out/path
data = json.loads(p.read_text())
callback(data)
F.put(p, data)
F.repin(self.out)
def reject(self, text):
errors = C.check(self.root, self.out)
self.assertTrue(errors, 'corrupted proof accepted')
self.assertIn(text, ' '.join(errors))
def test_positive_whole_checker(self):
self.assertEqual([], C.check(self.root, self.out))
def edit_os_request(self, folder, callback):
self.edit(folder+'/request.json', callback)
self.edit(folder+'/ack.json', lambda d: d.update(request_sha256=C.sha(self.out/(folder+'/request.json'))))
def test_reveal_missing_thumb(self):
self.edit_os_request('os-pair/supported_connected-1', lambda d: d.pop('thumb_rect'))
self.reject('unverified palette reveal')
def test_reveal_missing_bar(self):
self.edit_os_request('os-pair/supported_connected-1', lambda d: d.pop('bar'))
self.reject('unverified palette reveal')
def test_reveal_not_visible(self):
self.edit('os-pair/supported_connected-1/reveal.json', lambda d: d.update(visible_after=False))
self.reject('unverified palette reveal')
def test_reveal_target_outside(self):
self.edit('os-pair/supported_connected-1/reveal.json', lambda d: d.update(target_rect=[110,0,20,20]))
self.reject('unverified palette reveal')
def test_reveal_unchanged_scroll(self):
self.edit('os-pair/supported_connected-1/reveal.json', lambda d: d.update(scroll_after=0))
self.reject('palette reveal direction mismatch')
def test_reveal_opposite_scroll(self):
self.edit('os-pair/supported_connected-1/reveal.json', lambda d: d.update(scroll_after=-1))
self.reject('palette reveal direction mismatch')
def test_reveal_next_wrong_node(self):
self.edit_os_request('os-pair/supported_connected-2', lambda d: d.update(node='/other'))
self.reject('unverified palette reveal')
def test_reveal_next_not_button(self):
self.edit_os_request('os-pair/supported_connected-2', lambda d: d.update(action='framing'))
self.reject('unverified palette reveal')
def terminal(self, sequence):
F.put(self.out/'os-pair/writer-terminal.json', {'pid':303, 'role':'writer',
'sequence':sequence, 'stem':'writer-request', 'reason':'timeout'})
F.repin(self.out)
def test_terminal_failure(self):
sequences = [C.load(p)['sequence'] for p in (self.out/'os-pair').glob('*/request.json')]
self.terminal(max(sequences))
self.reject('terminal OS failure')
def test_terminal_continuation(self):
self.terminal(1)
self.reject('terminal OS continuation')
def test_os_sequence_gap(self):
self.edit_os_request('os-pair/supported_connected-2', lambda d: d.update(sequence=999))
self.reject('OS request sequence gap')
def action_row(self, label):
rows = C.load(self.out/'pair/writer-actions.json')['actions']
return copy.deepcopy(next(row for row in rows if row['id'] == label))
def action_check(self, row, message=None, mutate_state=None):
def typed(name):
node = C.load(self.out/('pair/'+name+'.json'))
if mutate_state and name == row['after']:
mutate_state(node)
return node
if message:
with self.assertRaisesRegex(ValueError, message):
C.action_gate(row, typed, self.root)
else:
C.action_gate(row, typed, self.root)
def test_agreeing_preview_refusal(self):
row = self.action_row('missing_connection')
self.assertEqual(row['status'], '配置不可: '+row['report']['reasons'][0]['message'])
self.action_check(row)
def test_connection_status_corruptions(self):
for status in ('先に階段の一つ下の階に通路を作ってください。', '配置不可: STAIR_LANDING_MISSING', '配置不可: wrong message'):
with self.subTest(status=status):
row = self.action_row('missing_connection')
row['status'] = status
self.action_check(row, 'connection reason mismatch')
def test_connection_reason_missing_or_empty(self):
for reasons in ([], [{'code':'OTHER', 'message':'other'}], [{'code':'STAIR_LANDING_MISSING', 'message':''}]):
with self.subTest(reasons=reasons):
row = self.action_row('missing_connection')
row['report']['reasons'] = reasons
self.action_check(row, 'connection reason mismatch')
def test_connection_preview_rejection_bypassed(self):
row = self.action_row('missing_connection')
row['results'] = [{'code':'VALIDATION_FAILED', 'details':{'reason':'STAIR_LANDING_MISSING'}, 'message':row['status']}]
self.action_check(row, 'preview rejection bypassed')
def test_preview_command_disagreement(self):
row = self.action_row('missing_connection')
row['report']['can_build'] = True
row['results'] = [{'code':'VALIDATION_FAILED', 'details':{'reason':'STAIR_LANDING_MISSING'}, 'message':row['status']}]
self.action_check(row, 'preview/command disagreement')
def test_preview_runtime_reason_difference(self):
row = self.action_row('missing_connection')
row['report']['error_codes'] = ['OVERLAP']
row['results'] = [{'code':'VALIDATION_FAILED', 'details':{'reason':'STAIR_LANDING_MISSING'}, 'message':row['status']}]
self.action_check(row, 'preview reason differs from Runtime reason')
def test_preview_partial_state_change(self):
self.action_check(self.action_row('missing_connection'), 'cancel/preview partially applied',
lambda node: C.typed_member(node, 'game_state', 'resources', 'materials').update(text='999'))
def test_readable_demolition_success(self):
row = self.action_row('demolition')
self.assertEqual('撤去しました(返金: 資材 2)', row['status'])
self.action_check(row)
def test_demolition_missing_refund_amount(self):
row = self.action_row('demolition'); row['status'] = '撤去しました(返金: 資材)'
self.action_check(row, 'demolition refund display mismatch')
def test_demolition_wrong_refund_amount(self):
row = self.action_row('demolition'); row['status'] = '撤去しました(返金: 資材 3)'
self.action_check(row, 'demolition refund display mismatch')
def test_demolition_raw_enum_status(self):
row = self.action_row('demolition'); row['status'] = 'OK | |'
self.action_check(row, 'raw demolition token in status')
def test_readable_demolition_refusal(self):
row = self.action_row('refused_demolition')
self.assertEqual('STAIR_LANDING_IN_USE', row['results'][0]['details']['reason'])
self.assertEqual('撤去できません: '+row['results'][0]['message'], row['status'])
self.action_check(row)
def test_demolition_raw_reason_status(self):
row = self.action_row('refused_demolition'); row['status'] += ' STAIR_LANDING_IN_USE'
self.action_check(row, 'raw demolition token in status')
def test_demolition_altered_refusal_message(self):
for status in ('撤去できません', '撤去できません: altered'):
row = self.action_row('refused_demolition'); row['status'] = status
self.action_check(row, 'demolition refusal message mismatch')
def test_demolition_raw_reason_mismatch(self):
row = self.action_row('refused_demolition')
row['results'][0]['details']['reason'] = 'OTHER'
def mutate(node):
items = C.typed_member(node, 'systems', 'runtime', 'command_queue', 'processed')['items']
items[-1] = F.typed(row['results'][0])
self.action_check(row, 'reason mismatch', mutate)
def test_verify_only_cli_uses_build_output_directory(self):
output = io.StringIO()
with patch.dict(os.environ, {'BUILD_OUTPUT_DIR':str(self.out.parent)}), contextlib.redirect_stdout(output):
self.assertEqual(0,C.main(['--verify-only','--project-root',str(self.root)]))
self.assertEqual('pending',json.loads(output.getvalue())['acceptance'])
def test_verify_only_cli_rejects_semantic_corruption(self):
self.edit('pair/writer-actions.json', lambda d: d['actions'][0].update(status='配置可能'))
output = io.StringIO()
with patch.dict(os.environ, {'BUILD_OUTPUT_DIR':str(self.out.parent)}), contextlib.redirect_stdout(output):
self.assertEqual(1,C.main(['--verify-only','--project-root',str(self.root)]))
self.assertIn('reason mismatch',json.loads(output.getvalue())['deficits'])
def bootstrap_edit(self, mutate):
self.edit("os-pair/writer-bootstrap-checkpoint.json", mutate)
self.reject("bootstrap source-defined operation drift")
def test_bootstrap_missing_notification(self):
self.bootstrap_edit(lambda d: C.typed_member(d,"systems","runtime","domain_events","recent_events")["items"].pop())
def test_bootstrap_extra_notification(self):
self.bootstrap_edit(lambda d: C.typed_member(d,"systems","runtime","domain_events","recent_events")["items"].append(
copy.deepcopy(C.typed_member(d,"systems","runtime","domain_events","recent_events")["items"][-1])))
def test_bootstrap_counter_drift(self):
self.bootstrap_edit(lambda d: C.typed_member(d,"systems","runtime","id_generator","counters","domain_event").update(text="999"))
def test_bootstrap_payload_drift(self):
def mutate(d):
event = C.typed_member(d,"systems","runtime","domain_events","recent_events")["items"][-1]
C.typed_member(event,"payload","save_path").update(text='"/wrong/path"')
self.bootstrap_edit(mutate)
def test_bootstrap_unknown_field_drift(self):
self.bootstrap_edit(lambda d: d["entries"].append([F.typed("unknown"),F.typed(1)]))
def test_bootstrap_missing_raw(self):
(self.out/"os-pair/writer-bootstrap-checkpoint.var").unlink()
F.repin(self.out)
self.reject("writer-bootstrap-checkpoint.var")
def test_bootstrap_overwritten_raw(self):
shutil.copyfile(self.out/"os-pair/writer-checkpoint.var", self.out/"os-pair/writer-bootstrap-checkpoint.var")
F.repin(self.out)
self.reject("wrong bootstrap raw model")
def test_reason_mismatch(self):
self.edit('pair/writer-actions.json', lambda d: d['actions'][0].update(status='配置可能'))
self.reject('reason mismatch')
def test_world_cell_drift(self):
self.edit('pair/writer-actions.json', lambda d: d['actions'][0]['position'].update(selected_cell=[9, 9, 0]))
self.reject('world-cell drift')
def test_transformed_viewport_corruption(self):
self.edit('pair/writer-actions.json', lambda d: d['actions'][0]['position'].update(viewport=[1,2]))
self.reject('transformed viewport mismatch')
def test_stale_source(self):
(self.root/C.TEST).write_text('changed')
self.reject('stale source')
def test_stale_commit(self):
self.edit('index.json', lambda d: d.update(commit='0'*40))
self.reject('stale commit')
def test_non_reuse_head_mismatch(self):
self.edit('index.json', lambda d:d.update(commit='0'*40))
self.reject('stale commit')
def test_stale_input(self):
(self.root/'docs/traceability/original/BASELINE.txt').write_text('changed')
self.reject('stale input')
def test_missing_os(self):
self.edit('index.json', lambda d: d.update(os_proof=None))
self.reject('missing artifact')
def test_fake_os_method(self):
self.edit('os-pair/unsupported-os.json', lambda d: d.update(method='handler'))
self.reject('fake OS provenance')
def test_fake_os_pid(self):
self.edit('os-pair/unsupported-os.json', lambda d: d.update(target_pid=555))
self.reject('fake OS provenance')
def test_same_pid_resume(self):
self.edit('runs/reader/reader-receipt.json', lambda d: d.update(actual_pid=101))
self.reject('duplicate actual PID')
def test_orphan_occupancy(self):
path = 'pair/writer-demolition-after.json'
model = C.decode(json.loads((self.out/path).read_text()))
model['systems']['runtime']['world']['occupancies'].append({'entity_id': 'ghost', 'cells': [[-8, 0, 0]]})
F.put(self.out/path, F.typed(model)); F.repin(self.out)
self.reject('orphan/lost occupancy')
def test_partial_demolition(self):
path = 'pair/writer-demolition-after.json'
model = C.decode(json.loads((self.out/path).read_text()))
model['systems']['runtime']['facilities']['instances'].append({'instance_id': 'f1', 'occupied_cells': [[-8, 0, 0]]})
F.put(self.out/path, F.typed(model)); F.repin(self.out)
self.reject('partial demolition')
def test_refusal_partial_resources(self):
path = 'pair/writer-refused_demolition-after.json'
model = C.decode(json.loads((self.out/path).read_text()))
model['game_state']['resources']['gold'] -= 1
F.put(self.out/path, F.typed(model)); F.repin(self.out)
self.reject('refusal partially applied')
def test_wrong_resume(self):
path = 'pair/reader-checkpoint.json'
original = (self.out/path).read_bytes()
model = C.decode(json.loads((self.out/path).read_text()))
model['clock']['absolute_minute'] += 1
F.put(self.out/path, F.typed(model)); F.repin(self.out)
self.reject('wrong resume model')
(self.out/path).write_bytes(original); F.repin(self.out)
self.edit('pair/reader-save-result-0001.json', lambda d: d.update(result=F.typed({'ok':False,'operation':'load','code':'UNSAFE_PATH'})))
self.reject('actual save/load failed')
self.edit('pair/reader-save-result-0001.json', lambda d: d.update(result=F.typed({'ok':True,'operation':'load'}), requested_path='/outside/formal'))
self.reject('save outside private user profile')
def test_notification_payload_is_recomputed_from_returned_dict(self):
path = 'pair/reader-checkpoint.json'
original = (self.out/path).read_bytes()
fields = ('ok','code','message','operation','save_path','reason','request_id',
'checkpoint_absolute_minute','duration_us','runtime_duration_us',
'selected_candidate_id','selected_candidate_kind','selected_generation',
'save_id','saved_absolute_minute')
for field in fields:
with self.subTest(field=field):
data = json.loads(original)
event = C.typed_member(data,'systems','runtime','domain_events','recent_events')['items'][-1]
value = C.typed_member(event,'payload',field)
decoded = C.decode(value)
changed = not decoded if isinstance(decoded,bool) else decoded+1 if isinstance(decoded,int) else decoded+'-drift'
value.update(C.scalar(changed,value['type']))
F.put(self.out/path,data); F.repin(self.out)
self.reject('source-defined operation drift')
(self.out/path).write_bytes(original); F.repin(self.out)
def test_notification_envelope_cannot_drift(self):
path = 'pair/writer-checkpoint.json'
original = (self.out/path).read_bytes()
for field in ('event_id','event_type','source_service','absolute_minute','causation_command_id','entity_ids'):
with self.subTest(field=field):
data = json.loads(original)
event = C.typed_member(data,'systems','runtime','domain_events','recent_events')['items'][-1]
value = C.typed_member(event,field)
if field == 'entity_ids': value['items'].append(C.scalar('unexpected-entity'))
else:
old = C.decode(value)
value.update(C.scalar(old+1 if isinstance(old,int) else old+'-drift',value['type']))
F.put(self.out/path,data); F.repin(self.out)
self.reject('source-defined operation drift')
(self.out/path).write_bytes(original); F.repin(self.out)
def test_notification_counter_must_increment_exactly_once(self):
def corrupt(data):
C.typed_member(data,'systems','runtime','id_generator','counters','domain_event')['text'] = '9'
self.edit('pair/reader-checkpoint.json',corrupt)
self.reject('source-defined operation drift')
def test_notification_published_ids_cannot_be_omitted(self):
self.edit('pair/writer-checkpoint.json',lambda d: C.typed_member(d,'systems','runtime','domain_events','published_ids')['items'].clear())
self.reject('source-defined operation drift')
def test_extra_notification_is_rejected(self):
def corrupt(data):
events = C.typed_member(data,'systems','runtime','domain_events','recent_events')['items']
events.append(copy.deepcopy(events[-1]))
self.edit('pair/reader-checkpoint.json',corrupt)
self.reject('source-defined operation drift')
def test_existing_event_history_cannot_be_rewritten(self):
def corrupt(data):
event = C.typed_member(data,'systems','runtime','domain_events','recent_events')['items'][0]
C.typed_member(event,'event_type').update(text='"REWRITTEN_HISTORY"')
self.edit('pair/reader-checkpoint.json',corrupt)
self.reject('source-defined operation drift')
def test_postsave_unrelated_resource_drift_is_rejected(self):
self.edit('pair/writer-checkpoint.json',lambda d: C.typed_member(d,'game_state','resources','gold').update(text='9999'))
self.reject('source-defined operation drift')
def test_postload_unknown_field_drift_is_rejected(self):
self.edit('pair/reader-checkpoint.json',lambda d: C.typed_member(d,'systems','runtime','inventory')['entries'].append([C.scalar('unknown'),C.scalar(1)]))
self.reject('source-defined operation drift')
def test_presave_must_match_read_only_formal_model(self):
self.edit('pair/writer-presave.json',lambda d: C.typed_member(d,'clock','absolute_minute').update(text='361'))
self.reject('wrong presave formal model')
def test_read_only_inspection_must_match_complete_formal_model(self):
self.edit('pair/reader-save-result-0002.json',lambda d: C.typed_member(d['result'],'snapshot','clock','absolute_minute').update(text='361'))
self.reject('wrong read-only formal model')
def test_inspection_cannot_lose_read_only_attribution(self):
self.edit('pair/writer-save-result-0002.json',lambda d: C.typed_member(d['result'],'read_only').update(text='false'))
self.reject('missing read-only formal model')
def test_returned_result_payload_tampering_is_rejected(self):
self.edit('pair/reader-save-result-0001.json',lambda d: C.typed_member(d['result'],'duration_us').update(text='999'))
self.reject('source-defined operation drift')
def test_notification_payload_type_drift_is_rejected(self):
def corrupt(data):
event = C.typed_member(data,'systems','runtime','domain_events','recent_events')['items'][-1]
C.typed_member(event,'payload','operation')['type'] = 'String'
C.typed_member(event,'payload','operation')['text'] = '"recovery"'
self.edit('pair/reader-checkpoint.json',corrupt)
self.reject('source-defined operation drift')
def test_operation_count_is_source_derived_with_recent_limit(self):
model = C.decode(json.loads((self.out/'pair/writer-formal-model.json').read_text()))
runtime = model['systems']['runtime']
runtime['id_generator']['counters']['domain_event'] = 900
stream = runtime['domain_events']
stream['published_ids'] = ['domain_event.%012d' % n for n in range(1,901)]
stream['recent_events'] = [{'ordinal':n} for n in range(645,901)]
expected = C.decode(C.expected_operation(F.typed(model),{'ok':True},'SAVE_CHECKPOINT_SUCCEEDED'))
new = expected['systems']['runtime']
self.assertEqual(901,new['id_generator']['counters']['domain_event'])
self.assertEqual(901,len(new['domain_events']['published_ids']))
self.assertEqual(256,len(new['domain_events']['recent_events']))
self.assertEqual(stream['recent_events'][1:],new['domain_events']['recent_events'][:-1])
self.assertEqual('domain_event.000000000901',new['domain_events']['recent_events'][-1]['event_id'])
del runtime['id_generator']['counters']
with self.assertRaisesRegex(ValueError,'missing/duplicate typed model member'):
C.expected_operation(F.typed(model),{'ok':True},'SAVE_CHECKPOINT_SUCCEEDED')
def test_missing_formal_member(self):
for name in ('formal','formal.bak'):
with self.subTest(package=name):
file = self.out/('pair/'+name+'/journal.log')
original = file.read_bytes()
file.unlink(); F.repin(self.out)
self.reject('journal.log')
file.write_bytes(original); F.repin(self.out)
self.edit('pair/formal-export.json', lambda d: d['slot_metadata'].update(sha256='0'*64))
self.reject('slot metadata SHA')
def test_missing_model_member(self):
path = 'pair/writer-unsupported-before.json'
model = C.decode(json.loads((self.out/path).read_text()))
del model['systems']['runtime']['inventory']
F.put(self.out/path, F.typed(model)); F.repin(self.out)
self.reject('missing whole Runtime members')
def test_missing_clamp_boundary(self):
self.edit('pair/writer-actions.json', lambda d: d['actions'][-1].update(steps=[1]*18))
self.reject('wheel clamp sequence')
def test_no_actual_pan(self):
self.edit('pair/writer-actions.json', lambda d: d['actions'][-1].update(pan_after=[0,0]))
self.reject('move-mode wheel did not pan')
def test_handler_command_must_remain_headless(self):
self.edit('runs/writer/writer-receipt.json', lambda d: d['command'].remove('--headless'))
self.reject('handler command contract')
def test_os_headless_masquerade(self):
self.edit('os-runs/writer/ended.json', lambda d: d['command'].insert(1, '--headless'))
self.reject('OS native argv')
def test_stale_os_recorder(self):
self.edit('os-runs/writer/ended.json', lambda d: d.update(recorder_sha256='0'*64))
self.reject('OS controller/recorder SHA')
def test_os_process_unended(self):
self.edit('os-runs/writer/ended.json', lambda d: d.update(engine_ended=False))
self.reject('OS native failure')
def test_os_default_player_changed(self):
self.edit('os-runs/writer/ended.json', lambda d: d.update(default_after={'changed':True}))
self.reject('OS protected state changed')
def test_os_actual_observation_missing(self):
self.edit('os-pair/unsupported-1/observed.json', lambda d: d.update(events=[]))
self.reject('missing OS events')
def test_os_dispatch_missing(self):
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][0]['position'].pop('dispatch'))
self.reject('dispatch')
def test_os_wrong_actual_input_cell(self):
# Keep event and action mutually consistent; independent conversion must reject.
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][0]['position']['dispatch']['world_cell'].__setitem__(0, 99))
self.edit('os-pair/unsupported-1/observed.json', lambda d: d['events'][0]['dispatch']['world_cell'].__setitem__(0, 99))
self.reject('OS actual input cell drift')
def test_os_event_position_disagrees(self):
self.edit('os-pair/unsupported-1/observed.json', lambda d: d['events'][0].update(position=[0,0]))
self.reject('OS dispatch event mismatch')
def test_os_wrong_selected_entity(self):
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][-1]['position']['selection'].update(entity_id='wrong'))
self.reject('OS selected entity mismatch')
def test_os_wrong_selected_occupancy(self):
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][-1]['position']['selection'].update(entity_id='wrong', occupant_id='wrong'))
self.reject('OS selected occupancy mismatch')
def test_os_selection_requirement_cannot_be_removed(self):
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][-1]['position']['selection'].update(required=False))
self.reject('OS missing entity selection')
def test_os_dispatch_transform_drift(self):
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][0]['position']['dispatch'].update(pan=[100,0]))
self.edit('os-pair/unsupported-1/observed.json', lambda d: d['events'][0]['dispatch'].update(pan=[100,0]))
self.reject('OS dispatch transform drift')
def test_os_later_getter_is_diagnostic(self):
self.edit('os-pair/writer-actions.json', lambda d: d['actions'][-1]['position']['diagnostics'].update(getter_cell=[999,999,0]))
self.assertEqual([], C.check(self.root, self.out))
def test_os_ack_for_another_request(self):
self.edit('os-pair/unsupported-1/ack.json', lambda d: d.update(request_sha256='0'*64))
self.reject('OS ack request mismatch')
def test_os_canvas_mapping_drift(self):
self.edit('os-pair/unsupported-1/request.json', lambda d: d.update(viewport_to_screen=[1,0,0,1,32,0]))
# Repin acknowledgement too: test semantics, rather than checksum rejection.
self.edit('os-pair/unsupported-1/ack.json', lambda d: d.update(request_sha256=C.sha(self.out/'os-pair/unsupported-1/request.json')))
self.reject('OS canvas mapping drift')
def test_os_guide_interception(self):
self.edit('os-pair/unsupported-1/request.json', lambda d: d.update(guide_visible=True,guide_rect=[0,0,10000,10000]))
self.edit('os-pair/unsupported-1/ack.json', lambda d: d.update(request_sha256=C.sha(self.out/'os-pair/unsupported-1/request.json')))
self.reject('OS guide interception')
def test_whole_checker_missing_stair_projection(self):
self.edit('pair/writer-lower_stair-completed.json', lambda d: C.typed_member(d,'systems','runtime','facility_infrastructure','projections')['items'].pop())
self.reject('missing/extra infrastructure projection')
def test_whole_checker_wrong_stair_connector(self):
self.edit('pair/writer-lower_stair-completed.json', lambda d: C.typed_member(C.typed_member(d,'systems','runtime','path','connectors')['items'][0],'cost').update(text='999'))
self.reject('wrong infrastructure connector topology')
def test_refusal_cannot_be_relabelled(self):
self.edit('pair/writer-actions.json', lambda d: d['actions'][4].update(expected='OTHER_REASON'))
self.reject('required refusal reason changed')
def test_demolition_cannot_be_relabelled(self):
self.edit('pair/writer-actions.json', lambda d: d['actions'][9].update(kind='camera'))
self.reject('operation kind mismatch')
def test_png_signature_is_not_a_screenshot(self):
next((self.out/'os-pair').rglob('screen-before.png')).write_bytes(b'\x89PNG\r\n\x1a\nFAKE')
F.repin(self.out)
self.reject('invalid OS screenshot')
def test_omitted_os_lifecycle_request(self):
self.edit('os-pair/writer-requests.json', lambda d: d['requests'].pop())
self.reject('omitted OS lifecycle requests')
def test_os_audio_boolean_cannot_replace_owner_evidence(self):
self.edit('os-runs/writer/teardown/owned-audio/owner_1.json', lambda d: d['final_targets'][0].update(alive=True))
self.reject('real WeakRef release')
def collector_mock(self, fail_wait=False):
"""No native engine: Popen, profiles, live engine probes are all mocked."""
expected2400 = 2400
self.assertEqual(expected2400, D.OS_CHILD_TIMEOUT_SECONDS)
def assert2400(receipt):
self.assertEqual(expected2400, receipt['timeout_seconds'])
output = Path(self.temp.name)/'mock-collection'
stage = output/'stage'; stage.mkdir(parents=True)
shutil.copyfile(self.root/'project.godot',stage/'project.godot')
public = (stage/'project.godot').read_bytes()
(output/'pair').mkdir()
shutil.copytree(self.out/'pair/formal',output/'pair/initial-formal')
F.put(output/'pair/diagnostic-budget.json',{'setup':'synthetic mock'})
refs = C.runner.source_refs(self.root)
protected = {'mock_default_player':True}
captured = []; cleaned = []
def spawn(argv, **kw):
self.assertNotIn('--headless', argv)
self.assertEqual(['--path',str(stage),'--script'],argv[1:4])
role = kw['env']['P08_PLACEMENT_ROLE']
profile_text = (stage/'project.godot').read_text()
name = __import__('re').search(r'config/custom_user_dir_name="([^"]+)"',profile_text).group(1)
pid = 801+len(captured)
process = SimpleNamespace(pid=pid, role=role)
def wait(timeout):
self.assertEqual(expected2400,timeout)
assert2400(C.load(output/('os-runs/'+role+'/started.json')))
if fail_wait:
raise TimeoutError('mock Root acknowledgement timeout')
kw['stdout'].write('P08 passive OS placement: 2 checks, 0 failures\n')
F.put(output/('os-pair/'+role+'-profile.json'),{'pid':pid,'user_data_dir':str(Path(self.temp.name)/name),'display':'mock'})
if role == 'writer': shutil.copytree(output/'pair/initial-formal',output/'os-pair/formal')
return 0
process.wait = wait; captured.append(process)
return process
def cleanup(children):
cleaned.extend(p.pid for p in children)
return ([{'pid':p.pid,'engine_ended':True} for p in children], [])
r = SimpleNamespace(require=C.runner.require, inventory=C.runner.inventory,
sha=C.sha, read=C.load, write=C.runner.write, copy_source=shutil.copyfile,
source_refs=lambda root:refs, default_player=lambda root:protected,
profile_path=lambda name:Path(self.temp.name)/name,
private_project=C.runner.private_project, fresh_environment=C.runner.fresh_environment,
SUPPORT=ROOT/'tools/regression', module=lambda *a:SimpleNamespace(ordinary_gate=lambda *a:{'mock':True}))
outer = SimpleNamespace(native=SimpleNamespace(helper={'live_engines':lambda:[]}),finish_captured=cleanup)
# Disk admission is an environmental input to this fully mocked collector.
# Preserve the real collector's gate; make this fixture independent of host free space.
with patch.object(D.shutil,'disk_usage',return_value=SimpleNamespace(free=1024**3)), patch.object(D.subprocess,'Popen',side_effect=spawn), contextlib.redirect_stdout(io.StringIO()):
if fail_wait:
with self.assertRaisesRegex(AssertionError,'native OS collection failed'):
D.collect_os(r,self.root,output,stage,str(Path(self.temp.name)/'synthetic-engine-not-executable'),'1'*40,refs,protected,outer)
else:
phases = D.collect_os(r,self.root,output,stage,str(Path(self.temp.name)/'synthetic-engine-not-executable'),'1'*40,refs,protected,outer)
self.assertEqual(2,len(phases))
self.assertEqual([p.pid for p in captured],cleaned)
self.assertEqual(public,(stage/'project.godot').read_bytes())
self.assertTrue((output/'os-runs/writer/ended.json').exists())
self.assertEqual(['writer'] if fail_wait else ['writer', 'reader'],
[p.role for p in captured])
for process in captured:
for event in ('started', 'ended'):
assert2400(C.load(output/('os-runs/'+process.role+'/'+event+'.json')))
def test_mock_native_controller_two_private_pids(self):
self.collector_mock()
def test_mock_native_timeout_restores_project_and_cleans_owned_child(self):
self.collector_mock(fail_wait=True)
def image_put(path, value):
path.write_text(json.dumps(value, sort_keys=True)+'\n')
def negative_jpeg(width=1280, height=752, progressive=False):
def segment(marker, body):
return bytes((255, marker))+struct.pack('>H', len(body)+2)+body
frame = segment(0xc2 if progressive else 0xc0,
b'\x08'+struct.pack('>HH', height, width)+b'\x01\x01\x11\x00')
scan = segment(0xda, b'\x01\x01\x00\x00\x3f\x00')+b'\x12\xff\x00\x34\xff\xd0\x56'
tables = segment(0xdb, b'\x00'+bytes([1])*64)+segment(0xc4, b'\x00'+b'\x01'+bytes(15)+b'\x00'+b'\x10'+b'\x01'+bytes(15)+b'\x00')
return b'\xff\xd8'+tables+segment(0xe0, b'SYNTHETIC')+frame+scan+(scan if progressive else b'')+b'\xff\xd9'
def negative_images(base, candidate):
out, original = base/'canonical', base/'original'
out.mkdir(parents=True); index = {'commit':candidate, 'artifacts':{}}
measurement = {'scope':'synthetic post-collection measurement', 'images':[]}
for role, count in (('reader',5), ('writer',64)):
for i in range(1,count+1):
for side in ('before','after'):
name = f'os-pair/{role}-request-{i:04d}/screen-{side}.png'
path = out/name; path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(negative_jpeg(progressive=i%2 == 0))
measurement['images'].append({'file':name, 'source_sha256':hashlib.sha256(path.read_bytes()).hexdigest(),
'width':1280,'height':752,'format_by_magic':'JPEG'})
for name in ('runner-input.json','runner-ended.json','os-runs/writer/ended.json','os-runs/reader/ended.json'):
path = out/name; path.parent.mkdir(parents=True,exist_ok=True); image_put(path, {'synthetic':True})
refresh_images(out, index, measurement)
shutil.copytree(out, original)
def pin(name):
p=original/name
return {'path':str(p),'bytes':p.stat().st_size,'sha256':hashlib.sha256(p.read_bytes()).hexdigest()}
pins = {'original_root':str(original),'native_candidate':candidate,'index':pin('index.json'),
'runner_input':pin('runner-input.json'),'runner_ended':pin('runner-ended.json'),
'OS':{role:{'receipt':pin('os-runs/'+role+'/ended.json')} for role in ('writer','reader')}}
return out, original, index, measurement, pins
def refresh_images(out, index, measurement):
index['artifacts'] = {p.relative_to(out).as_posix():{'bytes':p.stat().st_size,
'sha256':hashlib.sha256(p.read_bytes()).hexdigest()} for p in out.rglob('*') if p.is_file() and p.name != 'index.json'}
image_put(out/'index.json',index)
measurement['original_index_sha256'] = hashlib.sha256((out/'index.json').read_bytes()).hexdigest()
class FreshRoutingTests(unittest.TestCase):
setUp = EvidenceTests.setUp
edit = EvidenceTests.edit
reject = EvidenceTests.reject
# Reuse the whole synthetic proof, not a boolean checker substitute.
def historical(self):
F.put(self.root/C.R09_DOCS/'ROOT_NATIVE_R09_COLLECTION_PINS.json', {'synthetic': True})
def fresh_check(self):
commit = C.load(self.out/'index.json')['commit']
original_load = C.load
def guarded_load(path):
self.assertNotEqual(Path(path).name, 'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json')
return original_load(path)
with contextlib.ExitStack() as stack:
stack.enter_context(patch.object(C.subprocess, 'check_output', return_value=commit+'\n'))
stack.enter_context(patch.object(C, 'load', side_effect=guarded_load))
for name in ('r09_applicability', 'r09_runner_binding', 'r09_collection', 'r09_images'):
stack.enter_context(patch.object(C, name, side_effect=AssertionError('fresh used '+name)))
return C.check(self.root, self.out)
def test_historical_pins_fresh_positive(self):
self.historical()
self.assertEqual([], self.fresh_check())
def test_historical_pins_stale_commit(self):
self.historical()
self.edit('index.json', lambda d: d.update(commit='0'*40))
with patch.object(C.subprocess, 'check_output', return_value='1'*40+'\n'):
self.reject('stale commit')
def test_historical_pins_stale_source(self):
self.historical()
self.edit('index.json', lambda d: d['source_map'].update({'project.godot':'0'*64}))
self.assertIn('stale source', ' '.join(self.fresh_check()))
def test_historical_pins_stale_input(self):
self.historical()
self.edit('index.json', lambda d: d['inputs'].update({'AGENTS.md':'0'*64}))
self.assertIn('stale input', ' '.join(self.fresh_check()))
def test_reuse_missing_pins(self):
self.edit('index.json', lambda d: d.update(commit=C.R09_COMMIT))
self.reject('R09 reuse candidate')
def test_reuse_wrong_applicability(self):
self.historical()
F.put(self.root/C.R09_DOCS/'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json', {'synthetic':True})
self.edit('index.json', lambda d: d.update(commit=C.R09_COMMIT))
with patch.object(C.subprocess, 'check_output', return_value=C.R09_COMMIT+'\n'), patch.object(
C.subprocess, 'run', return_value=SimpleNamespace(returncode=1)):
self.reject('R09_COMMIT not an ancestor')
def screenshots(self, data):
for path in self.out.rglob('screen-*.png'):
path.write_bytes(data)
F.repin(self.out)
def test_fresh_jpeg_positive(self):
self.historical()
self.screenshots(negative_jpeg())
self.assertEqual([], self.fresh_check())
def test_fresh_truncated_jpeg(self):
self.screenshots(negative_jpeg()[:-2])
self.assertIn('invalid JPEG SOI/EOI', ' '.join(self.fresh_check()))
def test_fresh_garbage(self):
self.screenshots(b'garbage')
self.assertIn('missing OS screenshot', ' '.join(self.fresh_check()))
def test_fresh_dimension_mismatch(self):
self.screenshots(negative_jpeg())
next(self.out.rglob('screen-after.png')).write_bytes(negative_jpeg(width=640))
F.repin(self.out)
self.assertIn('OS screenshot dimension mismatch', ' '.join(self.fresh_check()))
def test_fresh_unbound_path(self):
ack = next(self.out.rglob('ack.json'))
data = C.load(ack)
data['screen_before'] = data['screen_after']
F.put(ack, data)
F.repin(self.out)
self.assertIn('R09 screenshot request binding', ' '.join(self.fresh_check()))
class R09ImageTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory(dir=ROOT/'tests/fixtures/p08-03/.scratch')
self.addCleanup(self.temp.cleanup)
self.fixture = SimpleNamespace(make=negative_images, jpeg=negative_jpeg, refresh=refresh_images)
self.out, self.original, self.index, self.measurement, self.pins = self.fixture.make(Path(self.temp.name), C.R09_COMMIT)
def images(self):
C.r09_images(self.out, self.index, self.measurement)
def change_bytes(self, data):
row = self.measurement['images'][0]
(self.out/row['file']).write_bytes(data)
row['source_sha256'] = hashlib.sha256(data).hexdigest()
self.fixture.refresh(self.out, self.index, self.measurement)
def test_baseline_progressive_and_collection_positive(self):
pins = C.load(ROOT/C.R09_DOCS/'ROOT_NATIVE_R09_COLLECTION_PINS.json')
original = Path(pins['original_root'])
if not (original/'index.json').exists():
self.skipTest('Root-executed original R09 capture hash re-check required')
C.r09_images(original, C.load(original/'index.json'),
C.load(ROOT/C.R09_DOCS/'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json'), pins)
def test_jpeg_missing_tables_and_scan(self):
data = negative_jpeg()
def remove(marker):
start = data.index(bytes((255, marker)))
size = int.from_bytes(data[start+2:start+4], 'big')
return data[:start]+data[start+2+size:]
for stream, error in ((remove(0xdb), 'missing JPEG DQT'),
(remove(0xc4), 'missing JPEG DHT'),
(data[:data.index(b'\xff\xda')+10]+b'\xff\xd9', 'empty JPEG entropy stream')):
with self.subTest(error=error), self.assertRaisesRegex(ValueError, error):
C.jpeg_dimensions(stream)
start = data.index(b'\xff\xda')
for offset, value, error in ((5, 2, 'JPEG SOF/SOS component mismatch'),
(6, 0x11, 'JPEG SOS undefined table')):
stream = bytearray(data); stream[start+offset] = value
with self.assertRaisesRegex(ValueError, error): C.jpeg_dimensions(stream)
def test_truncated_or_non_jpeg(self):
good = self.fixture.jpeg()
for data in (good[:-1], b'NOT JPEG', b'\xff\xd8\xff\xe0\xff\xff\xff\xd9',
good[:15]+b'\xff\xd9', b'\xff\xd8\xff\xd9'):
with self.subTest(data=data):
self.change_bytes(data)
with self.assertRaisesRegex(ValueError, 'JPEG'):
self.images()
def test_sof_dimension_mismatch(self):
self.change_bytes(self.fixture.jpeg(height=720))
with self.assertRaisesRegex(ValueError,'SOF dimension mismatch'): self.images()
def test_before_after_dimension_drift(self):
self.change_bytes(self.fixture.jpeg(height=753))
self.measurement['images'][0]['height'] = 753
with self.assertRaisesRegex(ValueError,'before/after dimension drift'): self.images()
def test_missing_image_entry(self):
self.measurement['images'].pop()
with self.assertRaisesRegex(ValueError,'missing/extra image entry'): self.images()
def test_extra_image_entry(self):
row = dict(self.measurement['images'][0],file='os-pair/extra.png')
self.measurement['images'].append(row)
with self.assertRaisesRegex(ValueError,'missing/extra image entry'): self.images()
def test_duplicate_image_entry(self):
self.measurement['images'].append(self.measurement['images'][0])
with self.assertRaisesRegex(ValueError,'duplicate file'): self.images()
def test_extra_indexed_image(self):
(self.out/'os-pair/extra.png').write_bytes(self.fixture.jpeg())
self.fixture.refresh(self.out,self.index,self.measurement)
with self.assertRaisesRegex(ValueError,'missing/extra image entry'): self.images()
def test_source_sha256_mismatch(self):
self.measurement['images'][0]['source_sha256'] = '0'*64
with self.assertRaisesRegex(ValueError,'source_sha256 mismatch'): self.images()
def test_original_capture_hash_and_measurement_drift(self):
for alter_bytes in (True, False):
with self.subTest(alter_bytes=alter_bytes):
out, original, index, measurement, pins = negative_images(
Path(self.temp.name)/('case-'+str(alter_bytes)), C.R09_COMMIT)
row = sorted(measurement['images'], key=lambda r:r['file'])[0]
if alter_bytes:
(out/row['file']).write_bytes(negative_jpeg(height=720))
refresh_images(out, index, measurement)
error = 'original capture hash mismatch'
else:
row['source_sha256'] = '0'*64
error = 'measurement capture hash altered'
with self.assertRaisesRegex(ValueError, error):
C.r09_images(out, index, measurement, pins)
def test_stale_index_pin(self):
self.measurement['original_index_sha256'] = '0'*64
with self.assertRaisesRegex(ValueError,'stale image index pin'): self.images()
def test_original_canonical_divergence(self):
(self.out/self.measurement['images'][0]['file']).write_bytes(self.fixture.jpeg(height=720))
with self.assertRaisesRegex(ValueError,'original/canonical byte divergence'):
C.r09_collection(self.out,self.index,self.pins)
def test_collection_pin_mismatch(self):
self.pins['runner_input']['sha256'] = '0'*64
with self.assertRaisesRegex(ValueError,'collection pin SHA'):
C.r09_collection(self.out,self.index,self.pins)
class R09ApplicabilityTests(unittest.TestCase):
def test_commit_and_ancestor(self):
with patch.object(C.subprocess, 'check_output', return_value=C.R09_COMMIT+'\n'):
with self.assertRaisesRegex(ValueError, 'R09 reuse candidate'):
C.r09_applicability(ROOT, {'commit':'0'*40})
with patch.object(C.subprocess, 'run', return_value=SimpleNamespace(returncode=1)):
with self.assertRaisesRegex(ValueError, 'R09_COMMIT not an ancestor'):
C.r09_applicability(ROOT, {'commit':C.R09_COMMIT})
def test_non_whitelisted_committed_and_dirty_changes(self):
names = ['scenes/main/main.gd', 'tests/ui/test_p08_normal_placement_ops.gd',
'tests/fixtures/p08-03/added.py', 'tests/fixtures/p08-03/placement.gd',
'tools/run_p08_normal_placement.py', 'tools/regression/suite-policy.json']
for name in names:
for status in ('A', 'D', 'M'):
with self.subTest(name=name, status=status):
with patch.object(C.subprocess, 'run', return_value=SimpleNamespace(returncode=0)), patch.object(
C.subprocess, 'check_output', side_effect=[
C.R09_COMMIT+'\n', (status+'\0'+name+'\0').encode()]):
with self.assertRaisesRegex(ValueError, 'non-whitelisted committed change'):
C.r09_applicability(ROOT, {'commit':C.R09_COMMIT})
with patch.object(C.subprocess, 'run', return_value=SimpleNamespace(returncode=0)), patch.object(
C.subprocess, 'check_output', side_effect=[C.R09_COMMIT+'\n', b'', b' M scenes/main/main.gd\0']):
with self.assertRaisesRegex(ValueError, 'dirty working tree outside whitelist'):
C.r09_applicability(ROOT, {'commit':C.R09_COMMIT})
def test_engine_settings_environment_drift(self):
pins = {'runner_input':{'tag':'run'}, 'index':{'tag':'index'},
'OS':{'writer':{'receipt':{'tag':'receipt'}}}}
baseline = {'engine':'pinned-engine', 'engine_sha256':'1'*64}
for field, message in (('engine_sha256', 'R09 engine hash/version drift'),
('settings', 'R09 settings/config/environment drift'),
('environment', 'R09 settings/config/environment drift')):
altered = dict(baseline); altered[field] = 'drift'
with self.subTest(field=field), patch.object(C, 'load', return_value=altered), patch.object(
C, 'pinned_document', return_value=baseline):
with self.assertRaisesRegex(ValueError, message):
C.r09_runner_binding(ROOT, {}, pins)
# Version is in the SHA-pinned profile, not a worker engine invocation.
run = dict(baseline, commit=C.R09_COMMIT)
documents = [run, {}]
originals = [run, {'artifacts':{'os-pair/writer-profile.json':{'sha256':'1'*64}}}, {}]
with patch.object(C, 'load', side_effect=documents), patch.object(
C, 'pinned_document', side_effect=originals):
with self.assertRaisesRegex(ValueError, 'R09 engine hash/version drift'):
C.r09_runner_binding(ROOT, {'artifacts':{'os-pair/writer-profile.json':{'sha256':'2'*64}}}, pins)
def test_input_drift(self):
with patch.object(C.subprocess, 'run', return_value=SimpleNamespace(returncode=0)), patch.object(
C.subprocess, 'check_output', side_effect=[C.R09_COMMIT+'\n', b'', b'']), patch.object(
C, 'input_map', return_value={'fixture':'changed'}):
with self.assertRaisesRegex(ValueError, 'stale reuse input'):
C.r09_applicability(ROOT, {'commit':C.R09_COMMIT, 'inputs':{'fixture':'original'}})
class R16EnvironmentTests(unittest.TestCase):
def setUp(self):
self.original = Path('/recorded/r09')
env = {'PATH':'/opaque/original', 'P08_PLACEMENT_ROLE':'writer',
'P08_PLACEMENT_ROOT':str(self.original/'os-pair'),
'THE_TREE_AUDIO_TEARDOWN_PROOF_DIR':str(self.original/'os-runs/writer/teardown/owned-audio'),
'THE_TREE_AUDIO_FIXTURE_CONTROL_PROOF':str(self.original/'os-runs/writer/teardown/control.json')}
self.pin = {'role':'writer', 'environment':{k:v for k,v in env.items() if k.startswith(('P08_', 'THE_TREE_'))},
'environment_sha256':hashlib.sha256(json.dumps(env, sort_keys=True).encode()).hexdigest(),
'controller_sha256':'driver', 'recorder_sha256':'recorder', 'engine_sha256':'engine',
'command':['engine', '--script', 'recorder'], 'source_map':{'source':'hash'}}
self.receipts = [copy.deepcopy(self.pin) for _ in range(3)]
def verify(self):
C.r09_environment(self.receipts, self.pin, 'writer', self.original)
def test_driver_recording_positive(self):
self.assertNotEqual(self.pin['environment_sha256'], hashlib.sha256(
json.dumps(self.pin['environment'], sort_keys=True).encode()).hexdigest())
self.verify()
def test_subset_one_receipt(self):
self.receipts[0]['environment']['P08_PLACEMENT_ROOT'] = '/changed'
with self.assertRaisesRegex(ValueError, 'R09 environment subset cross-receipt drift'): self.verify()
def test_subset_all_receipts_not_pin(self):
for r in self.receipts: r['environment']['P08_PLACEMENT_ROOT'] = '/changed'
with self.assertRaisesRegex(ValueError, 'R09 environment subset pin drift'): self.verify()
def test_hash_started_ended_difference(self):
self.receipts[0]['environment_sha256'] = '0'*64
with self.assertRaisesRegex(ValueError, 'R09 environment hash cross-receipt drift'): self.verify()
def test_hash_all_receipts_not_pin(self):
for r in self.receipts: r['environment_sha256'] = '0'*64
with self.assertRaisesRegex(ValueError, 'R09 environment hash pin drift'): self.verify()
def test_receipt_bytes_not_pin(self):
data = json.dumps(self.pin).encode()
pin = {'bytes':len(data), 'sha256':hashlib.sha256(data).hexdigest()}
C.r09_receipt_bytes(data, pin)
with self.assertRaisesRegex(ValueError, 'R09 receipt bytes drift'):
C.r09_receipt_bytes(data.replace(b'writer', b'reader'), pin)
def test_role_mismatch(self):
for r in [self.pin]+self.receipts: r['environment']['P08_PLACEMENT_ROLE'] = 'reader'
with self.assertRaisesRegex(ValueError, 'R09 environment role mismatch'): self.verify()
def test_proof_path_mismatch(self):
for field in ('THE_TREE_AUDIO_TEARDOWN_PROOF_DIR', 'THE_TREE_AUDIO_FIXTURE_CONTROL_PROOF'):
with self.subTest(field=field):
self.setUp()
for r in [self.pin]+self.receipts: r['environment'][field] = '/wrong'
with self.assertRaisesRegex(ValueError, 'R09 environment proof path mismatch'): self.verify()
def test_driver_changed_at_head(self):
C.r09_driver_bytes(b'original', b'original', b'original')
with self.assertRaisesRegex(ValueError, 'R09 driver bytes drift'):
C.r09_driver_bytes(b'original', b'changed', b'original')
class RealR05InfrastructureTests(unittest.TestCase):
"""Actual native snapshot, never a fabricated acceptance specimen."""
def setUp(self):
folder = ROOT/'tests/fixtures/p08-03'
provenance = json.loads((folder/'r05-reader-checkpoint-provenance.json').read_bytes())
raw = gzip.decompress((folder/'r05-reader-checkpoint.json.gz').read_bytes())
self.assertEqual(provenance['raw_sha256'],hashlib.sha256(raw).hexdigest())
self.assertEqual(provenance['raw_bytes'],len(raw))
self.node = json.loads(raw)
self.model = C.decode(self.node)
def test_real_reader_checkpoint(self):
self.assertEqual(self.model,C.state(self.node))
def reject(self, mutate, message):
runtime = self.model['systems']['runtime']; mutate(runtime)
with self.assertRaisesRegex(ValueError,message): C.state(F.typed(self.model))
def test_missing_projection(self):
self.reject(lambda r:r['facility_infrastructure']['projections'].pop(),'missing/extra infrastructure projection')
def test_extra_projection(self):
def mutate(r):
p=copy.deepcopy(r['facility_infrastructure']['projections'][0]); p['instance_id']='ghost'
r['facility_infrastructure']['projections'].append(p)
self.reject(mutate,'missing/extra infrastructure projection')
def test_wrong_projected_cells(self):
self.reject(lambda r:r['facility_infrastructure']['projections'][0].update(cells=[[9,9,0]]),'wrong infrastructure projection')
def test_missing_route(self):
self.reject(lambda r:r['spatial_subslots']['entities'].pop(),'wrong infrastructure ROUTE topology')
def test_extra_route(self):
self.reject(lambda r:r['spatial_subslots']['entities'].append({'entity_id':'infra.route:ghost','category':'ROUTE','cells':[[8,4,0]]}),'extra infrastructure ROUTE topology')
def test_wrong_route(self):
self.reject(lambda r:r['spatial_subslots']['entities'][0].update(category='UTILITY'),'wrong infrastructure ROUTE topology')
def test_missing_connector(self):
self.reject(lambda r:r['path']['connectors'].pop(0),'wrong infrastructure connector topology')
def test_extra_connector(self):
def mutate(r):
c=copy.deepcopy(r['path']['connectors'][0]); c['id']='infra.connector:ghost'; r['path']['connectors'].append(c)
self.reject(mutate,'extra infrastructure connector topology')
def test_wrong_connector(self):
self.reject(lambda r:r['path']['connectors'][0].update(to=[8,0,0]),'wrong infrastructure connector topology')
def test_wrong_projected_walkable(self):
self.reject(lambda r:r['route_projection']['projected_walkable_cells'].pop(),'wrong projected walkable topology')
def test_wrong_path_cells(self):
self.reject(lambda r:r['path']['cell_traffic'].pop(),'wrong projected walkable topology')
def test_ordinary_facility_loss(self):
self.reject(lambda r:r['facilities']['instances'].clear(),'partial demolition/completion')
def test_proxy_occupancy_loss(self):
self.reject(lambda r:r['world']['occupancies'].pop(),'orphan/lost occupancy')
def test_proxy_operational_duplication(self):
self.reject(lambda r:r['facilities']['instances'].append(copy.deepcopy(r['construction']['facilities'][0])),'partial demolition/completion')
if __name__ == '__main__':
unittest.main()