tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

unit.py (85765B)


      1 #!/usr/bin/env python3
      2 """Fast host-side policy, provenance, RSS and native-scope controls; no builds."""
      3 
      4 # native_header_equivalence_check
      5 import hashlib
      6 
      7 import json
      8 
      9 from pathlib import Path
     10 
     11 import tempfile
     12 
     13 import unittest
     14 
     15 from native_header_equivalence import native_header_equivalence, digest
     16 
     17 class HeaderEquivalenceTests(unittest.TestCase):
     18     root_kind = 'unit'
     19 
     20     def setUp(self):
     21         self.temporary = tempfile.TemporaryDirectory()
     22         self.addCleanup(self.temporary.cleanup)
     23         root = Path(self.temporary.name)
     24         self.repo, self.generation = root / 'repo', root / 'generation'
     25         self.repo.mkdir(); self.generation.mkdir()
     26         (self.generation / 'overlay').mkdir()
     27         (self.generation / 'cas/cache/source-build').mkdir(parents=True)
     28         self.map = self.repo / 'map.v1'
     29         old_map = ('arboricx.source-map.v1\nunit\tbase\tbase\tbase.tri\t-\n' +
     30                    self.root_kind + '\tsource_map\tsource.map\troot.tri\tbase\n')
     31         self.map.write_text(old_map.replace('unit\t', 'native\t').replace('reexport\t', 'native\t'))
     32         (self.generation / 'source-map.v1').write_text(old_map)
     33         raw = {'base': b'x = t\n', 'source_map': b'!import "base" !Local\nf x = x\n'}
     34         staged = {'base': b'!export *\nx = t\n\n',
     35                   'source_map': (b'!import base *\n!export *\n' +
     36                                  (b'!export * from base\n' if self.root_kind == 'reexport' else b'') +
     37                                  b'f x = x\n\n')}
     38         compiler = '1' * 64
     39         provenance = dict(map_sha256=hashlib.sha256(old_map.encode()).hexdigest(),
     40                           sources={k: hashlib.sha256(v).hexdigest() for k, v in raw.items()},
     41                           compiler_bundle_sha256=compiler)
     42         (self.generation / 'inputs.json').write_text(json.dumps(provenance))
     43         (self.generation / 'status.json').write_text(json.dumps(dict(complete=True, root='source_map')))
     44         for alias, path in [('base', 'base.tri'), ('source_map', 'root.tri')]:
     45             self.repo.joinpath(path).write_bytes(staged[alias])
     46             self.generation.joinpath('overlay', alias + '.tri').write_bytes(staged[alias])
     47             tree = self.put('arboricx.tree-term.v1', b'\0')
     48             manifest = self.put('arboricx.module-manifest.v1', ('arboricx.module-manifest.v1\nexport\tmain\tarboricx.tree-term.v1\t' + tree + '\tarboricx.abi.tree.v1\n').encode())
     49             rows = ['source\t' + digest('arboricx.source.v1', staged[alias]),
     50                     'compiler\t' + compiler,
     51                     'options\t' + digest('arboricx.build-options.v1', b'source-module.reference.v1')]
     52             receipt = self.put('arboricx.build-receipt.v1', ('arboricx.build-receipt.v1\n' + '\n'.join(rows) + '\noutput\t' + manifest + '\n').encode())
     53             key = digest('arboricx.source-build-key.v1', ('arboricx.source-build-key.v1\n' + '\n'.join(rows) + '\n').encode())
     54             self.generation.joinpath('cas/cache/source-build', alias).write_text('\t'.join(['arboricx.source-build-cache.v1', key, manifest, receipt]))
     55 
     56     def put(self, domain, payload):
     57         pin = digest(domain, payload)
     58         path = self.generation / 'cas/objects' / pin[:3] / pin
     59         path.parent.mkdir(parents=True, exist_ok=True); path.write_bytes(payload)
     60         return pin
     61 
     62     def check(self):
     63         return native_header_equivalence(self.generation, self.repo, self.map)
     64 
     65     def test_valid_staged_bytes(self):
     66         rows, proof = self.check()
     67         self.assertEqual(proof['changed'], ['base', 'source_map'])
     68         self.assertEqual(len(rows), 2)
     69 
     70     def test_changed_body(self):
     71         with (self.repo / 'root.tri').open('a') as f: f.write('-- body change\n')
     72         with self.assertRaisesRegex(ValueError, 'staged bytes'): self.check()
     73 
     74     def test_changed_namespace(self):
     75         p = self.repo / 'root.tri'; p.write_text(p.read_text().replace('!import base *', '!import base as B'))
     76         with self.assertRaisesRegex(ValueError, 'staged bytes'): self.check()
     77 
     78     def test_changed_exports(self):
     79         p = self.repo / 'root.tri'; p.write_text(p.read_text().replace('!export *', '!export f'))
     80         with self.assertRaisesRegex(ValueError, 'staged bytes|invalid native export'): self.check()
     81 
     82     def test_changed_map_path(self):
     83         (self.repo / 'other.tri').write_bytes((self.repo / 'base.tri').read_bytes())
     84         self.map.write_text(self.map.read_text().replace('base.tri', 'other.tri'))
     85         with self.assertRaisesRegex(ValueError, 'edges, paths'): self.check()
     86 
     87     def test_incomplete_generation(self):
     88         (self.generation / 'status.json').write_text('{"complete":false,"root":"source_map"}')
     89         with self.assertRaisesRegex(ValueError, 'completed'): self.check()
     90 
     91     def test_overlay_receipt_corruption(self):
     92         # Keep current source equal to the modified overlay: the receipt still
     93         # authenticates the old compiler input and must reject this pair.
     94         for p in [self.repo / 'root.tri', self.generation / 'overlay/source_map.tri']:
     95             with p.open('a') as f: f.write('-- forged overlay\n')
     96         with self.assertRaisesRegex(ValueError, 'receipt mismatch'): self.check()
     97 
     98     def test_checkpoint_key_corruption(self):
     99         p = self.generation / 'cas/cache/source-build/source_map'
    100         parts = p.read_text().split('\t'); parts[1] = '2' * 64; p.write_text('\t'.join(parts))
    101         with self.assertRaisesRegex(ValueError, 'key mismatch'): self.check()
    102 
    103 class ReexportHeaderEquivalenceTests(HeaderEquivalenceTests):
    104     # Exercise the same fail-closed controls with the prelude's re-export policy.
    105     root_kind = 'reexport'
    106 
    107 
    108 # native_library_migration_check
    109 import unittest
    110 
    111 from native_library_migration import native_candidate, alias, terminal_export, reexport_candidate
    112 
    113 class HeaderTests(unittest.TestCase):
    114     def test_local_and_namespace_preserved(self):
    115         body = b'-- body\nf x = N.id x\n'
    116         raw = b'!import "prelude" !Local\n!import "source.core" N\n' + body
    117         candidate, imports = native_candidate(raw, {'prelude': 'prelude', 'source.core': 'source_core'})
    118         self.assertEqual(candidate, b'!import prelude *\n!import source_core as N\n!export *\n'+body)
    119         self.assertEqual(imports, [('prelude', '!Local'), ('source_core', 'N')])
    120 
    121     def test_comments_before_import_preserved(self):
    122         raw = b'-- header comment\n!import "prelude" !Local\nx = t\n'
    123         candidate, _ = native_candidate(raw, {'prelude': 'prelude'})
    124         self.assertTrue(candidate.endswith(b'-- header comment\nx = t\n'))
    125 
    126     def test_explicit_transitive_dependency_is_recorded(self):
    127         raw = b'!import "patterns" !Local\nmain = equal? t t\n'
    128         candidate, imports = native_candidate(raw, {'patterns': 'patterns'}, [('prelude', '!Local')])
    129         self.assertEqual(imports, [('prelude', '!Local'), ('patterns', '!Local')])
    130         self.assertTrue(candidate.startswith(b'!import prelude *\n!import patterns *\n'))
    131         self.assertTrue(candidate.endswith(b'main = equal? t t\n'))
    132 
    133     def test_unknown_dependency_rejected(self):
    134         with self.assertRaisesRegex(ValueError, 'unknown dependency'):
    135             native_candidate(b'!import "missing" !Local\nx = t\n', {})
    136 
    137     def test_malformed_import_rejected(self):
    138         with self.assertRaisesRegex(ValueError, 'unsupported legacy'):
    139             native_candidate(b'!import "prelude"\nx = t\n', {'prelude': 'prelude'})
    140 
    141     def test_native_policy_not_widened(self):
    142         raw = b'!import dep as D\n!export f\nf = D.f\n'
    143         candidate, imports = native_candidate(raw, {'dep': 'dep'})
    144         self.assertEqual(candidate, raw)
    145         self.assertEqual(imports, [('dep', 'D')])
    146 
    147     def test_unknown_native_dependency_rejected(self):
    148         with self.assertRaisesRegex(ValueError, 'unknown native'):
    149             native_candidate(b'!import dep *\n!export f\nf = t\n', {'other': 'other'})
    150 
    151     def test_terminal_selector_requires_explicit_exact_symbol(self):
    152         raw = b'program x = x\nprogram\n\n'
    153         self.assertEqual(terminal_export(raw, 'program'), b'program x = x\n\n')
    154         with self.assertRaisesRegex(ValueError, 'exact bare'):
    155             terminal_export(b'program x = x\nprogram t\n', 'program')
    156 
    157     def test_terminal_selector_requires_authored_definition(self):
    158         with self.assertRaisesRegex(ValueError, 'authored definition'):
    159             terminal_export(b'program\n', 'program')
    160 
    161     def test_terminal_selector_rejects_expression_names(self):
    162         with self.assertRaisesRegex(ValueError, 'invalid terminal'):
    163             terminal_export(b'program t\n', 'program t')
    164 
    165     def test_reexport_directives_precede_body_comments(self):
    166         raw = b'-- example\n!import "dep" !Local\n'
    167         candidate, imports = native_candidate(raw, {'dep':'dep'})
    168         result = reexport_candidate(raw, candidate, imports)
    169         self.assertIn(b'!export *\n!export * from dep\n-- example', result)
    170         self.assertEqual(reexport_candidate(result,result,imports),result)
    171 
    172     def test_reexport_policy_rejects_authored_definitions(self):
    173         with self.assertRaisesRegex(ValueError,'only comments'):
    174             reexport_candidate(b'f = t\n',b'!export *\nf = t\n',[])
    175 
    176     def test_reexport_policy_rejects_namespace_widening(self):
    177         with self.assertRaisesRegex(ValueError,'local imports'):
    178             reexport_candidate(b'-- comment\n',b'!export *\n',[('dep','N')])
    179 
    180     def test_flat_aliases(self):
    181         self.assertEqual(alias('source.export-cache'), 'source_export_cache')
    182 
    183 
    184 # process_rss_check
    185 import subprocess
    186 
    187 import unittest
    188 
    189 from types import SimpleNamespace
    190 
    191 from unittest.mock import patch
    192 
    193 from process_rss import ChildRSS, status_fields
    194 
    195 class ChildRSSTests(unittest.TestCase):
    196     def monitor(self, candidates=(7, 1001)):
    197         monitor = ChildRSS.__new__(ChildRSS)
    198         def still_running(timeout):
    199             raise subprocess.TimeoutExpired('fake-child', timeout)
    200         monitor.proc = SimpleNamespace(pid=7, poll=lambda: None, wait=still_running)
    201         monitor.parent, monitor.namespace_index, monitor.pid = 1000, 1, None
    202         monitor.candidates = lambda: iter(candidates)
    203         return monitor
    204 
    205     def test_namespace_translation_ignores_wrong_host_pid(self):
    206         def read(path):
    207             if str(path) == '/proc/7/status':
    208                 return 'Pid: 7\nPPid: 2\nNSpid: 7\nVmRSS: 999999 kB\n'
    209             return 'Pid: 1001\nPPid: 1000\nNSpid: 1001 7\nVmRSS: 512 kB\n'
    210         monitor = self.monitor()
    211         with patch('pathlib.Path.read_text', read):
    212             self.assertEqual(monitor.sample(), 512)
    213             self.assertEqual(monitor.pid, 1001)
    214 
    215     def test_missing_rss_fails_closed(self):
    216         with patch('pathlib.Path.read_text', return_value='Pid: 1001\nPPid: 1000\nNSpid: 1001 7\n'):
    217             with self.assertRaisesRegex(RuntimeError, 'cannot monitor'):
    218                 self.monitor((1001,)).sample()
    219 
    220     def test_inaccessible_rss_fails_closed(self):
    221         with patch('pathlib.Path.read_text', side_effect=FileNotFoundError):
    222             with self.assertRaisesRegex(RuntimeError, 'cannot monitor'):
    223                 self.monitor().sample()
    224 
    225     def test_cached_pid_reuse_fails_closed(self):
    226         monitor = self.monitor(); monitor.pid = 1001
    227         with patch('pathlib.Path.read_text', return_value='Pid: 1001\nPPid: 1000\nNSpid: 1001 8\nVmRSS: 10 kB\n'):
    228             with self.assertRaisesRegex(RuntimeError, 'cannot monitor'):
    229                 monitor.sample()
    230 
    231     def test_exiting_leader_uses_shared_thread_memory(self):
    232         from pathlib import Path
    233         monitor = self.monitor((1001,))
    234         def read(path):
    235             if str(path) == '/proc/1001/status':
    236                 return 'Pid: 1001\nPPid: 1000\nNSpid: 1001 7\n'
    237             return 'Tgid: 1001\nVmRSS: 321 kB\n'
    238         with patch('pathlib.Path.read_text', read), patch('pathlib.Path.glob', return_value=[Path('/proc/1001/task/1002/status')]):
    239             self.assertEqual(monitor.sample(), 321)
    240 
    241     def test_identified_running_exit_with_no_address_space(self):
    242         monitor = self.monitor((1001,))
    243         def read(path):
    244             if str(path).endswith('/statm'):
    245                 return '0 0 0 0 0 0 0\n'
    246             return 'Pid: 1001\nPPid: 1000\nNSpid: 1001 7\nState: R (running)\n'
    247         with patch('pathlib.Path.read_text', read), patch('pathlib.Path.glob', return_value=[]):
    248             self.assertEqual(monitor.sample(), 0)
    249 
    250     def test_identified_zombie_can_finish_kernel_cleanup(self):
    251         monitor = self.monitor((1001,))
    252         with patch('pathlib.Path.read_text', return_value='Pid: 1001\nPPid: 1000\nNSpid: 1001 7\nState: Z (zombie)\n'), patch('pathlib.Path.glob', return_value=[]):
    253             self.assertIsNone(monitor.sample())
    254 
    255     def test_exited_child_has_no_sample(self):
    256         monitor = self.monitor(); monitor.proc.poll = lambda: 0
    257         self.assertIsNone(monitor.sample())
    258 
    259     def test_address_space_disappears_just_before_reap(self):
    260         monitor = self.monitor()
    261         monitor.proc.wait = lambda timeout: 0
    262         with patch('pathlib.Path.read_text', side_effect=FileNotFoundError):
    263             self.assertIsNone(monitor.sample())
    264 
    265     def test_real_child_is_visible(self):
    266         proc = subprocess.Popen(['sleep', '2'])
    267         try:
    268             monitor = ChildRSS(proc)
    269             sample = monitor.sample()
    270             self.assertIsNotNone(sample)
    271             self.assertGreater(sample, 0)
    272             self.assertTrue(monitor.matches(status_fields(__import__('pathlib').Path(f'/proc/{monitor.pid}/status').read_text())))
    273         finally:
    274             proc.terminate(); proc.wait()
    275 
    276 
    277 # research_session_check
    278 import hashlib
    279 
    280 from pathlib import Path
    281 
    282 import subprocess
    283 
    284 import tempfile
    285 
    286 import time
    287 
    288 import unittest
    289 
    290 from research_session import ResearchSession, ROOT
    291 
    292 class ResearchTests(unittest.TestCase):
    293     def test_prepared_fixture_selections_never_invoke_compiler(self):
    294         with tempfile.TemporaryDirectory() as directory:
    295             source = Path(directory) / 'fixture.tri'
    296             source.write_text('!export *\none = t\ntwo = t t\n')
    297             session = ResearchSession.__new__(ResearchSession)
    298             session.records = {source: {'after_sha256': hashlib.sha256(source.read_bytes()).hexdigest(),
    299                                         'manifest': 'manifest'}}
    300             session.tricu, session.store = 'tricu', Path(directory)
    301             session.object = lambda domain, pin: b'header\nexport\tone\tkind\troot1\tabi\nexport\ttwo\tkind\troot2\tabi\n'
    302             calls = []
    303             session.run = lambda command, seconds: (calls.append(command) or b'', .01)
    304             for name in ['one', 'two', 'one']:
    305                 session.compile(source, [name], Path(directory) / (name + '.arboricx'))
    306             self.assertEqual(len(calls), 3)
    307             self.assertTrue(all(command[1:3] == ['arboricx', 'export'] for command in calls))
    308 
    309     def test_changed_prepared_source_is_rejected(self):
    310         with tempfile.TemporaryDirectory() as directory:
    311             source = Path(directory) / 'fixture.tri'; source.write_text('changed')
    312             session = ResearchSession.__new__(ResearchSession)
    313             session.records = {source: {'after_sha256': '0' * 64}}
    314             with self.assertRaisesRegex(ValueError, 'changed during session'):
    315                 session.compile(source, ['main'], Path(directory) / 'out')
    316 
    317     def test_job_above_twenty_minutes_is_rejected(self):
    318         session = ResearchSession.__new__(ResearchSession)
    319         with self.assertRaisesRegex(ValueError, '1200'):
    320             session.run(['never-started'], 1201)
    321 
    322     def test_expired_whole_harness_cannot_start_another_child(self):
    323         session = ResearchSession.__new__(ResearchSession)
    324         session.deadline = time.monotonic() - 1
    325         with self.assertRaises(subprocess.TimeoutExpired):
    326             session.run(['never-started'], 1200)
    327 
    328     def test_ir_harness_uses_original_authenticated_fixture_path(self):
    329         text = (ROOT / 'test/staging_experiment.py').read_text()
    330         self.assertIn("source = ROOT / 'test/stagingExperiment.tri'", text)
    331         self.assertNotIn("source = root / 'fixtures.tri'", text)
    332         self.assertNotIn('zig', text.lower())
    333 
    334     def test_conditional_fixtures_are_batched(self):
    335         text = (ROOT / 'test/staged_conditional.py').read_text()
    336         body = text.split('    def fixture_for(', 1)[1].split('    for entry in ', 1)[0]
    337         self.assertNotIn('compile(', body)
    338         self.assertIn('arboricx', body)
    339 
    340 
    341 # standard_native_migration_check
    342 from pathlib import Path
    343 
    344 import unittest
    345 
    346 ROOT = Path(__file__).resolve().parent.parent
    347 
    348 DEFERRED_LIBRARIES = {}
    349 
    350 LEGACY_DEMO_FIXTURES = set()
    351 
    352 def legacy(path):
    353     return any(line.startswith('!import "') for line in path.read_text().splitlines())
    354 
    355 class StandardMigrationTests(unittest.TestCase):
    356     def test_remaining_library_imports_are_explicitly_scoped(self):
    357         remaining = {str(path.relative_to(ROOT)) for path in (ROOT / 'lib').rglob('*.tri')
    358                      if legacy(path)}
    359         self.assertEqual(remaining, set(DEFERRED_LIBRARIES))
    360 
    361     def test_application_demos_are_native(self):
    362         demos = [*(ROOT / 'demos').glob('*.tri'), *(ROOT / 'demos/interactionTrees').glob('*.tri')]
    363         self.assertEqual(len(demos), 22)
    364         for path in demos:
    365             with self.subTest(path=path):
    366                 self.assertFalse(legacy(path))
    367                 self.assertTrue(any(line.startswith('!export ') for line in path.read_text().splitlines()))
    368 
    369     def test_demo_inputs_have_no_legacy_imports(self):
    370         remaining = {str(path.relative_to(ROOT)) for path in (ROOT / 'demos').rglob('*.tri')
    371                      if legacy(path)}
    372         self.assertEqual(remaining, LEGACY_DEMO_FIXTURES)
    373 
    374     def test_formatter_and_recursion_are_native(self):
    375         for directory in ['lib/tools', 'lib/recursion']:
    376             for path in (ROOT / directory).rglob('*.tri'):
    377                 with self.subTest(path=path):
    378                     self.assertFalse(legacy(path))
    379                     self.assertTrue(any(line.startswith('!export ') for line in path.read_text().splitlines()))
    380 
    381 
    382 class NativeHeaderTests(unittest.TestCase):
    383     def test_order_and_deduplication(self):
    384         from native_header import native_header
    385         edges, imports = native_header("-- header\n!import math\n!export * from base\n!import math as M\n!source build_only\n!export main\nmain = t\n")
    386         self.assertEqual(edges, ['math', 'base', 'build_only'])
    387         self.assertEqual(imports, [('math', 'math'), ('math', 'M')])
    388 
    389     def test_hash_imports_and_barrels(self):
    390         from native_header import native_header
    391         pin = 'a' * 64
    392         self.assertEqual(native_header('!export * from dep\n')[0], ['dep'])
    393         self.assertEqual(native_header('!import '+pin+' as M\n!export * from '+pin+'\n')[0], [])
    394         with self.assertRaises(ValueError): native_header('!import '+pin+'\n')
    395 
    396     def test_redundant_build_only_edges(self):
    397         from native_header import native_header
    398         for source in ['!source dep\n!import dep\n', '!import dep\n!source dep\n',
    399                        '!source dep\n!export * from dep\n', '!source dep\n!source dep\n']:
    400             with self.subTest(source=source), self.assertRaisesRegex(ValueError, 'redundant'):
    401                 native_header(source)
    402 
    403     def test_old_native_syntax_and_recovery_spelling_rejected(self):
    404         from native_header import native_header
    405         for source in ['!use dep *\n', '!import "dep" !Local\n']:
    406             with self.subTest(source=source), self.assertRaises(ValueError): native_header(source)
    407 
    408 
    409 class GuardedJobTests(unittest.TestCase):
    410     def test_output_and_nonzero_exit(self):
    411         import subprocess
    412         import sys
    413         from process_rss import run_guarded
    414         output, peak = run_guarded([sys.executable, '-c', 'print("transport-ok")'],
    415                                   memory_mib=64, seconds=5)
    416         self.assertEqual(output.strip(), 'transport-ok')
    417         self.assertGreaterEqual(peak, 0)
    418         with self.assertRaises(subprocess.CalledProcessError) as failure:
    419             run_guarded([sys.executable, '-c', 'print("diagnostic"); raise SystemExit(3)'],
    420                         memory_mib=64, seconds=5)
    421         self.assertIn('diagnostic', failure.exception.output)
    422 
    423     def test_time_and_memory_guards(self):
    424         import sys
    425         from process_rss import run_guarded
    426         with self.assertRaisesRegex(RuntimeError, 'wall-time guard'):
    427             run_guarded([sys.executable, '-c', 'import time; time.sleep(10)'],
    428                         memory_mib=64, seconds=0.05)
    429         with self.assertRaisesRegex(RuntimeError, 'RSS guard'):
    430             run_guarded([sys.executable, '-c', 'import time; data=bytearray(8*1024*1024); time.sleep(10)'],
    431                         memory_mib=1, seconds=5)
    432 
    433 
    434 class BootstrapAcceptanceTests(unittest.TestCase):
    435     def setUp(self):
    436         from bootstrap_generations import source_map, snapshot
    437         from bootstrap_acceptance import ARTIFACTS, sha
    438         self.temporary = tempfile.TemporaryDirectory()
    439         self.addCleanup(self.temporary.cleanup)
    440         self.root = Path(self.temporary.name)
    441         self.source = self.root / 'source'
    442         self.source.mkdir()
    443         (self.source / 'root.tri').write_text('!export main\nmain = t\n')
    444         self.runtime = self.root / 'host'
    445         self.runtime.write_bytes(b'host-identity')
    446         self.first, self.second = self.root / 'first', self.root / 'second'
    447         for index, run in enumerate((self.first, self.second)):
    448             (run / 'cas/cache/source-build').mkdir(parents=True)
    449             (run / 'source-map.v1').write_text('arboricx.source-map.v1\nnative\tsource_map\troot\troot.tri\t-\n')
    450             (run / 'cas/cache/source-build/source_map').write_text('version\tkey\tidentical-manifest\treceipt')
    451             (run / 'compiler.arboricx').write_bytes(b'bridge' if index == 0 else b'rebuilt-compiler.arboricx')
    452             (run / 'io-adapter.arboricx').write_bytes(b'adapter')
    453             for name in ARTIFACTS:
    454                 (run / name).write_bytes(name.encode())
    455             text, entries, _ = source_map(run / 'source-map.v1', self.source, 'source_map')
    456             inputs = dict(source_root=str(self.source), root='source_map', backend='stable', fresh_store=True,
    457                           runtime_sha256=sha(self.runtime), adapter_sha256=sha(run / 'io-adapter.arboricx'),
    458                           compiler_bundle_sha256=sha(run / 'compiler.arboricx'), **snapshot(text, entries, self.source))
    459             (run / 'inputs.json').write_text(json.dumps(inputs))
    460             (run / 'status.json').write_text(json.dumps(dict(complete=True, **inputs,
    461                 artifacts={name: dict(sha256=sha(run / name)) for name in ARTIFACTS})))
    462 
    463     def verify(self):
    464         from bootstrap_acceptance import verify
    465         from unittest.mock import patch
    466         # Synthetic outputs test gate policy only; real CAS auditing is separate.
    467         with patch('bootstrap_acceptance.run_guarded', return_value=('synthetic audit', 0)):
    468             return verify(self.first, self.second, self.runtime)
    469 
    470     def test_accepts_matching_policy_records(self):
    471         self.assertTrue(self.verify()['accepted'])
    472 
    473     def test_rejects_wrong_compiler_and_artifact(self):
    474         from bootstrap_acceptance import sha
    475         path = self.second / 'compiler.arboricx'
    476         path.write_bytes(b'unrelated-worker')
    477         for filename in ('inputs.json', 'status.json'):
    478             record_path = self.second / filename
    479             record = json.loads(record_path.read_text())
    480             record['compiler_bundle_sha256'] = sha(path)
    481             record_path.write_text(json.dumps(record))
    482         with self.assertRaisesRegex(ValueError, 'did not execute'):
    483             self.verify()
    484         path.write_bytes(b'rebuilt-compiler.arboricx')
    485         (self.first / 'build-stable.arboricx').write_bytes(b'corrupt')
    486         with self.assertRaisesRegex(ValueError, 'artifact identity'):
    487             self.verify()
    488 
    489     def test_rejects_seeded_or_failed_runs(self):
    490         for field, value, diagnostic in [('fresh_store', False, 'fresh-store'),
    491                                           ('complete', False, 'completed stable')]:
    492             path = self.second / 'status.json'
    493             original = path.read_bytes()
    494             data = json.loads(original)
    495             data[field] = value
    496             path.write_text(json.dumps(data))
    497             with self.assertRaisesRegex(ValueError, diagnostic):
    498                 self.verify()
    499             path.write_bytes(original)
    500 
    501     def test_rejects_changed_source_and_actual_manifests(self):
    502         path = self.source / 'root.tri'
    503         original = path.read_bytes()
    504         path.write_bytes(original + b'-- changed\n')
    505         with self.assertRaisesRegex(ValueError, 'fingerprint'):
    506             self.verify()
    507         path.write_bytes(original)
    508         (self.second / 'cas/cache/source-build/source_map').write_text('version\tkey\tdifferent-manifest\treceipt')
    509         with self.assertRaisesRegex(ValueError, 'manifest reproduction'):
    510             self.verify()
    511 
    512 
    513 class ArchiveRecoveryPolicyTests(unittest.TestCase):
    514     def test_scp_execution_is_not_a_compiler_restore_gate(self):
    515         archive = (ROOT / 'test/bootstrap_archive.py').read_text()
    516         self.assertNotIn('scp_native_validation.py', archive)
    517         self.assertIn('scp_execution_required=False', archive)
    518 
    519 
    520 class SupportProjectionTests(unittest.TestCase):
    521     def setUp(self):
    522         from bundle_dag import merge_bundles, read
    523         self.temporary = tempfile.TemporaryDirectory()
    524         self.addCleanup(self.temporary.cleanup)
    525         self.directory = Path(self.temporary.name)
    526         self.original = self.directory / 'original.arboricx'
    527         self.projected = self.directory / 'projected.arboricx'
    528         leaf = ROOT / 'test/fixtures/false.arboricx'
    529         other = ROOT / 'test/fixtures/true.arboricx'
    530         merge_bundles(self.original, [(leaf, [(next(iter(read(leaf)[3])), 'keep.value')]),
    531                                      (other, [(next(iter(read(other)[3])), 'drop.value')])])
    532         merge_bundles(self.projected, [(self.original, [('keep.value', 'keep.value')])],
    533                       prune_unreachable=True)
    534 
    535     def test_exact_selected_exports_without_unreachable_nodes(self):
    536         from project_support_fixture import verify_projection
    537         self.assertEqual(verify_projection(self.original, self.projected, {'keep'}), 1)
    538 
    539     def test_missing_export_and_changed_kernel_rejected(self):
    540         from bundle_dag import merge_bundles
    541         from project_support_fixture import verify_projection
    542         with self.assertRaisesRegex(ValueError, 'export set'):
    543             verify_projection(self.original, self.projected, {'keep', 'drop'})
    544         merge_bundles(self.projected, [(self.original, [('drop.value', 'keep.value')])],
    545                       prune_unreachable=True)
    546         with self.assertRaisesRegex(ValueError, 'changed a kernel'):
    547             verify_projection(self.original, self.projected, {'keep'})
    548 
    549     def test_unreachable_archived_nodes_rejected(self):
    550         from bundle_dag import merge_bundles
    551         from project_support_fixture import verify_projection
    552         merge_bundles(self.projected, [(self.original, [('keep.value', 'keep.value')])])
    553         with self.assertRaisesRegex(ValueError, 'unreachable'):
    554             verify_projection(self.original, self.projected, {'keep'})
    555 
    556     def test_immutable_projection_origin_fingerprints(self):
    557         # The extraction ledger remains historical: current/rebuilt outputs have
    558         # since been genuinely rebuilt. Never rewrite the original snapshots or
    559         # pretend new compilation is an unchanged projection. Current outputs
    560         # and every rebuilt receipt are authenticated by the Haskell cohort gate.
    561         fixtures = ROOT / 'test/fixtures'
    562         evidence = json.loads((fixtures / 'support-projection.json').read_text())
    563         self.assertEqual(evidence['format'], 'tricu.support-projection.v1')
    564         for name in ['support-origin.v1', 'support-origin.arboricx', 'support-origin-compiler.arboricx']:
    565             self.assertEqual(hashlib.sha256((fixtures / name).read_bytes()).hexdigest(),
    566                              evidence['outputs_sha256'][name], name)
    567 
    568     def test_active_support_is_dependency_closed_without_archive(self):
    569         from native_header import native_header
    570         fixtures = ROOT / 'test/fixtures'
    571         rows = [line.split('\t') for name in ['compiler-library.v1', 'support-library.v1']
    572                 for line in (fixtures / name).read_text().splitlines() if line.startswith('module\t')]
    573         providers = {row[2] for row in rows}
    574         for _, _, alias, path, pin in rows:
    575             self.assertNotIn('consumer', path)
    576             self.assertNotIn('archive/', path)
    577             from fixture_sources import snapshot_bytes
    578             dependencies, _ = native_header(snapshot_bytes(fixtures, pin).decode())
    579             self.assertLessEqual(set(dependencies), providers, alias)
    580         self.assertNotIn('ScpCommand', (ROOT / 'tricu.cabal').read_text())
    581         self.assertFalse((ROOT / 'src/ScpCommand.hs').exists())
    582 
    583 
    584 class SupportPublicationControls(unittest.TestCase):
    585     def test_resource_limits_fail_before_build_or_publication(self):
    586         import contextlib
    587         import io
    588         from native_library_migration import main as compile_candidates
    589         from repair_support_fixture import main as publish_candidates
    590         cases = [
    591             (compile_candidates, ['tool', 'host', 'generation', 'output', '--library', 'socket', '--capabilities', '0']),
    592             (publish_candidates, ['tool', 'host', 'candidates', '--output-directory', 'output', '--capabilities', '0']),
    593             (publish_candidates, ['tool', 'host', 'candidates', '--output-directory', 'output', '--verification-memory-mib', '65537']),
    594         ]
    595         for main, argv in cases:
    596             with self.subTest(argv=argv), patch('sys.argv', argv), contextlib.redirect_stderr(io.StringIO()):
    597                 with self.assertRaises(SystemExit) as error:
    598                     main()
    599                 self.assertEqual(error.exception.code, 2)
    600 
    601     def test_publication_cannot_overwrite_an_existing_directory(self):
    602         from repair_support_fixture import main
    603         with tempfile.TemporaryDirectory() as directory:
    604             marker = Path(directory) / 'marker'
    605             marker.write_bytes(b'keep')
    606             with patch('sys.argv', ['tool', 'host', 'candidates', '--output-directory', directory]):
    607                 with self.assertRaises(FileExistsError):
    608                     main()
    609             self.assertEqual(marker.read_bytes(), b'keep')
    610             self.assertEqual(list(Path(directory).iterdir()), [marker])
    611 
    612 
    613 class CompilerDependencyTests(unittest.TestCase):
    614     def test_current_map_is_exact_compiler_closure_without_patterns(self):
    615         from bootstrap_generations import source_map
    616         _, entries, required = source_map(
    617             ROOT / 'bootstrap/compiler-source-map.v1', ROOT, 'source_map')
    618         self.assertEqual(entries['io'][2], ['base'])
    619         self.assertNotIn('patterns', entries)
    620         self.assertEqual(set(entries), required)
    621 
    622 
    623 class FixtureSourceTests(unittest.TestCase):
    624     def setUp(self):
    625         from fixture_sources import digest
    626         self.temporary = tempfile.TemporaryDirectory()
    627         self.addCleanup(self.temporary.cleanup)
    628         self.root = Path(self.temporary.name)
    629         self.fixtures, self.source = self.root / 'fixtures', self.root / 'source'
    630         self.fixtures.mkdir()
    631         self.source.mkdir()
    632         for cohort, alias in [('compiler-library', 'compiler'), ('support-library', 'support')]:
    633             source = b'!export main\nmain = t\n' + ('-- ' + alias + '\n').encode()
    634             (self.source / (alias + '.tri')).write_bytes(source)
    635             bundle = b'synthetic-policy-bundle-' + alias.encode()
    636             (self.fixtures / (cohort + '.arboricx')).write_bytes(bundle)
    637             (self.fixtures / (cohort + '.v1')).write_text(
    638                 'tricu.' + cohort + '-fixture.v1\nmodule\t' + alias + '\t' + alias + '\t' + alias + '.tri\t'
    639                 + digest('arboricx.source.v1', source) + '\nbundle\t'
    640                 + digest('tricu.' + cohort + '-fixture.v1', bundle) + '\n')
    641         (self.source / 'bootstrap').mkdir()
    642         (self.source / 'bootstrap/compiler-source-map.v1').write_text(
    643             'arboricx.source-map.v1\nnative\tcompiler\tcompiler\tcompiler.tri\t-\n')
    644 
    645     def capture(self):
    646         from fixture_sources import capture
    647         return capture(self.fixtures, self.source)
    648 
    649     def test_reference_and_freshness_are_distinct(self):
    650         from fixture_sources import verify, current_mismatches
    651         self.capture()
    652         self.assertEqual(current_mismatches(self.fixtures, self.source), [])
    653         (self.source / 'compiler.tri').write_text('!import changed *\n!export main\nmain = changed\n')
    654         self.assertEqual(len(verify(self.fixtures)), 2)
    655         self.assertEqual(current_mismatches(self.fixtures, self.source), ['compiler.tri'])
    656 
    657     def test_capture_cannot_bless_edited_sources(self):
    658         (self.source / 'support.tri').write_text('changed')
    659         with self.assertRaisesRegex(ValueError, 'does not match'):
    660             self.capture()
    661         self.assertFalse((self.fixtures / 'reference-source').exists())
    662 
    663     def test_missing_snapshot_never_falls_back_to_matching_working_source(self):
    664         from fixture_sources import verify
    665         with self.assertRaises(FileNotFoundError):
    666             verify(self.fixtures)
    667 
    668     def test_corrupt_snapshot_is_rejected_not_repaired(self):
    669         from fixture_sources import verify
    670         self.capture()
    671         next((self.fixtures / 'reference-source').glob('*.tri')).write_bytes(b'corrupt')
    672         with self.assertRaisesRegex(ValueError, 'digest mismatch'):
    673             verify(self.fixtures)
    674         with self.assertRaisesRegex(ValueError, 'existing reference'):
    675             self.capture()
    676 
    677     def test_corrupt_bundle_is_rejected(self):
    678         from fixture_sources import verify
    679         self.capture()
    680         (self.fixtures / 'compiler-library.arboricx').write_bytes(b'corrupt')
    681         with self.assertRaisesRegex(ValueError, 'bundle digest'):
    682             verify(self.fixtures)
    683 
    684     def test_unsafe_or_duplicate_metadata_is_rejected(self):
    685         from fixture_sources import source_rows
    686         path = self.fixtures / 'compiler-library.v1'
    687         original = path.read_text()
    688         for value in ['../outside', '/outside']:
    689             path.write_text(original.replace('compiler.tri', value))
    690             with self.assertRaisesRegex(ValueError, 'path or digest'):
    691                 source_rows(self.fixtures)
    692         path.write_text(original + original.splitlines()[1] + '\n')
    693         with self.assertRaisesRegex(ValueError, 'duplicate'):
    694             source_rows(self.fixtures)
    695 
    696     def test_cli_reports_reference_success_but_current_failure(self):
    697         import sys
    698         self.capture()
    699         (self.source / 'compiler.tri').write_bytes(b'edited')
    700         base = [sys.executable, str(ROOT / 'test/fixture_sources.py')]
    701         common = ['--fixtures', str(self.fixtures), '--source-root', str(self.source)]
    702         reference = subprocess.run(base + ['verify'] + common, capture_output=True, text=True)
    703         current = subprocess.run(base + ['current'] + common, capture_output=True, text=True)
    704         self.assertEqual(reference.returncode, 0, reference.stderr)
    705         self.assertIn('freshness NOT checked', reference.stdout)
    706         self.assertEqual(current.returncode, 1)
    707         self.assertIn('compiler.tri', current.stderr)
    708 
    709     def test_map_change_is_freshness_failure(self):
    710         from fixture_sources import current_mismatches
    711         self.capture()
    712         (self.source / 'bootstrap/compiler-source-map.v1').write_text('arboricx.source-map.v1\n')
    713         self.assertEqual(current_mismatches(self.fixtures, self.source), ['bootstrap/compiler-source-map.v1'])
    714 
    715 
    716 class SupportPublicationTests(unittest.TestCase):
    717     def test_patterns_relocation_is_explicit_and_identity_checked(self):
    718         from repair_support_fixture import relocate_patterns, updated_module_rows
    719         old = ['module\thttp\thttp\tlib/http.tri\told-source']
    720         record = dict(legacy='patterns', path='lib/patterns.tri')
    721         candidates = {'patterns': (record, b'!export main\nmain = t\n')}
    722         with self.assertRaisesRegex(ValueError, 'membership'):
    723             updated_module_rows(old, candidates)
    724         relocated = relocate_patterns(old, candidates)
    725         self.assertEqual(updated_module_rows(relocated, candidates), relocated)
    726         self.assertEqual(old, ['module\thttp\thttp\tlib/http.tri\told-source'])
    727         with self.assertRaisesRegex(ValueError, 'requires rebuilt'):
    728             relocate_patterns(old, {})
    729         with self.assertRaisesRegex(ValueError, 'identity'):
    730             relocate_patterns(old, {'patterns': (dict(record, path='other.tri'), b't')})
    731         with self.assertRaisesRegex(ValueError, 'already belongs'):
    732             relocate_patterns(relocated, candidates)
    733 
    734     def test_changed_source_has_a_new_authenticated_fingerprint(self):
    735         from repair_support_fixture import updated_module_rows
    736         old = ['module\thttp\thttp\tlib/http.tri\told-source',
    737                'module\tsocket\tsocket\tlib/socket.tri\toriginal-socket']
    738         record = dict(legacy='http', path='lib/http.tri')
    739         payload = b'!export f\nf x = x\n'
    740         rows = updated_module_rows(old, {'http': (record, payload)})
    741         self.assertEqual(rows[0].split('\t')[-1], digest('arboricx.source.v1', payload))
    742         self.assertEqual(rows[1], old[1])
    743         self.assertEqual(old[0].split('\t')[-1], 'old-source')
    744         with self.assertRaisesRegex(ValueError, 'identity'):
    745             updated_module_rows(old, {'http': (dict(record, path='other.tri'), payload)})
    746         with self.assertRaisesRegex(ValueError, 'membership'):
    747             updated_module_rows(old, {'unknown': (record, payload)})
    748 
    749     def test_candidate_replacement_is_explicit_and_complete(self):
    750         from repair_support_fixture import select_candidates
    751         with tempfile.TemporaryDirectory() as directory:
    752             roots = [Path(directory) / str(n) for n in range(2)]
    753             for root in roots:
    754                 root.mkdir()
    755                 (root / 'migration.json').write_text(json.dumps([dict(alias='http', complete=True)]))
    756             with self.assertRaisesRegex(ValueError, 'requires --replace'):
    757                 select_candidates(roots, [])
    758             self.assertEqual(select_candidates(roots, ['http'])['http'][0], roots[1])
    759             with self.assertRaisesRegex(ValueError, 'exactly'):
    760                 select_candidates(roots[:1], ['http'])
    761             (roots[1] / 'migration.json').write_text(json.dumps([dict(alias='http', complete=False)]))
    762             with self.assertRaisesRegex(ValueError, 'incomplete'):
    763                 select_candidates(roots, ['http'])
    764 
    765     def test_receipt_cannot_relabel_source_compiler_or_output(self):
    766         from repair_support_fixture import checked_receipt
    767         source = b'f x = x\n'
    768         payload = ('arboricx.build-receipt.v1\nsource\t' + digest('arboricx.source.v1', source) +
    769                    '\ncompiler\tcompiler\noutput\tmanifest\noptions\t' +
    770                    digest('arboricx.build-options.v1', b'source-module.stable.v1') + '\n').encode()
    771         checked_receipt(payload, source, 'manifest', 'compiler')
    772         for raw, text, manifest, compiler in [
    773             (payload, source + b'-- changed\n', 'manifest', 'compiler'),
    774             (payload, source, 'wrong', 'compiler'),
    775             (payload, source, 'manifest', 'wrong'),
    776             (payload + b'output\tmanifest\n', source, 'manifest', 'compiler'),
    777             (payload.replace(digest('arboricx.build-options.v1', b'source-module.stable.v1').encode(),
    778                              digest('arboricx.build-options.v1', b'source-module.reference.v1').encode()),
    779              source, 'manifest', 'compiler'),
    780         ]:
    781             with self.subTest(manifest=manifest, compiler=compiler), self.assertRaisesRegex(ValueError, 'receipt mismatch'):
    782                 checked_receipt(raw, text, manifest, compiler)
    783 
    784     def test_old_consumer_receipt_cannot_pin_an_old_support_provider(self):
    785         from repair_support_fixture import check_support_dependencies
    786         providers = dict(http='current-http', socket='current-socket')
    787         rows = [['local', 'http', 'current-http', 'facade-source'],
    788                 ['pin', 'http', 'current-http', 'parseHeaders', 'tree']]
    789         check_support_dependencies(rows, providers)
    790         for bad in ([['local', 'http', 'old-http', 'source']],
    791                     [rows[0], ['pin', 'http', 'old-http', 'parseHeaders', 'tree']],
    792                     [rows[0], rows[0]], [['local', 'http']], [['pin', 'http']]):
    793             with self.subTest(bad=bad), self.assertRaises(ValueError):
    794                 check_support_dependencies(bad, providers)
    795 
    796 
    797 class LocalControlHarnessTests(unittest.TestCase):
    798     def test_independent_case_diagnostic_and_ast_corpus(self):
    799         from local_control import cases, failures, ast_cases
    800         rows = cases()+failures()+ast_cases()
    801         self.assertEqual(len(rows),len({row[0] for row in rows}))
    802         labels = {row[0] for row in rows}
    803         for label in ('case-ignored-scrutinee', 'case-unused-loop', 'case-pin-collision',
    804                       'rec-nested-capture', 'rec-no-mutual', 'plain-let-shadow',
    805                       'rec-value-before-missing', 'case-ast', 'rec-ast'):
    806             self.assertIn(label,labels)
    807         from http_headers import expect
    808         for _,_,expected in ast_cases():
    809             with self.assertRaises(ValueError):
    810                 expect(expected+b'\0', expected, 'extra AST field')
    811 
    812     def test_candidate_source_guard_rejects_old_facade_and_source_edits(self):
    813         from native_library_migration import checked_candidate_source
    814         import tempfile, hashlib
    815         from pathlib import Path
    816         with tempfile.TemporaryDirectory() as directory:
    817             root = Path(directory); workspace = root/'workspace'; workspace.mkdir()
    818             source = b'!export value\nvalue = 23\n'
    819             (root/'library.tri').write_bytes(source)
    820             staged = workspace/'source_ast.tri'; staged.write_bytes(source)
    821             record = dict(path='library.tri', alias='source_ast', after_sha256=hashlib.sha256(source).hexdigest())
    822             self.assertEqual(checked_candidate_source(workspace, record, root), source)
    823             staged.write_bytes(b'!export * from '+b'0'*64+b'\n')
    824             with self.assertRaises(ValueError): checked_candidate_source(workspace, record, root)
    825             staged.write_bytes(source); (root/'library.tri').write_bytes(source+b'-- changed\n')
    826             with self.assertRaises(ValueError): checked_candidate_source(workspace, record, root)
    827 
    828 
    829 class RegistryServerHarnessTests(unittest.TestCase):
    830     def test_independent_oracles_and_explicit_changes(self):
    831         from registry_server import pure_cases, effect_cases, demo_cases
    832         rows = pure_cases() + effect_cases() + list(demo_cases())
    833         self.assertEqual(len(rows), len({row.label for row in rows}))
    834         self.assertEqual(sum(c.label.startswith('hex-byte-') for c in rows), 256)
    835         self.assertEqual(sum(c.label.startswith('hash-invalid-at-') for c in rows), 64)
    836         self.assertEqual(sum(c.label.startswith('demo-init-failure-') for c in rows), 7)
    837         for c in rows:
    838             self.assertIsInstance(c.expected, bytes)
    839             self.assertEqual(bool(c.change), c.previous is not None)
    840             if c.previous is not None:
    841                 self.assertNotEqual(c.expected, c.previous)
    842 
    843     def test_traversal_never_requests_filesystem_io(self):
    844         import posixpath
    845         from registry_server import effect_cases, finished, result, number, bs, DIAGNOSTIC, action, ROOT, path
    846         rows = effect_cases()
    847         for c in rows:
    848             if c.label.startswith('get-reject-'):
    849                 self.assertEqual(c.expected, finished(result(False, number(400), bs(DIAGNOSTIC))))
    850             if c.label.startswith('write-reject-'):
    851                 self.assertEqual(c.expected, finished(result(False, bs(b'sha256Hex: '+DIAGNOSTIC)),
    852                                                       [action(28, bs(b'body'))]))
    853                 self.assertEqual(c.previous_export, 'putBundleWithHash')
    854         self.assertFalse(posixpath.normpath(path(b'../../../escaped')).startswith(ROOT+b'/'))
    855 
    856     def test_prior_defects_cannot_pass_current_oracles(self):
    857         from registry_server import effect_cases, demo_cases
    858         from http_pipeline import check_case
    859         for c in effect_cases()+list(demo_cases()):
    860             if c.previous is not None:
    861                 check_case(c, c.expected, c.previous)
    862                 with self.assertRaises(ValueError):
    863                     check_case(c, c.previous, c.previous)
    864 
    865     def test_wrong_path_and_atomic_operation_are_detected(self):
    866         from registry_server import action, bs, pair, finished, result, expect
    867         output = result(True, bs(b'hash'))
    868         write = action(33, pair(bs(b'/registry/objects/abc/hash.arboricx'), bs(b'data')))
    869         expected = finished(output, [write])
    870         for trace in ([], [action(22, pair(bs(b'/registry/tmp/hash.tmp'), bs(b'data')))],
    871                       [action(33, pair(bs(b'/outside'), bs(b'data')))], [write, write]):
    872             with self.assertRaises(ValueError):
    873                 expect(finished(output, trace), expected, 'mutated publication')
    874 
    875 
    876 class HttpPipelineHarnessTests(unittest.TestCase):
    877     def test_changes_have_explicit_old_and_independent_new_results(self):
    878         from http_pipeline import pure_cases, effect_cases
    879         rows = pure_cases() + effect_cases()
    880         self.assertEqual(len(rows), len({row.label for row in rows}))
    881         changes = {row.label for row in rows if row.previous is not None}
    882         self.assertEqual(changes, {'body-eof-', 'body-eof-6162', 'receive-recv-failure',
    883             'handler-pure-too-large', 'handler-io-too-large', 'handler-pure-header-limit',
    884             'handler-io-header-limit', 'handler-pure-body-eof', 'handler-io-body-eof', 'status-413'})
    885         for row in rows:
    886             self.assertIsInstance(row.expected, bytes)
    887             if row.previous is not None:
    888                 self.assertNotEqual(row.expected, row.previous)
    889 
    890     def test_reference_modes_never_disable_subject_oracle(self):
    891         from http_pipeline import Case, check_case
    892         row = Case('changed', 'probe', (), b'\0', previous=b'\1\0')
    893         check_case(row, row.expected, row.previous)
    894         check_case(row, row.expected, row.expected, reference_current=True)
    895         for actual, before, current in [(row.previous, row.previous, False),
    896                                         (row.previous, row.expected, True),
    897                                         (row.expected, row.previous, True),
    898                                         (row.expected, row.expected, False)]:
    899             with self.assertRaises(ValueError):
    900                 check_case(row, actual, before, current)
    901 
    902     def test_both_router_paths_have_success_failure_and_cleanup_cases(self):
    903         from http_pipeline import effect_cases
    904         labels = {row.label for row in effect_cases()}
    905         for flavor in ('pure', 'io'):
    906             for suffix in ('get', 'post', 'head', 'bad-line', 'bad-header-head', 'bad-method',
    907                            'bad-version-head', 'bad-target-head', 'duplicate', 'coding-head',
    908                            'too-large', 'header-eof', 'header-limit', 'body-fragmented',
    909                            'body-recv-error', 'body-eof', 'body-send-error', 'body-close-error'):
    910                 self.assertIn('handler-' + flavor + '-' + suffix, labels)
    911         for control in ('pure', 'extra-script', 'unscripted', 'wrong-tag', 'abort', 'bind', 'fuel'):
    912             self.assertIn('driver-' + control, labels)
    913 
    914     def test_trace_oracle_detects_missing_reordered_or_wrong_effect_payloads(self):
    915         from http_pipeline import action, finished, number, byte_tree, pair, result, LEAF
    916         from http_headers import expect
    917         send = action(77, pair(number(7), byte_tree(b'response')))
    918         close = action(71, number(7))
    919         expected = finished(result(True, LEAF), [send, close])
    920         for trace in ([send], [close, send], [send, close, close],
    921                       [action(77, pair(number(7), byte_tree(b'wrong'))), close]):
    922             with self.assertRaises(ValueError):
    923                 expect(finished(result(True, LEAF), trace), expected, 'trace mutation')
    924 
    925 
    926 class HttpLengthHarnessTests(unittest.TestCase):
    927     def test_length_corpus_covers_bytes_and_policies_without_running_artifacts(self):
    928         from http_lengths import cases, BAD_REQUEST
    929         rows = cases(include_costs=False)
    930         self.assertEqual(len(rows), len({row.label for row in rows}))
    931         self.assertEqual(sum(row.label.startswith('value-byte-') for row in rows), 256)
    932         self.assertEqual(sum(row.label.startswith('duplicate-') for row in rows), 25)
    933         for row in rows:
    934             if row.label.startswith(('duplicate-', 'coding-')):
    935                 self.assertEqual(row.expected, BAD_REQUEST)
    936                 self.assertFalse(row.compare_reference)
    937             self.assertEqual(len(row.arguments), 3 if row.export == 'readBody' else 1)
    938 
    939     def test_rejections_require_complete_pure_action_not_just_a_status(self):
    940         from http_lengths import cases, pair, number, BAD_REQUEST, TOO_LARGE
    941         from http_headers import expect
    942         rows = cases(include_costs=False)
    943         by_name = {row.label: row for row in rows}
    944         for row in rows:
    945             if row.label.startswith('no-io-'):
    946                 original = by_name[row.label[len('no-io-'):]]
    947                 self.assertIn(original.expected, (BAD_REQUEST, TOO_LARGE))
    948                 self.assertEqual(row.expected, pair(number(0), original.expected))
    949                 with self.assertRaises(ValueError):
    950                     expect(pair(number(1), original.expected), row.expected, row.label)
    951 
    952     def test_reference_comparisons_never_feed_large_valid_numbers_to_old_parser(self):
    953         from http_lengths import cases, length_result, BAD_REQUEST, pair, number, result, byte_tree, LEAF
    954         safe_results = {BAD_REQUEST, length_result(None), length_result(0), length_result(1), length_result(42),
    955                         pair(number(0), result(True, LEAF, byte_tree(b'body')))}
    956         for row in cases(include_costs=False):
    957             if row.compare_reference:
    958                 self.assertIn(row.expected, safe_results, row.label)
    959 
    960 
    961 class HttpHeaderHarnessTests(unittest.TestCase):
    962     """Cheap oracle/identity controls only; artifact execution stays opt-in."""
    963 
    964     def test_syntax_changes_have_explicit_reasons_and_independent_oracles(self):
    965         from http_headers import cases, syntax_cases
    966         old = {case.label: case for case in cases()}
    967         rows = syntax_cases()
    968         self.assertEqual(len(rows), len({row.label for row in rows}))
    969         self.assertEqual(sum(row.label.startswith('name-byte-') for row in rows), 256)
    970         self.assertEqual(sum(row.label.startswith('value-byte-') for row in rows), 256)
    971         self.assertTrue(any(row.export == 'parseRequestLine' for row in rows))
    972         for row in rows:
    973             if row.change:
    974                 self.assertIsNotNone(row.expected, row.label)
    975                 if row.label in old:
    976                     self.assertEqual(row.reference_expected, old[row.label].expected)
    977             elif row.label in old:
    978                 self.assertEqual(row, old[row.label])
    979 
    980     def test_only_approved_nonbyte_cases_are_retired(self):
    981         from http_headers import cases, syntax_cases, retired_case
    982         historical = cases()
    983         current = {case.label for case in syntax_cases()}
    984         retired = {case.label for case in historical if retired_case(case)}
    985         self.assertEqual(len(retired), 103)
    986         self.assertEqual({case.label for case in historical} - current, retired)
    987         self.assertEqual(sum(case.label.startswith('ascii-') for case in syntax_cases()), 256)
    988         self.assertIn('headers-shape-5', current)
    989         self.assertIn('headers-shape-6', current)
    990 
    991     def test_changed_expectations_do_not_disable_result_or_reference_checks(self):
    992         from dataclasses import replace
    993         from http_headers import syntax_cases, check_case, LEAF
    994         changed = next(row for row in syntax_cases() if row.label == 'bare-lf')
    995         check_case(changed, changed.reference_expected, changed.expected)
    996         for before, after in [(changed.reference_expected, changed.reference_expected),
    997                               (LEAF, changed.expected)]:
    998             with self.assertRaisesRegex(ValueError, 'complete result differs'):
    999                 check_case(changed, before, after)
   1000         with self.assertRaisesRegex(ValueError, 'independent oracle'):
   1001             check_case(replace(changed, expected=None), LEAF, LEAF)
   1002         unchanged = next(row for row in syntax_cases() if row.label == 'headers-shape-5')
   1003         with self.assertRaisesRegex(ValueError, 'differential'):
   1004             check_case(unchanged, LEAF, b'\x01\x00')
   1005         with self.assertRaisesRegex(ValueError, 'independent oracle'):
   1006             check_case(unchanged, None, LEAF)
   1007 
   1008     def test_independent_tree_encodings(self):
   1009         from http_headers import number, pair, tree_list, byte_tree, parse_tree_output
   1010         self.assertEqual(number(0), b'\x00')
   1011         self.assertEqual(number(1), b'\x02\x01\x00\x00')
   1012         self.assertEqual(number(2), b'\x02\x00\x02\x01\x00\x00')
   1013         self.assertEqual(pair(b'\x00', b'\x01\x00'), b'\x02\x00\x01\x00')
   1014         self.assertEqual(tree_list([number(65)]), byte_tree(b'A'))
   1015         self.assertEqual(parse_tree_output(' 2010\n'), b'\x02\x00\x01\x00')
   1016 
   1017     def test_renderer_rejects_partial_or_extra_output(self):
   1018         from http_headers import parse_tree_output
   1019         for text in ('', '1', '20', '00', '0 error', '3', '0\n0'):
   1020             with self.subTest(text=text), self.assertRaises(ValueError):
   1021                 parse_tree_output(text)
   1022 
   1023     def test_corpus_is_named_and_policy_characterization_is_explicit(self):
   1024         from http_headers import cases, EXPORTS
   1025         rows = cases()
   1026         self.assertEqual(len(rows), len({row.label for row in rows}))
   1027         self.assertEqual({row.export for row in rows}, set(EXPORTS))
   1028         self.assertEqual(sum(row.label.startswith('ascii-') for row in rows), 256)
   1029         for row in rows:
   1030             self.assertEqual(row.expected is None, row.category == 'differential-only')
   1031         by_name = {row.label: row for row in rows}
   1032         for label in ('embedded-cr', 'bare-lf', 'space-name', 'eof-header', 'duplicates'):
   1033             # Duplicate preservation is ordinary parser behavior; deciding how
   1034             # particular headers interact belongs to a later semantic slice.
   1035             expected = 'independent' if label == 'duplicates' else 'characterization'
   1036             self.assertEqual(by_name[label].category, expected)
   1037 
   1038     def test_complete_result_comparison_rejects_wrong_fields(self):
   1039         from http_headers import cases, expect, headers, reply, diagnostic
   1040         by_name = {row.label: row for row in cases()}
   1041         wrong = [
   1042             ('ascii-065', by_name['ascii-064'].expected),
   1043             ('blank', headers([], b'wrong remainder')),
   1044             ('reply-missing-colon', reply(False, diagnostic(1, b'colon'), consumed=False)),
   1045             ('reply-missing-colon', reply(False, diagnostic(2, b'colon'))),
   1046         ]
   1047         for label, value in wrong:
   1048             with self.subTest(label=label), self.assertRaisesRegex(ValueError, 'complete result differs'):
   1049                 expect(by_name[label].expected, value, label)
   1050 
   1051     def test_reference_integrity_and_recorded_source(self):
   1052         from http_headers import reference, ROOT
   1053         bundle, metadata = reference(ROOT / 'test/fixtures')
   1054         self.assertTrue(bundle.is_file())
   1055         self.assertEqual(metadata['source_path'], 'lib/http.tri')
   1056 
   1057     def test_corrupt_reference_metadata_bundle_and_source_fail(self):
   1058         import shutil
   1059         from http_headers import reference, ROOT
   1060         fixtures = ROOT / 'test/fixtures'
   1061         metadata = json.loads((fixtures / 'http-headers-reference.json').read_text())
   1062         paths = ['http-headers-reference.json', 'http-headers-reference.arboricx',
   1063                  'reference-source/' + metadata['source_digest'] + '.tri']
   1064         for damaged in paths:
   1065             with self.subTest(damaged=damaged), tempfile.TemporaryDirectory() as directory:
   1066                 destination = Path(directory)
   1067                 for path in paths:
   1068                     (destination / path).parent.mkdir(parents=True, exist_ok=True)
   1069                     shutil.copyfile(fixtures / path, destination / path)
   1070                 with (destination / damaged).open('ab') as output:
   1071                     output.write(b'corrupt')
   1072                 with self.assertRaises(ValueError):
   1073                     reference(destination)
   1074 
   1075 
   1076 class ArboricxReaderOracleTests(unittest.TestCase):
   1077     def test_cases_have_unique_names_and_independent_truncation_oracles(self):
   1078         from arboricx_readers import cases
   1079         rows = list(cases())
   1080         self.assertEqual(len(rows), 401)
   1081         self.assertEqual(len({r[1] for r in rows}), len(rows))
   1082         for module in ['common', 'nodes', 'manifest']:
   1083             self.assertTrue(any(r[0] == module and '-cut-' in r[1] and r[4] is not None for r in rows))
   1084 
   1085     def test_binary_failure_keeps_field_start_not_consumed_prefix(self):
   1086         from arboricx_readers import Reader, Failure
   1087         from http_headers import result, number
   1088         from stable_bundle import byte_tree
   1089         r = Reader(b'xx')
   1090         r.take(1)
   1091         with self.assertRaises(Failure) as caught:
   1092             r.take(2)
   1093         self.assertEqual(caught.exception.tree, result(False, number(1), byte_tree(b'x')))
   1094 
   1095 
   1096 class SocketConvenienceOracleTests(unittest.TestCase):
   1097     def test_only_post_acquisition_failures_change(self):
   1098         from socket_conveniences import cases
   1099         rows = list(cases())
   1100         self.assertEqual(len(rows), 14)
   1101         self.assertEqual(len({r.label for r in rows}), len(rows))
   1102         changed = [r for r in rows if r.previous is not None]
   1103         self.assertEqual(len(changed), 6)
   1104         self.assertTrue(all('failure-close-' in r.label and r.previous != r.expected for r in changed))
   1105 
   1106 
   1107 class ParserControlOracleTests(unittest.TestCase):
   1108     def test_complete_reply_and_poison_probe_inventory(self):
   1109         from parser_control import cases
   1110         rows = list(cases())
   1111         self.assertEqual(len(rows), 252)
   1112         self.assertEqual(len({r[0] for r in rows}), len(rows))
   1113         self.assertEqual(sum('ignored-fields-' in r[0] for r in rows), 4)
   1114         self.assertTrue(any(r[0] == 'unselected-failure' for r in rows))
   1115         self.assertTrue(any(r[0] == 'unselected-success' for r in rows))
   1116 
   1117 
   1118 
   1119 class BaseWorkloadTests(unittest.TestCase):
   1120     def test_generated_worker_variants(self):
   1121         from base_workloads import candidate, WORKERS, reference_source
   1122         source = reference_source()
   1123         for variant in ['manual', 'direct', 'patterned', 'captured']:
   1124             text = candidate(source, variant)
   1125             for public in WORKERS:
   1126                 self.assertIn(public + ' ', text)
   1127             if variant != 'manual':
   1128                 self.assertNotRegex(text, r'\bself\b')
   1129         self.assertIn('map f l = (let rec', candidate(source, 'captured'))
   1130         self.assertIn('foldl f x l = (let rec', candidate(source, 'captured'))
   1131         self.assertIn('reverse_ other acc = other', candidate(source, 'patterned'))
   1132 
   1133     def test_large_rendered_result_before_rts_residency(self):
   1134         from base_workloads import measurements
   1135         with tempfile.TemporaryDirectory() as d:
   1136             path = Path(d) / 'sample.log'
   1137             rows = [dict(profile=name, wall_ns=i * 100, allocated_bytes=i * 10,
   1138                          gc_elapsed_ns=i, live_bytes=7)
   1139                     for i, name in enumerate(['load-begin', 'load-end',
   1140                                               'invoke-begin-1', 'invoke-end-1',
   1141                                               'observe-begin-1', 'observe-end-1'])]
   1142             # Must not perform an unanchored numeric regex search over this line.
   1143             path.write_text('\n'.join(json.dumps(r) for r in rows) + '\n' +
   1144                             '0' * 200000 + ' 9,999 bytes allocated in the heap\n' +
   1145                             '   1,234 bytes maximum residency (2 samples)\n')
   1146             actual = measurements(path)
   1147             self.assertEqual(actual['rts_max_residency_bytes'], 1234)
   1148             self.assertEqual(actual['invoke_wall_ns'], 300)
   1149             self.assertEqual(actual['invoke_allocated_bytes'], 30)
   1150             self.assertEqual(actual['measurement_scope'], 'invocation-through-rendered-output-v1')
   1151 
   1152     def test_workload_independent_oracles(self):
   1153         from base_workloads import workloads, encode
   1154         rows = {name: (argument, expected) for name, argument, expected in workloads(1024)}
   1155         data = bytes(rows['lower'][0])
   1156         self.assertEqual(bytes(rows['lower'][1]), data.lower())
   1157         self.assertEqual(rows['splitLines'][1], [list(x) for x in data.split(b'\n')])
   1158         self.assertEqual(rows['sumRows'][1], [sum(x) for x in rows['sumRows'][0]])
   1159         self.assertEqual(len(rows['prefixDigits'][1]), 20)
   1160         self.assertEqual(encode([0, 1]), b'\2\0\2\2\1\0\0\0')
   1161 
   1162     def test_summary_discards_warmup(self):
   1163         from base_workloads import summarize
   1164         fields = ['invoke_wall_ns', 'invoke_allocated_bytes', 'invoke_gc_elapsed_ns',
   1165                   'load_wall_ns', 'load_allocated_bytes', 'post_invoke_live_bytes',
   1166                   'rts_max_residency_bytes', 'peak_kib', 'seconds']
   1167         rows = [dict(variant='manual', workload='lower', size=64, warmup=(v == 999),
   1168                      **{k: v for k in fields}) for v in [999, 2, 4, 8]]
   1169         result = summarize(rows)[0]
   1170         self.assertEqual(result['repetitions'], 3)
   1171         self.assertEqual(result['invoke_wall_ns'], dict(median=4, minimum=2, maximum=8))
   1172 
   1173 
   1174 
   1175 class BaseReadableOracleTests(unittest.TestCase):
   1176     def test_small_bounded_inventory(self):
   1177         from base_readable import cases, edges
   1178         from source_order import end
   1179         rows = list(edges())
   1180         self.assertEqual(len(rows), 30)
   1181         self.assertEqual(len({row[0] for row in rows}), 30)
   1182         for _, _, argument, expected in rows:
   1183             self.assertEqual(end(argument), len(argument))
   1184             self.assertEqual(end(expected), len(expected))
   1185         for size in [64, 1024]:
   1186             workloads = list(cases(size))
   1187             self.assertEqual(len(workloads), 8)
   1188             for _, argument, expected in workloads:
   1189                 self.assertEqual(end(argument), len(argument))
   1190                 self.assertEqual(end(expected), len(expected))
   1191 
   1192 
   1193 class CollectionBatchTests(unittest.TestCase):
   1194     def test_oracles_are_complete_trees_with_unique_short_labels(self):
   1195         from collections_batch import cases, effects
   1196         from source_order import end
   1197         rows = cases() + effects()
   1198         self.assertEqual(len({row[0] for row in rows}), len(rows))
   1199         for label, _, argument, expected in rows:
   1200             self.assertLess(len(label), 120)
   1201             self.assertEqual(end(argument), len(argument))
   1202             self.assertEqual(end(expected), len(expected))
   1203 
   1204     def test_workspace_audit_rejects_changed_sources_and_objects(self):
   1205         from collections_batch import audit_workspace
   1206         with tempfile.TemporaryDirectory() as directory:
   1207             workspace, store, compiler, tree, restore = self.make_workspace(Path(directory))
   1208             self.assertEqual(set(audit_workspace(workspace, store, compiler)), {'a', 'b'})
   1209             source = workspace/'a.tri'
   1210             source.write_text('!export value\nvalue = t t\n')
   1211             with self.assertRaisesRegex(ValueError, 'source'):
   1212                 audit_workspace(workspace, store, compiler)
   1213             source.write_text('!export value\nvalue = t\n')
   1214             (store/'objects'/tree[:3]/tree).write_bytes(b'corrupt')
   1215             with self.assertRaisesRegex(AssertionError, 'object digest'):
   1216                 audit_workspace(workspace, store, compiler)
   1217 
   1218     def test_workspace_audit_rejects_stale_dependency_and_export_pins(self):
   1219         from collections_batch import audit_workspace
   1220         with tempfile.TemporaryDirectory() as directory:
   1221             workspace, store, compiler, tree, restore = self.make_workspace(Path(directory))
   1222             restore(stale_receipt=True)
   1223             with self.assertRaisesRegex(AssertionError, 'stale candidate dependency'):
   1224                 audit_workspace(workspace, store, compiler)
   1225             restore(stale_pin=True)
   1226             with self.assertRaisesRegex(AssertionError, 'stale candidate export pin'):
   1227                 audit_workspace(workspace, store, compiler)
   1228 
   1229     @staticmethod
   1230     def make_workspace(root):
   1231         workspace, store = root/'workspace', root/'store'
   1232         workspace.mkdir();(store/'cache/source-build').mkdir(parents=True)
   1233         compiler = 'a'*64
   1234         def obj(domain, payload):
   1235             pin = digest(domain, payload)
   1236             path = store/'objects'/pin[:3]/pin;path.parent.mkdir(parents=True, exist_ok=True)
   1237             path.write_bytes(payload);return pin
   1238         tree = obj('arboricx.tree-term.v1', b'\0')
   1239         sources = {'a': '!export value\nvalue = t\n',
   1240                    'b': '!import a *\n!export main\nmain = value\n'}
   1241         manifests = {alias: obj('arboricx.module-manifest.v1',
   1242             ('arboricx.module-manifest.v1\nexport\t'+name+'\tarboricx.tree-term.v1\t'+tree+'\tarboricx.abi.tree.v1\n').encode())
   1243             for alias, name in [('a', 'value'), ('b', 'main')]}
   1244         for alias, source in sources.items():
   1245             (workspace/(alias+'.tri')).write_text(source)
   1246         def receipt(alias, extra=''):
   1247             payload = ('arboricx.build-receipt.v1\nsource\t'+digest('arboricx.source.v1', sources[alias].encode())+
   1248                 '\ncompiler\t'+compiler+'\noptions\t'+digest('arboricx.build-options.v1', b'source-module.stable.v1')+
   1249                 '\noutput\t'+manifests[alias]+'\n'+extra).encode()
   1250             pin = obj('arboricx.build-receipt.v1', payload)
   1251             (store/'cache/source-build'/alias).write_text('arboricx.source-build-cache.v1\t'+'0'*64+'\t'+manifests[alias]+'\t'+pin)
   1252             return pin
   1253         a_receipt = receipt('a')
   1254         def restore(stale_receipt=False, stale_pin=False):
   1255             receipt('b', 'local\ta\t'+manifests['a']+'\t'+('b'*64 if stale_receipt else a_receipt)+
   1256                 '\npin\ta\t'+manifests['a']+'\tvalue\t'+('c'*64 if stale_pin else tree)+'\n')
   1257         restore()
   1258         return workspace, store, compiler, tree, restore
   1259 
   1260 
   1261 class NumericBatchTests(unittest.TestCase):
   1262     def test_independent_numeric_oracles_are_complete_trees(self):
   1263         from numeric_batch import cases
   1264         from source_order import end
   1265         rows = cases()
   1266         self.assertEqual(len({row[0] for row in rows}), len(rows))
   1267         for label, _, argument, expected in rows:
   1268             self.assertLess(len(label), 120)
   1269             self.assertEqual(end(argument), len(argument))
   1270             self.assertEqual(end(expected), len(expected))
   1271 
   1272     def test_zero_divisor_and_shape_oracles_are_distinct(self):
   1273         from numeric_batch import cases, error, n, pair, tree_list
   1274         rows = cases()
   1275         checked = next(expected for _, name, argument, expected in rows
   1276                        if name == 'checkedBinary' and argument == pair(n(7), n(0)))
   1277         self.assertTrue(checked.endswith(tree_list([error('non-zero')]*3)))
   1278         self.assertNotEqual(error('non-zero'), n(0))
   1279         self.assertNotEqual(error('not a natural number'), error('non-zero'))
   1280 
   1281 
   1282 class BundleBatchTests(unittest.TestCase):
   1283     def test_complete_independent_oracles(self):
   1284         from bundle_batch import cases
   1285         from source_order import end
   1286         rows = cases()
   1287         self.assertEqual(len(rows), len({row[0] for row in rows}))
   1288         for label, _, _, arguments, expected in rows:
   1289             self.assertLess(len(label), 120)
   1290             for tree in arguments + [expected]:
   1291                 self.assertEqual(end(tree), len(tree))
   1292 
   1293     def test_suffix_and_rejection_controls_remain_present(self):
   1294         from bundle_batch import cases, byte_tree, n
   1295         from source_order import field
   1296         rows = cases()
   1297         self.assertTrue(any(export == 'prefix' and field(expected, 1, 1) == byte_tree(b'TAIL')
   1298                             for _, _, export, _, expected in rows))
   1299         for code in [7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]:
   1300             self.assertTrue(any(expected.startswith(b'\2\0\2') and field(expected, 1, 0) == n(code)
   1301                                 for _, _, _, _, expected in rows), code)
   1302 
   1303 
   1304 class ToolingBatchTests(unittest.TestCase):
   1305     def test_complete_models_and_graph_ready_order(self):
   1306         from tooling_batch_models import cases, graph_result, n, result
   1307         from source_order import end
   1308         # A newly ready earlier entry precedes the already-ready later one.
   1309         self.assertEqual(graph_result([(0,[1]),(1,[]),(2,[])]), result(True,n([1,0,2])))
   1310         self.assertEqual(graph_result([(0,[1]),(1,[2]),(2,[1])]), result(False,n(1)))
   1311         rows=cases()
   1312         self.assertEqual(len(rows),len({row[0] for row in rows}))
   1313         for _,_,argument,expected in rows:
   1314             for tree in [argument,expected]:self.assertEqual(end(tree),len(tree))
   1315 
   1316     def test_formatter_oracles_preserve_tokens_and_line_counts(self):
   1317         from tooling_batch_models import format_cases
   1318         import re
   1319         tokens=re.compile(r'"(?:\\.|[^"\\])*"|\|-.*?-\||--[^\r\n]*|[^\s]',re.S)
   1320         for label,source,expected in format_cases():
   1321             self.assertEqual(tokens.findall(source),tokens.findall(expected),label)
   1322             self.assertEqual(source.count('\n'),expected.count('\n'),label)
   1323 
   1324 
   1325 class SourceFoundationOracleTests(unittest.TestCase):
   1326     def test_corpus_has_complete_trees_and_unique_labels(self):
   1327         from source_foundations_models import corpus
   1328         from source_order import end
   1329         rows = corpus(consumers=True)
   1330         self.assertEqual(len(rows), len({row[0] for row in rows}))
   1331         for label, _, argument, expected in rows:
   1332             self.assertEqual(end(argument), len(argument), label)
   1333             self.assertEqual(end(expected), len(expected), label)
   1334 
   1335     def test_shift_preserves_bound_indices_and_opaque_trees(self):
   1336         from source_foundations_models import Node, shift, substitute
   1337         bound = Node(4, Node(2, 0))
   1338         free = Node(4, Node(2, 1))
   1339         opaque = Node(5, free.tree())
   1340         self.assertEqual(shift(bound, 7), bound)
   1341         self.assertEqual(shift(free, 7), Node(4, Node(2, 8)))
   1342         self.assertEqual(shift(opaque, 7), opaque)
   1343         self.assertEqual(substitute(free, [Node(2, 3)], 5), Node(4, Node(2, 4)))
   1344         self.assertEqual(shift(free, 2), shift(shift(free)))
   1345 
   1346 
   1347 class CandidateResourceTests(unittest.TestCase):
   1348     def test_memory_selection_reaches_heap_and_rss_guards(self):
   1349         from collections_batch import ProbeRunner
   1350         from unittest.mock import patch
   1351         with tempfile.TemporaryDirectory() as directory:
   1352             root = Path(directory)
   1353             for selected in [8192, 65536]:
   1354                 runner = ProbeRunner(root/'tricu', root, 60, memory_mib=selected)
   1355                 with patch('source_order.run_guarded', return_value=('', 123)) as run:
   1356                     runner.run('probe', ['run'], 30)
   1357                     command = run.call_args.args[0]
   1358                     self.assertIn(f'-M{selected}m', command)
   1359                     self.assertEqual(run.call_args.kwargs['memory_mib'], selected)
   1360                     self.assertLessEqual(run.call_args.kwargs['seconds'], 30)
   1361             self.assertEqual(ProbeRunner(root/'tricu', root, 60).memory_mib, 8192)
   1362 
   1363     def test_memory_selection_rejects_out_of_policy_limits(self):
   1364         from source_order import Runner
   1365         for value in [-1, 0, 65537]:
   1366             with self.assertRaises(ValueError):
   1367                 Runner(Path('tricu'), Path('.'), memory_mib=value)
   1368 
   1369 
   1370 class GrammarOracleTests(unittest.TestCase):
   1371     def test_authored_replies_and_lookahead_vectors_are_whole_trees(self):
   1372         from source_grammar_models import authored, lookahead_rows
   1373         from source_order import end
   1374         cases = authored()
   1375         self.assertEqual(len(cases), len({row[0] for row in cases}))
   1376         for label, _, argument, expected in cases:
   1377             self.assertEqual(end(argument), len(argument), label)
   1378             self.assertEqual(end(expected), len(expected), label)
   1379         for row in lookahead_rows():
   1380             for tree in row:
   1381                 self.assertEqual(end(tree), len(tree))
   1382 
   1383     def test_lookahead_boundaries_do_not_consume_or_guess_assignments(self):
   1384         from source_grammar_models import lookahead
   1385         self.assertTrue(lookahead([2, 2, 5], [1, 3, 5])[0][0])
   1386         self.assertFalse(lookahead([2, 6, 5], [1, 2, 3])[0][0])
   1387         self.assertFalse(lookahead([2, 6, 11], [1, 2, 3])[0][1])
   1388         self.assertTrue(lookahead([2, 3, 6, 4, 11], [1]*5)[0][1])
   1389         self.assertFalse(lookahead([2, 12, 11], [1]*3)[0][1])
   1390         self.assertEqual(lookahead([6, 2, 11], [1, 3, 5])[1],
   1391                          [True, True, True, False, True, True])
   1392 
   1393 
   1394 class ResolutionOracleTests(unittest.TestCase):
   1395     def test_models_emit_complete_trees(self):
   1396         from source_resolution_models import (ast_cases, scope_cases, pattern_cases,
   1397             resolve_result, free, pattern_result, graph_cases, graph_model)
   1398         from source_order import end
   1399         for node, environment in ast_cases():
   1400             for tree in [node.tree(), resolve_result(node, environment)]:
   1401                 self.assertEqual(end(tree), len(tree))
   1402         for node in scope_cases():
   1403             for name in free(node):
   1404                 self.assertEqual(name.kind, 2)
   1405                 self.assertEqual(end(name.tree()), len(name.tree()))
   1406         for node, binders, environment in pattern_cases():
   1407             tree = pattern_result(node, binders, environment)
   1408             self.assertEqual(end(tree), len(tree))
   1409         for definitions, environment, final in graph_cases():
   1410             for tree in graph_model(definitions, environment, final):
   1411                 self.assertEqual(end(tree), len(tree))
   1412 
   1413     def test_scope_and_duplicate_priority(self):
   1414         from source_resolution_models import Ast, free, resolved, graph_model
   1415         from source_order import field, number
   1416         name = Ast(2, 'x', 9)
   1417         self.assertEqual(free(Ast(9, ('x', name, name))), [name])
   1418         self.assertEqual(free(Ast(15, ('x', name, name))), [])
   1419         bound = resolved(name, [], ('y', 'x', 'x'))
   1420         self.assertEqual(number(field(bound, 1, 0)), 1)
   1421         definitions = [(Ast(2, 'x', 1), Ast(2, 'missing', 2)),
   1422                        (Ast(2, 'x', 3), Ast(1))]
   1423         ordered, analyzed = graph_model(definitions, [], Ast(2, 'absent'))
   1424         self.assertEqual(number(field(ordered, 1, 0, 0)), 30)
   1425         self.assertEqual(number(field(analyzed, 1, 0, 0)), 20)
   1426 
   1427 
   1428 class LoweringOracleTests(unittest.TestCase):
   1429     def test_identity_and_constant(self):
   1430         from source_lowering_models import tc_apply, I, K, n, LEAF
   1431         for value in [LEAF, n(23), I]:
   1432             self.assertEqual(tc_apply(I, value), value)
   1433             self.assertEqual(tc_apply(tc_apply(K, value), n(99)), value)
   1434 
   1435     def test_complete_residual_models(self):
   1436         from source_lowering_models import specimens, bracket, eliminate, run_result
   1437         from source_order import end
   1438         terms, cores = specimens()
   1439         for tree in [bracket(t).tree() for t in terms] + [eliminate(c).tree() for c in cores] + [run_result(t) for t in terms]:
   1440             self.assertEqual(end(tree), len(tree))
   1441 
   1442 
   1443 class ModuleOracleTests(unittest.TestCase):
   1444     def test_roundtrip_and_whole_expectations(self):
   1445         from source_modules import tree_cases, decode, manifest_cases, header_cases
   1446         from source_order import end
   1447         from http_headers import result
   1448         for tree in tree_cases():
   1449             self.assertEqual(decode(list(tree), True), result(True, tree))
   1450         for text, expected in manifest_cases() + header_cases():
   1451             self.assertEqual(end(expected), len(expected))
   1452 
   1453 
   1454 class BuildOracleTests(unittest.TestCase):
   1455     def test_bundle_layout_and_missing_dependency(self):
   1456         from source_building import bundle_bytes, map_cases
   1457         from stable_bundle import sections
   1458         from source_order import field
   1459         from http_headers import LEAF
   1460         with tempfile.TemporaryDirectory() as directory:
   1461             path = Path(directory) / 'golden.arboricx'
   1462             path.write_bytes(bundle_bytes([('x', LEAF), ('y', LEAF)]))
   1463             self.assertEqual(len(sections(path)), 2)
   1464         text, outcome = next((text, outcome) for text, outcome in map_cases() if '\tmissing\n' in text)
   1465         self.assertEqual(field(outcome, 0), LEAF)
   1466 
   1467 
   1468 class BackendDefaultTests(unittest.TestCase):
   1469     def test_historical_provenance_is_not_reinterpreted(self):
   1470         from generation_backend import DEFAULT_BACKEND, recorded_backend
   1471         self.assertEqual(DEFAULT_BACKEND, 'stable')
   1472         self.assertEqual(recorded_backend({}), 'optimized')
   1473         for backend in ['stable', 'optimized']:
   1474             self.assertEqual(recorded_backend({'backend': backend}), backend)
   1475         with self.assertRaises(ValueError):
   1476             recorded_backend({'backend': 'unknown'})
   1477 
   1478     def test_real_runner_parsers_default_stable_and_keep_overrides(self):
   1479         # Stop at parse_args: exercise the actual CLI declarations without
   1480         # reading artifacts, spawning hosts or building source in the fast gate.
   1481         import argparse
   1482         import runpy
   1483         from unittest.mock import patch
   1484 
   1485         class Parsed(Exception):
   1486             pass
   1487 
   1488         parse = argparse.ArgumentParser.parse_args
   1489         scripts = {
   1490             'bootstrap_generations.py': ['previous', 'output'],
   1491             'bootstrap_generation_cli.py': ['previous'],
   1492             'native_library_migration.py': ['tricu', 'generation', 'output', '--library', 'socket'],
   1493             'portable_cli.py': ['tricu'],
   1494             'source_equations.py': ['tricu', '--generation', 'generation'],
   1495         }
   1496         for script, required in scripts.items():
   1497             for override in [None, 'stable', 'optimized']:
   1498                 with self.subTest(script=script, override=override):
   1499                     captured = []
   1500                     argv = required + (['--backend', override] if override else [])
   1501 
   1502                     def intercept(parser, *args, **kwargs):
   1503                         captured.append(parse(parser, argv))
   1504                         raise Parsed()
   1505 
   1506                     with patch.object(argparse.ArgumentParser, 'parse_args', intercept):
   1507                         with self.assertRaises(Parsed):
   1508                             runpy.run_path(str(Path(__file__).parent / script), run_name='__main__')
   1509                     self.assertEqual(captured[0].backend, override or 'stable')
   1510 
   1511 
   1512 class BootstrapKitTests(unittest.TestCase):
   1513     def setUp(self):
   1514         from bootstrap_inputs import SCHEMA, ROLES
   1515         self.temporary = tempfile.TemporaryDirectory()
   1516         self.addCleanup(self.temporary.cleanup)
   1517         self.directory = Path(self.temporary.name)
   1518         self.metadata = dict(schema=SCHEMA, backend='stable', artifacts={})
   1519         for role, filename in ROLES.items():
   1520             payload = (role + '-opaque-test-bytes').encode()
   1521             (self.directory / filename).write_bytes(payload)
   1522             self.metadata['artifacts'][role] = dict(file=filename,
   1523                 export='main' if role == 'adapter' else 'sourceMapBuildStable',
   1524                 sha256=hashlib.sha256(payload).hexdigest())
   1525         self.save()
   1526 
   1527     def save(self):
   1528         (self.directory / 'bootstrap-kit.json').write_text(json.dumps(self.metadata))
   1529 
   1530     def load(self, backend='stable'):
   1531         from bootstrap_inputs import load_kit
   1532         return load_kit(self.directory, backend)
   1533 
   1534     def test_captures_exact_manifest_and_artifacts(self):
   1535         raw, payloads, pin = self.load()
   1536         self.assertEqual(raw, (self.directory / 'bootstrap-kit.json').read_bytes())
   1537         self.assertEqual(pin, hashlib.sha256(raw).hexdigest())
   1538         (self.directory / 'rebuilt-compiler.arboricx').write_bytes(b'changed')
   1539         self.assertEqual(payloads['compiler'], b'compiler-opaque-test-bytes')
   1540         with self.assertRaisesRegex(ValueError, 'digest mismatch'): self.load()
   1541 
   1542     def test_backend_is_explicit(self):
   1543         with self.assertRaisesRegex(ValueError, 'backend mismatch'): self.load('optimized')
   1544         self.metadata.pop('backend'); self.save()
   1545         with self.assertRaisesRegex(ValueError, 'backend mismatch'): self.load()
   1546 
   1547     def test_rejects_invalid_manifest_schema(self):
   1548         for schema in ('old', None, 1):
   1549             with self.subTest(schema=schema):
   1550                 self.metadata['schema'] = schema; self.save()
   1551                 with self.assertRaisesRegex(ValueError, 'schema'): self.load()
   1552 
   1553     def test_requires_both_roles_and_no_extra_role(self):
   1554         adapter = self.metadata['artifacts'].pop('adapter'); self.save()
   1555         with self.assertRaisesRegex(ValueError, 'exactly'): self.load()
   1556         self.metadata['artifacts']['adapter'] = adapter
   1557         self.metadata['artifacts']['extra'] = adapter; self.save()
   1558         with self.assertRaisesRegex(ValueError, 'exactly'): self.load()
   1559 
   1560     def test_rejects_path_traversal_and_role_swap(self):
   1561         for filename in ('../elsewhere', '/tmp/elsewhere', 'io-adapter.arboricx'):
   1562             with self.subTest(filename=filename):
   1563                 self.metadata['artifacts']['compiler']['file'] = filename; self.save()
   1564                 with self.assertRaisesRegex(ValueError, 'filename'): self.load()
   1565 
   1566     def test_rejects_wrong_export(self):
   1567         self.metadata['artifacts']['compiler']['export'] = 'sourceMapBuild'; self.save()
   1568         with self.assertRaisesRegex(ValueError, 'export'): self.load()
   1569 
   1570     def test_rejects_invalid_digest(self):
   1571         for pin in ('a' * 63, 'A' * 64, '../x', None):
   1572             with self.subTest(pin=pin):
   1573                 self.metadata['artifacts']['adapter']['sha256'] = pin; self.save()
   1574                 with self.assertRaisesRegex(ValueError, 'digest'): self.load()
   1575 
   1576     def test_missing_or_corrupt_adapter_fails(self):
   1577         path = self.directory / 'io-adapter.arboricx'
   1578         path.write_bytes(b'corrupt')
   1579         with self.assertRaisesRegex(ValueError, 'digest mismatch'): self.load()
   1580         path.unlink()
   1581         with self.assertRaises(FileNotFoundError): self.load()
   1582 
   1583 
   1584 class IndexedObjectAuditTests(unittest.TestCase):
   1585     def test_independent_writer_and_complete_audit(self):
   1586         from native_indexed_store import indexed_bytes
   1587         from executable_object import verify_payload, INDEXED, FLAT
   1588         for tree in [b'\0', b'\1\0', b'\2\1\0\2\0\0']:
   1589             verify_payload(FLAT, tree)
   1590             data = indexed_bytes(tree)
   1591             verify_payload(INDEXED, data)
   1592             for at in [0, 8, 15, 31, 32, 95, 96, len(data) - 1]:
   1593                 changed = bytearray(data); changed[at] ^= 255
   1594                 with self.subTest(tree=tree, at=at):
   1595                     with self.assertRaises((AssertionError, ValueError)):
   1596                         verify_payload(INDEXED, bytes(changed))
   1597             for changed in [data[:-1], data + b'\0']:
   1598                 with self.assertRaises(AssertionError): verify_payload(INDEXED, changed)
   1599         with self.assertRaises(AssertionError): verify_payload('wrong.kind', b'\0')
   1600 
   1601 
   1602 class InspectorStagingTests(unittest.TestCase):
   1603     def test_staging_is_opt_in_immutable_and_authenticates_bytes(self):
   1604         from library_inspector import stage_site
   1605         with tempfile.TemporaryDirectory() as temporary:
   1606             root = Path(temporary)
   1607             (root / 'producer').mkdir()
   1608             site = root / 'site'; (site / 'bundles').mkdir(parents=True)
   1609             index = site / 'packages.json'
   1610             index.write_text(json.dumps(dict(schema=1, releases=[])))
   1611             payload = root / 'producer/tricu-base.arboricx'
   1612             payload.write_bytes(b'fixture bytes')
   1613             digest = hashlib.sha256(payload.read_bytes()).hexdigest()
   1614             report = dict(complete=True, releases={'tricu-base': dict(version='0.1.0', manifest='a'*64, bundle=digest)})
   1615             with self.assertRaises(ValueError): stage_site(root, site, dict(report, complete=False))
   1616             stage_site(root, site, report)
   1617             before = index.read_bytes(); stage_site(root, site, report)
   1618             self.assertEqual(index.read_bytes(), before)
   1619             self.assertEqual((site / 'bundles' / (digest + '.arboricx')).read_bytes(), payload.read_bytes())
   1620             payload.write_bytes(b'changed bytes')
   1621             with self.assertRaises(ValueError): stage_site(root, site, report)
   1622             changed = hashlib.sha256(payload.read_bytes()).hexdigest()
   1623             report['releases']['tricu-base']['bundle'] = changed
   1624             with self.assertRaises(ValueError): stage_site(root, site, report)
   1625             self.assertEqual(index.read_bytes(), before)
   1626             self.assertFalse((site / 'bundles' / (changed + '.arboricx')).exists())
   1627             report['releases']['tricu-base']['version'] = '0.2.0'
   1628             stage_site(root, site, report)
   1629             self.assertEqual(len(json.loads(index.read_text())['releases']), 2)
   1630             self.assertTrue((site / 'bundles' / (digest + '.arboricx')).exists())
   1631 
   1632 
   1633 if __name__ == '__main__':
   1634     unittest.main()