tricu

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

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()