tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

io_v2_files.py (5529B)


      1 #!/usr/bin/env python3
      2 """Compile/run the experimental v2 copier through the retained v1 compiler.
      3 
      4 Nix-built host required. No grants are passed to the v2 application. Compares
      5 complete output files and result trees for direct and handler-backed compositions.
      6 """
      7 import argparse
      8 import hashlib
      9 import json
     10 import re
     11 from pathlib import Path
     12 
     13 from source_order import Runner, field
     14 from stable_bundle import byte_tree
     15 from http_headers import result, tree_list
     16 
     17 ROOT = Path(__file__).resolve().parent.parent
     18 
     19 
     20 def main():
     21     parser = argparse.ArgumentParser(description=__doc__)
     22     parser.add_argument('tricu', type=Path)
     23     parser.add_argument('--directory', type=Path, required=True)
     24     args = parser.parse_args()
     25     args.directory.mkdir(parents=True, exist_ok=False)
     26     directory = args.directory.resolve()
     27     runner = Runner(args.tricu, directory)
     28     bundle = directory/'copy.arboricx'
     29     runner.run('compile', ['compile', '--file', ROOT/'demos/io-v2/copy.tri',
     30                           '--store', runner.store, '--output', bundle])
     31     pin = runner.imported(bundle, 'import')
     32     checks = []
     33     for name in ('copy', 'handledCopy', 'borrowedCopy', 'transferredCopy', 'joinedOnExitCopy', 'handledChildCopy'):
     34         for size in (0, 1, 4095, 4096, 4097, 65536, 1048576):
     35             label = f'{name}-{size}'
     36             source, target = directory/(label+'.in'), directory/(label+'.out')
     37             body = (bytes(range(256))*((size+255)//256))[:size]
     38             source.write_bytes(body)
     39             argv = ['--io']
     40             for index, path in enumerate((source, target)):
     41                 argument = directory/f'{label}-{index}.tree'
     42                 argument.write_bytes(byte_tree(str(path).encode()))
     43                 argv += ['--arg-tree-file', argument]
     44             actual = runner.invoke(label, pin, name, argv)
     45             expected = result(True, b'\0')
     46             if name == 'handledCopy':
     47                 expected = result(True, expected)
     48             assert actual == expected, label
     49             assert target.read_bytes() == body, label
     50             checks.append({'name': label, 'sha256': hashlib.sha256(body).hexdigest()})
     51     for name in ('copy', 'handledCopy'):
     52         # Independent failure controls: no destination on a missing source, and
     53         # a failed writer acquisition after a successful reader acquisition.
     54         for case in ('missing-source', 'missing-parent'):
     55             source = directory/(name+'-failure.in')
     56             source.write_bytes(b'not copied')
     57             target = directory/(name+'-failure.out')
     58             if case == 'missing-source':
     59                 source = directory/'NO-SUCH-SOURCE'
     60             else:
     61                 target = directory/'NO-SUCH-DIRECTORY'/'out'
     62             label = name+'-'+case
     63             argv = ['--io']
     64             for index, path in enumerate((source, target)):
     65                 argument = directory/f'{label}-{index}.tree'
     66                 argument.write_bytes(byte_tree(str(path).encode()))
     67                 argv += ['--arg-tree-file', argument]
     68             actual = runner.invoke(label, pin, name, argv)
     69             assert field(actual, 0) == b'\0', label
     70             diagnostic = field(actual, 1, 0)
     71             assert field(diagnostic, 0) == byte_tree(b'not-found'), label
     72             assert field(diagnostic, 1, 0) == byte_tree(b'file/open-reader' if case == 'missing-source' else b'file/open-writer'), label
     73             assert not target.exists(), label
     74             checks.append({'name': label})
     75     for size in (0, 1, 4096, 4097, 65536):
     76         label = f'cancelledCopy-{size}'
     77         source, target = directory/(label+'.in'), directory/(label+'.out')
     78         body = (bytes(range(256))*((size+255)//256))[:size]
     79         source.write_bytes(body)
     80         argv = ['--io']
     81         for index, path in enumerate((source, target)):
     82             argument = directory/f'{label}-{index}.tree'
     83             argument.write_bytes(byte_tree(str(path).encode()))
     84             argv += ['--arg-tree-file', argument]
     85         actual = runner.invoke(label, pin, 'cancelledCopy', argv)
     86         assert actual == tree_list([byte_tree(b'cancelled'), byte_tree(b'requested'), b'\0']), label
     87         assert target.read_bytes() == body[:4096], label
     88         checks.append({'name': label, 'sha256': hashlib.sha256(target.read_bytes()).hexdigest()})
     89     # Regression guards for this selected copier, not general resource SLAs.
     90     observations = [row for row in runner.records if row['label'].endswith(('-65536', '-1048576'))]
     91     for row in observations:
     92         assert row['peak_kib'] is not None and row['peak_kib'] <= 128*1024, row
     93         match = re.search(r'([0-9,]+) bytes maximum residency', (directory/(row['label']+'.log')).read_text())
     94         assert match, row
     95         row['maximum_live_bytes'] = int(match[1].replace(',', ''))
     96         assert row['maximum_live_bytes'] <= 16*1024*1024, row
     97     report = {'checks': checks, 'sources': {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest()
     98         for p in [ROOT/'src/IOV2/Resources.hs', ROOT/'src/IOV2/Driver.hs', ROOT/'src/IOV2/Protocol.hs',
     99              ROOT/'test/source_order.py', ROOT/'test/rts_metrics.py', ROOT/'src/IOV2/Tasks.hs', ROOT/'lib/io-v2.tri',
    100                   ROOT/'demos/io-v2/copy.tri', ROOT/'lib/handlers.tri']}}
    101     report['observations'] = observations
    102     (directory/'report.json').write_text(json.dumps(report, indent=2)+'\n')
    103     print(f'PASS: {len(checks)} binary-copy and failure checks, including handler composition')
    104 
    105 
    106 if __name__ == '__main__':
    107     main()