io_callers.py (5022B)
1 #!/usr/bin/env python3 2 """Real migrated reader/state/file/bundle callers, with independent outputs.""" 3 import argparse,json,os,subprocess,struct 4 from pathlib import Path 5 from source_order import Runner 6 from http_headers import result,parse_tree_output 7 from io_text import text 8 ROOT=Path(__file__).resolve().parent.parent 9 10 def main(): 11 p=argparse.ArgumentParser(description=__doc__);p.add_argument('tricu',type=Path);p.add_argument('--directory',type=Path,required=True) 12 a=p.parse_args();d=a.directory.resolve();d.mkdir(parents=True,exist_ok=False);r=Runner(a.tricu,d);checks=[];pins={} 13 for name in ['safeRead','shout','writeThenRead','environment','state','bundle']: 14 source=ROOT/'demos/runArboricxBundle.tri' if name=='bundle' else ROOT/f'demos/interactionTrees/{name}.tri' 15 bundle=d/(name+'.arboricx');r.run(name+'-compile',['compile','--file',source,'--store',r.store,'--output',bundle,'--compile-timeout','600'],seconds=660);pins[name]=r.imported(bundle,name+'-import') 16 def call(label,module,name,args=(),env=None,good=True,output=False): 17 command=[str(r.exe),'arboricx','run','--module',pins[module],'--name',name,'--store',str(r.store),'--io','--format','ternary'] 18 if output:command+=['--no-result'] 19 for i,arg in enumerate(args): 20 f=d/f'{label}-{i}.tree';f.write_bytes(text(arg));command+=['--arg-tree-file',str(f)] 21 proc=subprocess.run(command,capture_output=True,timeout=60,env=env) 22 (d/(label+'.stdout')).write_bytes(proc.stdout);(d/(label+'.stderr')).write_bytes(proc.stderr) 23 assert (proc.returncode==0)==good,(label,proc.stderr) 24 checks.append(label) 25 return proc.stdout if output or not good else parse_tree_output(proc.stdout.decode()) 26 for i,value in enumerate(['','\ufeffλ\r\n\0😀','a'*4092+'😀']): 27 f=d/f'text-{i}.txt';f.write_bytes(value.encode()) 28 assert call(f'read-{i}','safeRead','run',[str(f)])==text(value) 29 assert call(f'shout-{i}','shout','run',[str(f)])==text(value+'!!!') 30 out=d/f'write-{i}.txt' 31 assert call(f'roundtrip-{i}','writeThenRead','run',[str(out),value])==result(True,text(value)) 32 assert out.read_bytes()==value.encode() 33 missing=d/'missing' 34 assert call('missing-read','safeRead','run',[str(missing)])==text('ERROR: Unable to read file') 35 assert call('missing-shout','shout','run',[str(missing)])==text('missing!!!') 36 invalid=d/'invalid';invalid.write_bytes(b'\xff') 37 assert call('invalid-text','safeRead','run',[str(invalid)])==text('ERROR: Unable to read file') 38 over=d/'overlong';over.write_bytes(b'a'*1048577) 39 assert call('read-budget','safeRead','run',[str(over)])==text('ERROR: Unable to read file') 40 failure=call('write-failed','writeThenRead','run',[str(missing/'output'),'never']) 41 assert failure[:2]==b'\2\0' and not missing.exists(),failure 42 env=dict(os.environ);env.pop('TRICU_DEMO_OUTPUT',None) 43 call('explicit-output-required','writeThenRead','main',env=env,good=False) 44 out=d/'configured.txt' 45 assert call('configured-output','writeThenRead','main',env=dict(env,TRICU_DEMO_OUTPUT=str(out)),output=True)==b'hello from tricu\n' 46 assert out.read_bytes()==b'hello from tricu' 47 assert call('lexical-configuration','environment','main',env=dict(env,TRICU_DEMO_OUTPUT='unrelated'),output=True)==b'working in env: demo\nlocal returned: inside-done\noutside env: outer\n' 48 assert call('explicit-state','state','main',output=True)==b'state: idle\nstate: running\nstate: done\n' 49 assert call('bundle-main','bundle','main',output=True)==b'hi\n' 50 # Main must check its final write even when --no-result suppresses values. 51 for name in ['shout','writeThenRead','environment','state','bundle']: 52 with open('/dev/full','wb') as full: 53 run=subprocess.run([str(r.exe),'arboricx','run','--module',pins[name],'--name','main','--store',str(r.store),'--io','--no-result'],stdout=full,stderr=subprocess.PIPE,env=dict(env,TRICU_DEMO_OUTPUT=str(out)),timeout=60) 54 assert run.returncode!=0,(name,run.stderr) 55 checks.append(name+'-failed-final-output') 56 original=(ROOT/'test/fixtures/id.arboricx').read_bytes() 57 padding=d/'padding.arboricx';padding.write_bytes(original+b'x') 58 assert call('bundle-permitted-padding','bundle','run',[str(padding),'hi'],output=True)==b'hi\n' 59 offset=struct.unpack_from('>Q',original,80)[0] 60 bad=bytearray(original[:offset]+b'x'+original[offset:]) 61 struct.pack_into('>Q',bad,56,struct.unpack_from('>Q',original,56)[0]+1) 62 struct.pack_into('>Q',bad,80,offset+1) 63 for label,data in [('truncated',original[:-1]),('trailing-manifest',bad),('bad-magic',b'X'+original[1:])]: 64 f=d/(label+'.arboricx');f.write_bytes(data) 65 assert call('bundle-'+label,'bundle','run',[str(f),'never'],good=False,output=True)==b'' 66 (d/'report.json').write_text(json.dumps(dict(passed=checks,records=r.records),indent=2)+'\n');print(f'PASS {len(checks)} migrated caller controls') 67 if __name__=='__main__':main()