tricu

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

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