io_v2_zig_stages.py (3731B)
1 #!/usr/bin/env python3 2 """Cross-directory publication, EXDEV, invalid destinations and no rollback.""" 3 import argparse 4 import hashlib 5 import json 6 import subprocess 7 import tempfile 8 from pathlib import Path 9 from source_order import Runner 10 from http_headers import result,tree_list,number 11 from process_rss import run_guarded 12 ROOT=Path(__file__).resolve().parent.parent 13 14 def text(s):return tree_list([number(ord(c)) for c in str(s)]) 15 def main(): 16 p=argparse.ArgumentParser(description=__doc__);p.add_argument('tricu',type=Path);p.add_argument('probe',type=Path);p.add_argument('--directory',type=Path,required=True) 17 args=p.parse_args();d=args.directory.resolve();d.mkdir(parents=True,exist_ok=False);r=Runner(args.tricu,d);bundle=d/'stages.arboricx' 18 r.run('compile',['compile','--file',ROOT/'test/io-v2/zig-stages.tri','--store',r.store,'--output',bundle]);pin=r.imported(bundle,'import');checks=[] 19 a,b=d/'a',d/'b';a.mkdir();b.mkdir();target=b/'published' 20 def invoke(name,values,label,expected): 21 command=[str(args.probe.resolve()),str(bundle),name,*[str(v) if isinstance(v,int) else 'text:'+str(v) for v in values]] 22 output,peak=run_guarded(command,memory_mib=128,seconds=60);(d/(label+'.log')).write_text(output) 23 assert bytes(map(int,output.strip()))==expected,label 24 checks.append(dict(label=label,peak_kib=peak)) 25 for mode,state in [(0,'published'),(0,'exists'),(1,'published')]: 26 invoke('publish',[a,target,mode],f'publish-{mode}-{state}',result(True,text(state))) 27 assert target.read_bytes()==b'abc' and list(a.iterdir())==[] 28 target.unlink() # prove visibility was created before the terminal failure 29 process=subprocess.run([str(args.probe.resolve()),str(bundle),'failureAfterPublication','text:'+str(a),'text:'+str(target)],capture_output=True,timeout=60) 30 assert process.returncode==1 and b'failed (payload not forced)' in process.stderr 31 assert target.read_bytes()==b'abc' and list(a.iterdir())==[];checks.append(dict(label='no-rollback-after-failure')) 32 for name in ('.tricu-stage-protected','x.tricu-stage-protected'): 33 bad=b/name 34 expected=tree_list([result(False,tree_list([text('invalid-destination'),text('stage/publish'),text('destination must name a file outside the private staging namespace'),text('not-published')])),result(False,tree_list([text('invalid-state'),text('stage/publish'),text('stage is not in the required phase'),text('none')]))]) 35 invoke('invalidDestination',[a,bad],name,expected) 36 assert not bad.exists() and list(a.iterdir())==[] 37 # Do not manufacture EXDEV through a mock here: require a distinct device. 38 with tempfile.TemporaryDirectory(prefix='tricu-zig-exdev-',dir='/dev/shm') as foreign: 39 foreign=Path(foreign) 40 assert foreign.stat().st_dev != a.stat().st_dev,'EXDEV control needs distinct filesystems' 41 output=foreign/'out' 42 expected=tree_list([tree_list([text('io-error'),text('unknown')]),tree_list([text('invalid-state'),text('none')])]) 43 invoke('crossDevice',[a,output],'actual-exdev',expected) 44 assert not output.exists() and list(a.iterdir())==[] 45 flags=['--io'] 46 for i,v in enumerate((a,output)): 47 arg=d/f'exdev-{i}.tree';arg.write_bytes(text(v));flags+=['--arg-tree-file',arg] 48 assert r.invoke('exdev-haskell',pin,'crossDevice',flags)==expected 49 assert not output.exists() and list(a.iterdir())==[] 50 (d/'report.json').write_text(json.dumps(dict(checks=checks,haskell=r.records,probe_sha256=hashlib.sha256(args.probe.read_bytes()).hexdigest()),indent=2)+'\n') 51 print(f'PASS {len(checks)} private staged-publication controls including real EXDEV') 52 if __name__=='__main__':main()