io_v2_process.py (4087B)
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_v2_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-v2/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()