io_tasks.py (6764B)
1 #!/usr/bin/env python3 2 """Independent source-level task/handler oracles, using a Nix-built native host.""" 3 import argparse 4 import hashlib 5 import json 6 import re 7 from pathlib import Path 8 from source_order import Runner 9 from stable_bundle import byte_tree 10 from http_headers import tree_list, number, result 11 12 ROOT = Path(__file__).resolve().parent.parent 13 14 def text(s): 15 return byte_tree(s.encode()) 16 def completed(value): 17 return tree_list([text('completed'), value]) 18 def failed(message): 19 return tree_list([text('failed'), text(message), b'\0']) 20 21 def core_cases(): 22 return { 23 'joined': completed(number(42)), 24 'supervised': failed('fault'), 25 'cancelled': tree_list([text('cancelled'), text('shutdown'), b'\0']), 26 'ordinary': completed(result(False, text('domain'))), 27 'consumed': tree_list([result(True, completed(number(42))), 28 result(False, tree_list([text('stale-task'), text('core/join'), b'\0', text('none')]))]), 29 'inherited': tree_list([completed(number(7)), number(7)]), 30 'changed': tree_list([completed(number(9)), number(7)]), 31 'callback': tree_list([completed(number(7)), number(9)]), 32 'lazyLabels': completed(number(7)), 33 'labelledFailure': tree_list([text('failed'), tree_list([text('problem'), text('service'), 34 text('op'), text('detail'), tree_list([text('outer'), text('inner')])]), b'\0']), 35 'labelledStateBoundary': tree_list([text('failed'), tree_list([text('state-boundary'), 36 text('log'), text('write'), text('Install child-local collection explicitly'), 37 tree_list([text('outer'), text('inner')])]), b'\0']), 38 'stateBoundary': tree_list([text('failed'), tree_list([text('state-boundary'), 39 text('log'), text('write'), text('Install child-local collection explicitly'), b'\0']), b'\0']), 40 } 41 42 def main(): 43 parser = argparse.ArgumentParser(description=__doc__) 44 parser.add_argument('tricu', type=Path) 45 parser.add_argument('--directory', type=Path, required=True) 46 args = parser.parse_args() 47 args.directory.mkdir(parents=True, exist_ok=False) 48 runner = Runner(args.tricu, args.directory.resolve()) 49 source = ROOT/'test/io/tasks.tri' 50 bundle = args.directory.resolve()/'tasks.arboricx' 51 runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle]) 52 pin = runner.imported(bundle, 'import') 53 cases = core_cases() 54 for name, expected in cases.items(): 55 argv = ['--io'] 56 if name == 'lazyLabels': 57 arg = args.directory.resolve()/'unit.tree' 58 arg.write_bytes(b'\0') 59 argv += ['--arg-tree-file', arg] 60 actual = runner.invoke(name, pin, name, argv) 61 assert actual == expected, (name, actual.hex(), expected.hex()) 62 partial_checks = [] 63 for name in ('partialWrites', 'failedPartialWrite'): 64 for body in (b'', b'\xff', bytes([0, 255, 1, 128, 2, 254, 3])): 65 argument = args.directory.resolve()/f'{name}-{len(body)}.tree' 66 argument.write_bytes(byte_tree(body)) 67 label = f'{name}-{len(body)}' 68 actual = runner.invoke(label, pin, name, ['--io', '--arg-tree-file', argument]) 69 fails = name == 'failedPartialWrite' and len(body) > 2 70 expected = tree_list([result(False, tree_list([text('write-all'), number(2), text('write fault')])) if fails else result(True, b'\0'), 71 byte_tree(body[:2] if fails else body)]) 72 assert actual == expected, label 73 partial_checks.append(label) 74 body=bytes(range(256))*16 75 for i,counts in enumerate([[4096],[4095,1],[1,4095],[2048,2048], 76 [1,2048,2047],[],[4095],[2048],[1]]): 77 supplied=args.directory.resolve()/f'acks-{i}.tree' 78 supplied.write_bytes(tree_list([number(n) for n in counts])) 79 argument=args.directory.resolve()/f'ack-body-{i}.tree' 80 argument.write_bytes(byte_tree(body)) 81 offset=0; requests=[] 82 for count in counts: 83 assert 0 < count <= len(body)-offset 84 requests.append(byte_tree(body[offset:])) 85 offset+=count 86 if offset < len(body): 87 requests.append(byte_tree(body[offset:])) 88 answer=result(False,tree_list([text('write-all'),number(offset),text('write fault')])) 89 else: 90 answer=result(True,b'\0') 91 expected=tree_list([answer,tree_list(requests)]) 92 label=f'acknowledged-writes-{i}' 93 actual=runner.invoke(label,pin,'acknowledgedWrites', 94 ['--io','--arg-tree-file',supplied,'--arg-tree-file',argument]) 95 assert actual==expected,label 96 partial_checks.append(label) 97 retention = [] 98 for name in ('churnJoined', 'churnDiscarded'): 99 for count in (16, 256, 1024): 100 payload = args.directory.resolve()/f'{name}-{count}.bin' 101 payload.write_bytes(bytes(range(256))*(count*16)) 102 count_arg = args.directory.resolve()/f'count-{count}.tree' 103 count_arg.write_bytes(number(count)) 104 path_arg = args.directory.resolve()/f'{name}-{count}.path' 105 path_arg.write_bytes(byte_tree(str(payload).encode())) 106 label = f'{name}-{count}' 107 actual = runner.invoke(label, pin, name, ['--io', '--arg-tree-file', count_arg, 108 '--arg-tree-file', path_arg]) 109 assert actual == b'\0', label 110 row = dict(runner.records[-1]) 111 log = (args.directory/(label+'.log')).read_text() 112 match = re.search(r'([0-9,]+) bytes maximum residency', log) 113 assert match, label 114 row['maximum_live_bytes'] = int(match[1].replace(',', '')) 115 assert row['maximum_live_bytes'] <= 16*1024*1024, row 116 assert row['peak_kib'] <= 128*1024, row 117 retention.append(row) 118 paths = [source, ROOT/'lib/handlers.tri', ROOT/'lib/io.tri', 119 ROOT/'src/IO/Tasks.hs', ROOT/'src/IO/Driver.hs', ROOT/'src/IO/Protocol.hs', 120 ROOT/'test/source_order.py', ROOT/'test/rts_metrics.py', ROOT/'src/IO/Resources.hs', 121 ROOT/'lib/http.tri', ROOT/'lib/math/native.tri', ROOT/'lib/base.tri', 122 ROOT/'bootstrap/build-stable.arboricx'] 123 (args.directory/'report.json').write_text(json.dumps({'checks': list(cases)+partial_checks, 124 'sources': {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}, 125 'retention': retention, 'observations': runner.records}, indent=2)+'\n') 126 print(f'PASS: {len(cases)+len(partial_checks)} task/handler source oracles and {len(retention)} fresh-chunk retention workloads') 127 128 if __name__ == '__main__': 129 main()