io_v2_zig_core.py (6203B)
1 #!/usr/bin/env python3 2 """Actual source core/handler parity using the PRIVATE Zig core probe, not native IO.""" 3 import argparse 4 import hashlib 5 import json 6 import subprocess 7 from pathlib import Path 8 from source_order import Runner 9 from io_v2_tasks import core_cases 10 from process_rss import run_guarded 11 from http_headers import number, result, tree_list 12 from stable_bundle import byte_tree 13 import time 14 15 ROOT = Path(__file__).resolve().parent.parent 16 17 def main(): 18 parser = argparse.ArgumentParser(description=__doc__) 19 parser.add_argument('tricu', type=Path) 20 parser.add_argument('probe', type=Path) 21 parser.add_argument('--directory', type=Path, required=True) 22 args = parser.parse_args() 23 d = args.directory.resolve(); d.mkdir(parents=True, exist_ok=False) 24 r = Runner(args.tricu, d) 25 probe = args.probe.resolve() 26 source = ROOT/'test/io-v2/tasks.tri' 27 bundle = d/'tasks.arboricx' 28 r.run('compile', ['compile', '--file', source, '--store', r.store, '--output', bundle]) 29 pin = r.imported(bundle, 'import') 30 checks = [] 31 for name, expected in core_cases().items(): 32 flags = ['--io']; argv = [] 33 if name == 'lazyLabels': 34 (d/'zero.tree').write_bytes(b'\0') 35 flags += ['--arg-tree-file', d/'zero.tree']; argv = ['0'] 36 assert r.invoke('haskell-'+name, pin, name, flags) == expected, ('haskell', name) 37 process = subprocess.run([str(probe), str(bundle), name, *argv], capture_output=True, timeout=60) 38 (d/(name+'.stdout')).write_bytes(process.stdout) 39 (d/(name+'.stderr')).write_bytes(process.stderr) 40 assert process.returncode == 0, (name, process.returncode, process.stderr) 41 assert process.stdout == ''.join(map(str, expected)).encode()+b'\n', name 42 checks.append(name) 43 controls_source = ROOT/'test/io-v2/core-controls.tri' 44 controls_bundle = d/'controls.arboricx' 45 r.run('compile-controls', ['compile', '--file', controls_source, '--store', r.store, '--output', controls_bundle]) 46 controls_pin = r.imported(controls_bundle, 'import-controls') 47 def text(s): return byte_tree(s.encode()) 48 def problem(category, operation, detail=''): 49 return result(False, tree_list([text(category), text(operation), text(detail), text('none')])) 50 controls = { 51 'cancelChild': (tree_list([text('cancelled'), text('stop'), b'\0']), False), 52 'currentScopes': (number(1), False), 53 'badFlags': (problem('invalid-argument', 'core/spawn'), True), 54 'badTransfer': (problem('invalid-argument', 'core/spawn'), True), 55 'discardedTicket': (problem('stale-task', 'core/join'), False), 56 'selfCycle': (number(42), False), 57 'pureDomainError': (result(False, text('ordinary')), False), 58 'unknownWithPoison': (problem('unsupported', 'future/call', 'group revision 3'), True), 59 } 60 for name, (expected, seeded) in controls.items(): 61 flags = ['--io'] + (['--arg-tree-file', d/'zero.tree'] if seeded else []) 62 assert r.invoke('haskell-'+name, controls_pin, name, flags) == expected, ('haskell', name) 63 p = subprocess.run([str(probe), str(controls_bundle), name] + (['0'] if seeded else []), capture_output=True, timeout=60) 64 (d/(name+'.stderr')).write_bytes(p.stderr) 65 assert p.returncode == 0 and p.stdout == ''.join(map(str, expected)).encode()+b'\n', (name, p.returncode, p.stderr, p.stdout) 66 checks.append(name) 67 for name, seeded in [('selfCycleWrong', False), ('rootFailure', True), ('rootCancelled', True), ('malformedBind', False)]: 68 flags = ['--io'] + (['--arg-tree-file', d/'zero.tree'] if seeded else []) 69 try: 70 r.invoke('haskell-'+name, controls_pin, name, flags) 71 except subprocess.CalledProcessError as error: 72 assert 'IO v2 task '+('cancelled' if name == 'rootCancelled' else 'failed') in error.output 73 else: 74 raise AssertionError(('Haskell negative succeeded', name)) 75 p = subprocess.run([str(probe), str(controls_bundle), name] + (['0'] if seeded else []), capture_output=True, timeout=60) 76 assert p.returncode == 1 and p.stdout == b'' and b'not forced' in p.stderr, (name, p.returncode, p.stderr) 77 checks.append(name+'-negative') 78 p = subprocess.run([str(probe), str(controls_bundle), 'legacyEnvelope', '0'], capture_output=True, timeout=10) 79 assert p.returncode != 0 and p.stdout == b'' and b'UnsupportedVersion' in p.stderr, p.stderr 80 checks.append('v1-not-retried') 81 retention_source = ROOT/'test/io-v2/core-retention.tri' 82 retention_bundle = d/'retention.arboricx' 83 r.run('compile-retention', ['compile', '--file', retention_source, '--store', r.store, '--output', retention_bundle]) 84 retention_pin = r.imported(retention_bundle, 'import-retention') 85 retention = [] 86 for name in ('joined', 'discarded'): 87 for count in (16, 128, 512): 88 label = f'{name}-{count}' 89 argument = d/(label+'.tree'); argument.write_bytes(number(count)) 90 assert r.invoke('haskell-retention-'+label, retention_pin, name, ['--io', '--arg-tree-file', argument]) == b'\0' 91 command = [str(probe), str(retention_bundle), name, str(count)] 92 start = time.monotonic() 93 output, peak = run_guarded(command, memory_mib=128, seconds=180) 94 (d/('zig-retention-'+label+'.log')).write_text(output) 95 assert output == '0\n', (label, output) 96 retention.append(dict(name=name, count=count, peak_kib=peak, seconds=time.monotonic()-start, command=command)) 97 paths = [controls_source, retention_source, source, probe, ROOT/'ext/zig/src/io_v2_core.zig', ROOT/'ext/zig/src/io_v2_tasks.zig', ROOT/'ext/zig/src/io_v2_resources.zig', ROOT/'ext/zig/src/io_v2_protocol.zig', ROOT/'test/io_v2_tasks.py'] 98 (d/'report.json').write_text(json.dumps({'checks': checks, 'scope': 'private-core-only-no-OS-facilities', 'retention': retention, 'sources': {str(p): hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}, 'haskell_records': r.records}, indent=2)+'\n') 99 print(f'PASS {len(checks)} actual source core/handler controls on Haskell and private Zig core') 100 101 if __name__ == '__main__': main()