tricu

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

io_process.py (4081B)


      1 #!/usr/bin/env python3
      2 """Independent actual-command, binary pipeline and normal-scope-exit controls."""
      3 import argparse
      4 import json
      5 import shutil
      6 from pathlib import Path
      7 from source_order import Runner
      8 from http_headers import number, tree_list, result
      9 from io_objects import text
     10 
     11 ROOT = Path(__file__).resolve().parent.parent
     12 
     13 def main():
     14     p=argparse.ArgumentParser(description=__doc__)
     15     p.add_argument('tricu',type=Path); p.add_argument('--directory',type=Path,required=True)
     16     a=p.parse_args(); directory=a.directory.resolve(); directory.mkdir(parents=True,exist_ok=False)
     17     runner=Runner(a.tricu,directory); bundle=directory/'process.arboricx'; checks=[]
     18     runner.run('compile',['compile','--file',ROOT/'demos/io/process.tri','--store',runner.store,'--output',bundle,'--compile-timeout','600'],seconds=660)
     19     pin=runner.imported(bundle,'import')
     20     inherit=tree_list([text('inherit')])
     21     def invoke(label,name,args):
     22         path=directory/(label+'.tree');path.write_bytes(tree_list(args))
     23         return runner.invoke(label,pin,name,['--io','--arg-tree-file',path])
     24     cat=shutil.which('cat');shell=shutil.which('sh');assert cat and shell
     25     source=directory/'input';output=directory/'output';errors=directory/'errors'
     26     def pipeline(label,exe,argv,cwd,env,wanted,err=b'',code=0):
     27         actual=invoke(label,'pipeline',[text(exe),tree_list([text(x) for x in argv]),cwd,env,number(20),text(str(source)),text(str(output)),text(str(errors))])
     28         assert actual==tree_list([text('exit'),number(code)]),(label,actual.hex())
     29         assert output.read_bytes()==wanted,label
     30         assert errors.read_bytes()==err,label
     31         checks.append(label)
     32     for size in [0,1,4095,4096,4097,65536,1048576]:
     33         data=bytes(i%256 for i in range(size));source.write_bytes(data)
     34         pipeline(f'binary-{size}',cat,[],inherit,inherit,data)
     35     tee=shutil.which('tee');assert tee
     36     data=bytes(range(256))*4096;source.write_bytes(data)
     37     pipeline('concurrent-stdout-stderr',tee,['/dev/stderr'],inherit,inherit,data,data)
     38     source.write_bytes(b'')
     39     pipeline('literal-argv-env-cwd',shell,['-c','printf "%s|%s|%s" "$1" "$VALUE" "$PWD"; printf diagnostic >&2; exit 7','test','$(not-a-command); *'],tree_list([text('at'),text(str(directory))]),tree_list([text('replace'),tree_list([tree_list([text('VALUE'),text('literal')])])]),('$(not-a-command); *|literal|'+str(directory)).encode(),b'diagnostic',7)
     40     marker=directory/'joined'
     41     actual=invoke('normal-exit','normalExit',[text(shell),tree_list([text('-c'),text('i=0; while [ "$i" -lt 10000 ]; do i=$((i+1)); done; printf finished > "$1"; exit 3'),text('test'),text(str(marker))])])
     42     assert actual==number(42) and marker.read_bytes()==b'finished'
     43     checks.append('normal-exit-reaps-without-implying-zero-status')
     44     for label,exe,args,cwd,env,grace in [
     45         ('empty-executable','',[],inherit,inherit,0),
     46         ('duplicate-environment',cat,[],inherit,tree_list([text('replace'),tree_list([tree_list([text('X'),text('a')]),tree_list([text('X'),text('b')])])]),0),
     47         ('excess-argv',cat,['a']*257,inherit,inherit,0),
     48         ('excess-text',cat,['a'*4097],inherit,inherit,0),
     49         ('excess-total',cat,['a'*4096]*16,inherit,inherit,0),
     50         ('excess-grace',cat,[],inherit,inherit,9223372036854775808)]:
     51         actual=invoke(label,'raw',[text(exe),tree_list([text(x) for x in args]),cwd,env,number(grace)])
     52         assert actual==result(False,tree_list([text('invalid-argument'),text('process/spawn'),b'\0',text('none')])),label
     53         checks.append(label)
     54     for row in runner.records:
     55         if row.get('label') in ('binary-1048576','concurrent-stdout-stderr'):
     56             # Runtime process observations, not subprocess heap or capacity promises.
     57             assert row['rts_process']['maximum_live_bytes'] < 16*1024*1024,row
     58             assert row['peak_kib'] < 128*1024,row
     59     (directory/'report.json').write_text(json.dumps(dict(passed=checks,records=runner.records),indent=2)+'\n')
     60     print(f'PASS {len(checks)} process source controls')
     61 if __name__=='__main__':main()