bootstrap_smoke.py (5907B)
1 #!/usr/bin/env python3 2 """Small post-acceptance generation-two smoke; no fixture publication or matrix.""" 3 import argparse 4 import hashlib 5 import json 6 from pathlib import Path 7 import re 8 import time 9 10 from base_workloads import encode as n 11 from collections_batch import ProbeRunner, audit_workspace 12 from http_headers import pair, result, expect 13 from native_library_migration import digest 14 from generation_io import versions, compiler_flags 15 16 ROOT = Path(__file__).resolve().parent.parent 17 LIBRARY = '''!import prelude as B 18 !export copy 19 !export reverseLocal 20 copy [] = [] 21 copy [head | tail] = B.pair head (copy tail) 22 copy _ = [] 23 reverseLocal input = (let rec 24 walk [] reversed = reversed 25 walk [head | tail] reversed = walk tail (B.pair head reversed) 26 walk _ reversed = reversed 27 in walk input []) 28 ''' 29 PROGRAM = '''!import prelude as B 30 !import recursive as R 31 !import shapes as S 32 !export imports 33 !export copy 34 !export reverseLocal 35 !export invoice 36 imports input = [(S.first (B.pair 23 24)) (S.shape input)] 37 copy = R.copy 38 reverseLocal = R.reverseLocal 39 lineTotal (quantity, cents) = B.mul quantity cents 40 lineTotal _ = 0 41 invoice rows = (let total = B.foldl (sum row : B.add sum (lineTotal row)) 0 rows in 42 B.ok (B.pair total (B.showNumber total)) t) 43 ''' 44 45 46 def sha(path): 47 return hashlib.sha256(path.read_bytes()).hexdigest() 48 49 50 def smoke_cases(): 51 cases = [('imports', b'\0', n([23,0])), ('imports', b'\1\0', n([23,1])), 52 ('imports', b'\2\0\0', n([23,2]))] 53 for values in [[],[7],list(range(32))]: 54 cases += [('copy',n(values),n(values)),('reverseLocal',n(values),n(values[::-1]))] 55 for entries in [[],[(3,125),(2,49),(0,999),(1,1000)],[(9,9999),(11,12345)]]: 56 argument = b'\0' 57 for quantity,price in reversed(entries): 58 argument = pair(pair(n(quantity),n(price)),argument) 59 total = sum(q*p for q,p in entries) 60 cases.append(('invoice',argument,result(True,pair(n(total),n(list(str(total).encode())))))) 61 return cases 62 63 64 def main(): 65 p = argparse.ArgumentParser(description=__doc__) 66 p.add_argument('tricu', type=Path) 67 p.add_argument('--generation', type=Path, required=True) 68 p.add_argument('--acceptance', type=Path, required=True) 69 p.add_argument('--source-root', type=Path, required=True) 70 p.add_argument('--directory', type=Path, required=True) 71 args = p.parse_args() 72 accepted = json.loads(args.acceptance.read_text()) 73 generation = args.generation.resolve() 74 assert accepted['accepted'] and accepted['backend'] == 'stable' 75 assert generation == Path(accepted['generations'][1]).resolve() 76 artifact = generation/'build-stable.arboricx' 77 assert sha(artifact) == accepted['artifacts'][artifact.name] 78 assert sha(args.tricu) == accepted['inputs'][1]['runtime_sha256'] 79 root = args.directory.resolve(); root.mkdir(parents=True) 80 workspace = root/'workspace'; workspace.mkdir() 81 # Isolate staged siblings from an enclosing checkout's source map. 82 (workspace/'tricu.workspace').write_text('tricu.workspace.v1\n') 83 sources = {name:'lib/'+name+'.tri' for name in ['base','bytes','conversions','prelude']} 84 sources['native_math'] = 'lib/math/native.tri' 85 for alias, path in sources.items(): 86 source = args.source_root/path 87 assert sha(source) == accepted['inputs'][1]['sources'][alias] 88 (workspace/(alias+'.tri')).write_bytes(source.read_bytes()) 89 (workspace/'shapes.tri').write_bytes((args.source_root/'demos/source-patterns/patterns.tri').read_bytes()) 90 (workspace/'recursive.tri').write_text(LIBRARY) 91 (workspace/'program.tri').write_text(PROGRAM) 92 (root/'harness.py').write_bytes(Path(__file__).read_bytes()) 93 report = dict(complete=False, acceptance_sha256=sha(args.acceptance), 94 compiler_sha256=sha(artifact), runtime_sha256=sha(args.tricu), 95 harness_sha256=sha(Path(__file__)), cases=[]) 96 runner = ProbeRunner(args.tricu, root, 300) 97 try: 98 tool = runner.imported(artifact, 'generation-two') 99 bundle = root/'program.arboricx' 100 runner.run('compile', ['compile','--file',workspace/'program.tri', 101 '--toolchain',tool,*compiler_flags(versions(accepted['inputs'][1])[1]),'--tool-store',runner.store,'--store',runner.store, 102 '--output',bundle,'--compile-timeout','180'], 210) 103 raw = (runner.store/'objects'/tool[:3]/tool).read_bytes() 104 assert digest('arboricx.module-manifest.v1', raw) == tool 105 compiler = next(row.split('\t')[3] for row in raw.decode().splitlines()[1:] 106 if row.split('\t')[1] == 'sourceBuildGraph') 107 report['modules'] = audit_workspace(workspace, runner.store, compiler) 108 imported = runner.run('native-import', ['import','--file',bundle,'--store',runner.store]) 109 pin = re.search(r'Created module alias .* -> ([0-9a-f]{64})', imported)[1] 110 assert pin == report['modules']['program']['manifest'] 111 cases = smoke_cases() 112 for i,(export,argument,expected) in enumerate(cases): 113 path = root/'argument.tree'; path.write_bytes(argument) 114 actual = runner.invoke(str(i),pin,export,['--arg-tree-file',path]) 115 expect(actual,expected,export) 116 report['cases'].append(dict(export=export,input_sha256=hashlib.sha256(argument).hexdigest(), 117 output_sha256=hashlib.sha256(actual).hexdigest())) 118 assert actual != result(True, n(0)), 'wrong-output control' 119 report.update(complete=True, seconds=time.monotonic()-runner.started, 120 bundle_sha256=sha(bundle), wrong_output_rejected=True) 121 print(f'PASS: {len(cases)} complete results; native imports, top-level/local recursion, invoice totals/decimal text; {report["seconds"]:.2f}s') 122 finally: 123 (root/'report.json').write_text(json.dumps(report,indent=2)+'\n') 124 125 126 if __name__ == '__main__': 127 main()