tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

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()