scp_native_validation.py (5569B)
1 #!/usr/bin/env python3 2 """Focused Haskell-only SCP artifacts, lessons and prepared deployments.""" 3 import argparse 4 from pathlib import Path 5 import re 6 import shutil 7 from research_session import ResearchSession, ROOT 8 9 p = argparse.ArgumentParser(description=__doc__) 10 p.add_argument('tricu', type=Path) 11 p.add_argument('--directory', type=Path, required=True) 12 p.add_argument('--projects-only', action='store_true') 13 p.add_argument('--include-self-size', action='store_true', help='explicit expensive logical-tree self-size observation') 14 args = p.parse_args() 15 # No compiler authoring/preparation job: only import the checked full artifacts. 16 s = ResearchSession.__new__(ResearchSession) 17 s.tricu, s.root = str(args.tricu.resolve()), args.directory.resolve() 18 s.root.mkdir(parents=True, exist_ok=True) 19 s.store, s.events = s.root / 'cas', [] 20 s.memory_mib = 8192 21 import time 22 s.deadline = time.monotonic() + 1180 23 bootstrap = s.root / 'bootstrap'; bootstrap.mkdir(exist_ok=True) 24 for source, target in [('compiler-library.arboricx','compiler-bootstrap.arboricx'), 25 ('support-library.arboricx','scp-bootstrap.arboricx')]: 26 shutil.copyfile(ROOT / 'test/fixtures' / source, bootstrap / target) 27 output, _ = s.run([s.tricu,'arboricx','import','--store',s.store, 28 '--file',bootstrap/'scp-bootstrap.arboricx','--indexed']) 29 pin = re.search(rb'Created module alias .* -> ([0-9a-f]{64})', output)[1].decode() 30 lessons = { 31 'equality': [b'same representation: same',b'two identities: different',b'result on Leaf: same',b'result on Fork: same'], 32 'size': [b'Leaf: 1',b'Stem Leaf: 2',b'Fork Leaf Leaf: 3',b'Compiled worker itself: '], 33 'patterns': [b'Leaf: Leaf',b'Stem Leaf: Stem Leaf',b'Stem Fork: Stem of Fork',b'Stem Stem: Stem of something else',b'Fork: Fork'], 34 'source': [b'Leaf: t',b'Stem Leaf: (t t)',b'Fork Leaf Leaf: (t t t)',b'Identity function: (t ',b'String a as a tree: (t '], 35 } 36 cases = [('fizzbuzz_lambda', False, [b'lambda-baseline-ok']), 37 ('fizzbuzz_structural', False, [b'consumer-spike-ok']), 38 ('layout_layout', False, [b'layout-consumer-ok']), 39 ('collect_prefixes_collect_prefixes', True, [b'matches=3',b'first=item',b'unmatched-first=none'])] 40 for name, expected in lessons.items(): 41 if name == 'size' and not args.include_self_size: 42 continue 43 cases.append(('lessons_'+name+'_'+name, True, expected)) 44 for name, io, expected in ([] if args.projects_only else cases): 45 output, _ = s.run([s.tricu,'arboricx','run','--store',s.store,'--module',pin, 46 '--name','demos_consumers_'+name+'.main','--format','decode', 47 *(['--io','--unsafe-io'] if io else [])], 120) 48 assert all(text in output for text in expected), (name,output) 49 print(name + ': stored execution passed', flush=True) 50 for name, expected in [('prefix-lines',b'matches=3\nfirst=disk\n'), 51 ('data-lines',b'data-lines=2\nfirst=alpha\n'), 52 ('fizzbuzz',b'ok: [1, 2, "Fizz"')]: 53 prepared = s.root / (name+'.prepared') 54 s.run([s.tricu,'scp','prepare',ROOT/f'demos/consumers/{name}/{name}.scp-project', 55 '--bootstrap',bootstrap,'--store',s.store,'--output',prepared,'--seconds','600'],1100) 56 if name == 'fizzbuzz': 57 transport = ['--input-tree-file',ROOT/'demos/consumers/fizzbuzz/fizzbuzz-input.tree'] 58 else: 59 transport = ['--input-file',ROOT / ('demos/consumers/data-lines/data-lines-input.txt' if name == 'data-lines' else 'demos/consumers/prefix-lines/tagged-log.fixture')] 60 # Deployment runs must not depend on bootstrap artifacts or authored source. 61 moved = s.root / 'bootstrap-hidden'; bootstrap.rename(moved) 62 try: 63 output, _ = s.run([s.tricu,'scp','run',prepared,*transport,'--seconds','60'],120) 64 finally: 65 moved.rename(bootstrap) 66 assert expected in output,(name,output) 67 print(name + ': native preparation and source-free deployment passed', flush=True) 68 print('Focused SCP gate passed; not the historical broad matrix') 69 70 # Failure controls: no legacy fallback, no accepting damaged executable graphs. 71 missing = s.root / 'missing-bootstrap'; missing.mkdir(exist_ok=True) 72 for selected, expected in [(missing,b'artifact bootstrap')]: 73 try: 74 s.run([s.tricu,'scp','build','--store',s.store,'--bootstrap',selected,'--seconds','10', 75 '--workspace','demos.consumer-prefix-workspace','--directory',ROOT,'--compiler','0'*64],30) 76 except AssertionError as error: 77 assert expected in error.args[0][2],error 78 else: 79 raise AssertionError('missing bootstrap unexpectedly accepted') 80 corrupt = bootstrap / 'scp-bootstrap.arboricx' 81 original = corrupt.read_bytes(); altered = bytearray(original) 82 # An arbitrary bit flip can create a *different valid program*, not malformed 83 # input. This API has no external expected file digest. Corrupt the first graph 84 # node tag deterministically; the container/header remain otherwise intact. 85 import struct 86 from stable_bundle import sections 87 manifest_bytes, _ = sections(corrupt) 88 altered[96 + len(manifest_bytes) + 8 + 4] = 255; corrupt.write_bytes(altered) 89 try: 90 try: 91 s.run([s.tricu,'scp','build','--worker','--store',s.store,'--bootstrap',bootstrap,'--seconds','10', 92 '--workspace','demos.consumer-prefix-workspace','--directory',ROOT,'--compiler','0'*64],30) 93 except AssertionError as error: 94 assert error.args[0][2],error 95 else: 96 raise AssertionError('corrupt bootstrap unexpectedly accepted') 97 finally: 98 corrupt.write_bytes(original) 99 print('Missing/corrupt bootstrap controls passed')