tricu

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

io_protocol.py (6801B)


      1 #!/usr/bin/env python3
      2 """Independent source oracles for v2 discovery, inspection and unsupported calls."""
      3 import argparse
      4 import hashlib
      5 import json
      6 from pathlib import Path
      7 from source_order import Runner
      8 from stable_bundle import byte_tree
      9 from http_headers import tree_list, number, result
     10 
     11 ROOT = Path(__file__).resolve().parent.parent
     12 
     13 def text(value):
     14     return byte_tree(value.encode())
     15 
     16 def strings(values):
     17     return tree_list([text(v) for v in values])
     18 
     19 def expected_capabilities():
     20     def row(key, value):
     21         return tree_list([text(key), value])
     22     groups = [('runtime', ['capabilities', 'inspect']), ('standard', ['input', 'output', 'error']), ('file', ['open-reader', 'open-writer', 'open-appender', 'open-update', 'seek', 'position', 'size', 'resize', 'sync']),
     23               ('path', ['stat', 'lstat', 'remove-file', 'rename']), ('directory', ['create', 'remove', 'open', 'next', 'sync']), ('tcp', ['listen', 'connect', 'accept', 'local-address', 'peer-address', 'shutdown-write']), ('resolver', ['resolve']), ('indexed-tree', ['read', 'hash', 'write', 'verify']), ('utf8', ['encode', 'decode']), ('stream', ['read', 'write']), ('resource', ['close']),
     24               ('stage', ['open', 'seal', 'publish']), ('digest', ['open', 'update', 'finish']), ('clock', ['monotonic', 'sleep']), ('scheduler', ['yield']), ('process', ['spawn', 'working-directory', 'environment-variable'])]
     25     return tree_list([
     26         row('schema', number(1)), row('io-version', number(2)),
     27         row('backend', text('haskell-native')), row('authority', text('os-process')),
     28         row('core', strings(['pure', 'bind', 'fail', 'scope', 'current-scope', 'spawn', 'join', 'discard', 'cancel', 'cancelled'])),
     29         row('groups', tree_list([tree_list([text(g), number(1), strings(ops)]) for g, ops in groups])),
     30         row('limits', tree_list([row('chunk-bytes', number(4096)), row('transfer-handles', number(256)), row('file-offset-max', number(9223372036854775807)), row('directory-name-scalars', number(4096)), row('tcp-backlog', number(4096)), row('resolver-host-characters', number(253)), row('resolver-addresses', number(64)), row('indexed-sections', number(64)), row('indexed-manifest-bytes', number(65536)), row('utf8-encode-scalars', number(1024)), row('sleep-milliseconds-max', number(9223372036854775807)), row('process-arguments', number(256)), row('process-environment-entries', number(256)), row('process-text-scalars', number(4096)), row('process-total-scalars', number(65536)), row('process-grace-milliseconds-max', number(9223372036854775807))])),
     31         row('tcp-address-families', strings(['ipv4', 'ipv6'])),
     32         row('digest-algorithms', strings(['sha256'])),
     33         row('restrictions', strings(['action-boundary-cancellation', 'no-hard-deadlines',
     34             'stage-same-filesystem', 'stage-no-copy-fallback', 'stage-private-namespace', 'publish-new-requires-hard-links', 'no-crash-recovery', 'borrowed-standard-streams', 'standard-stream-leases-per-run', 'file-single-cursor-lease', 'append-no-position-or-resize', 'file-host-handle-locking', 'directory-unsorted-non-snapshot', 'filesystem-host-encoding', 'directory-failure-poisons-cursor', 'tcp-numeric-addresses', 'tcp-ipv6-only-listeners', 'tcp-direction-leases', 'tcp-failure-poisons-direction', 'resolver-host-order-and-buffering', 'resolver-may-delay-cancellation', 'indexed-authenticated-snapshot', 'indexed-caller-frame-and-disk-limit', 'indexed-dag-resident', 'indexed-writer-preflight', 'indexed-budget-not-heap-quota', 'sync-host-filesystem-guarantees', 'utf8-strict-scalars', 'clock-host-monotonic-word64', 'sleep-may-run-late', 'yield-no-ordering-guarantee', 'process-direct-child-only', 'process-normal-exit-joins', 'process-explicit-pipe-draining']))])
     35 
     36 def main():
     37     parser = argparse.ArgumentParser(description=__doc__)
     38     parser.add_argument('tricu', type=Path)
     39     parser.add_argument('--directory', type=Path, required=True)
     40     args = parser.parse_args()
     41     directory = args.directory.resolve()
     42     directory.mkdir(parents=True, exist_ok=False)
     43     runner = Runner(args.tricu, directory)
     44     bundle = directory/'protocol.arboricx'
     45     source = ROOT/'test/io/protocol.tri'
     46     runner.run('compile', ['compile', '--file', source, '--store', runner.store, '--output', bundle])
     47     pin = runner.imported(bundle, 'import')
     48     victim = directory/'must-remain'
     49     victim.write_bytes(b'not truncated\x00\xff')
     50     path = directory/'path.tree'
     51     path.write_bytes(byte_tree(str(victim).encode()))
     52     seed = directory/'seed.tree'
     53     seed.write_bytes(number(7))
     54     cases = {
     55         'capabilities': (result(True, expected_capabilities()), []),
     56         'handledCapabilities': (result(True, expected_capabilities()), []),
     57         'inspectWriter': (result(True, tree_list([text('facility'), text('file'), number(1),
     58                            text('open-writer'), text('available')])), [path]),
     59         'inspectLazyBind': (result(True, strings(['core', 'bind'])), [seed]),
     60         'inspectLazyScope': (result(True, strings(['core', 'scope'])), [seed]),
     61         'inspectFuture': (result(True, tree_list([text('facility'), text('future-tcp'), number(1),
     62                            text('connect'), text('unsupported')])), [seed]),
     63         'unsupported': (result(False, strings(['unsupported', 'future-tcp/connect', 'group revision 1', 'none'])), [seed])}
     64     for name, (expected, inputs) in cases.items():
     65         argv = ['--io']
     66         for arg in inputs:
     67             argv += ['--arg-tree-file', arg]
     68         assert runner.invoke(name, pin, name, argv) == expected, name
     69         assert victim.read_bytes() == b'not truncated\x00\xff'
     70     for name in ['invalidHeader', 'retired']:
     71         try:
     72             runner.invoke(name, pin, name, ['--io'])
     73         except Exception as error:
     74             assert 'IO task failed' in str(getattr(error, 'output', error)), error
     75         else:
     76             raise AssertionError('malformed protocol executed: ' + name)
     77     observations = runner.records
     78     for row in observations:
     79         stats = row['rts_process']
     80         assert all(k in stats for k in ['copied_bytes', 'mutator_cpu_seconds', 'gc_cpu_seconds',
     81                                         'productivity_cpu_percent']), row
     82     paths = [source, ROOT/'lib/io.tri', ROOT/'src/IO/Protocol.hs', ROOT/'src/IO/Driver.hs',
     83              ROOT/'test/source_order.py', ROOT/'test/rts_metrics.py', Path(__file__).resolve()]
     84     report = {'checks': list(cases)+['invalidHeader', 'retired'], 'observations': observations,
     85               'sources': {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() for p in paths}}
     86     (directory/'report.json').write_text(json.dumps(report, indent=2)+'\n')
     87     print('PASS: 9 independent capability/inspection/protocol checks')
     88 
     89 if __name__ == '__main__':
     90     main()