compiler.test.mjs (4361B)
1 import test from 'node:test'; 2 import assert from 'node:assert/strict'; 3 import {readFile} from 'node:fs/promises'; 4 import {createHash} from 'node:crypto'; 5 import {Graph} from '../dist/graph.js'; 6 import {natural,text,list,readText} from '../dist/codecs.js'; 7 import {compiledDefinitions,sourceLiteral,sourceText,SOURCE_LIMIT} from '../dist/compiler.js'; 8 test('compiler shipped pin matches authenticated reference and Haskell golden provenance',async()=>{ 9 const descriptor=JSON.parse(await readFile(new URL('../compiler.json',import.meta.url))); 10 const goldens=JSON.parse(await readFile(new URL('./compiler-conformance.json',import.meta.url))); 11 const shipped=await readFile(new URL('../bundles/'+descriptor.hash+'.arboricx',import.meta.url)); 12 assert.equal(createHash('sha256').update(shipped).digest('hex'),descriptor.hash); 13 assert.equal(goldens.compilerBundleSha256,descriptor.hash); 14 assert.match(descriptor.referenceCommit,/^[0-9a-f]{40}$/); 15 // The site's accepted compiler is intentionally pinned independently of HEAD. 16 // Authenticate it against its retained publication metadata below, not the 17 // current compiler-library fixture. Never ask assert to diff megabyte buffers. 18 const metadata=(await readFile(new URL('../compiler-reference.v1',import.meta.url),'utf8')).trim().split('\n'); 19 assert.equal(metadata[0],'tricu.compiler-library-fixture.v1'); 20 const fixtureDigest=createHash('sha256').update('tricu.compiler-library-fixture.v1\0').update(shipped).digest('hex'); 21 assert.deepEqual(metadata.filter(row=>row.startsWith('bundle\t')),['bundle\t'+fixtureDigest]); 22 const sources=metadata.filter(row=>row.startsWith('module\t')); 23 assert.equal(sources.length,28); 24 assert.ok(sources.every(row=>row.split('\t')[2]!=='patterns')); 25 for(const row of sources){ 26 const pin=row.split('\t')[4];assert.match(pin,/^[0-9a-f]{64}$/); 27 const source=await readFile(new URL('../../../test/fixtures/reference-source/'+pin+'.tri',import.meta.url)); 28 assert.equal(createHash('sha256').update('arboricx.source.v1\0').update(source).digest('hex'),pin); 29 } 30 assert.equal(descriptor.export,'source_unit.compileStableModuleDefinitions'); 31 }); 32 test('bounded compiler result decoder preserves lazy program handles',()=>{ 33 const g=new Graph({fuel:5000}),q=g.fromTernary('212110021100'),loop=g.app(q,q); 34 const binding=g.fork(text(g,'loop'),loop); 35 const success=defs=>g.fork(g.stem(g.leaf),g.fork(defs,g.leaf)); 36 const exports=compiledDefinitions(g,success(list(g,[binding]))); 37 assert.equal(exports.get('loop'),loop); // No forcing/expansion of code to name exports. 38 assert.throws(()=>compiledDefinitions(g,success(list(g,[binding,binding]))),/duplicate/); 39 assert.throws(()=>compiledDefinitions(g,success(list(g,Array(17).fill(binding)))),/limit/); 40 assert.throws(()=>compiledDefinitions(g,success(g.leaf)),/no definitions/); 41 assert.throws(()=>compiledDefinitions(g,g.fork(g.stem(g.stem(g.leaf)),g.leaf)),/tag/); 42 assert.throws(()=>sourceLiteral(g,'x'.repeat(SOURCE_LIMIT+1)),/limit/); 43 assert.throws(()=>sourceLiteral(g,''),/Enter/); 44 assert.throws(()=>sourceLiteral(g,'\ud800'),/surrogate/); 45 }); 46 test('structured compiler diagnostics ignore arbitrary found/context/remainder trees',()=>{ 47 const g=new Graph({fuel:5000}),q=g.fromTernary('212110021100'),loop=g.app(q,q); 48 const location=g.fork(natural(g,2n),g.fork(natural(g,3n),loop)); 49 const diagnostic=g.fork(natural(g,2n),g.fork(natural(g,7n),g.fork(loop,g.fork(location,loop)))); 50 const failure=g.fork(g.leaf,g.fork(diagnostic,loop)); 51 assert.throws(()=>compiledDefinitions(g,failure),/^Error: Compiler diagnostic 2 at 2:3: expected token kind 7$/); 52 assert.ok(g.stats.work<1000); 53 }); 54 55 test('16,384 scalar source validation and Worker encoding have no hidden 256/main-heap cap',()=>{ 56 assert.equal(SOURCE_LIMIT,16384); 57 const ascii='x'.repeat(SOURCE_LIMIT), unicode='🙂'.repeat(SOURCE_LIMIT); 58 assert.equal(sourceText(ascii),ascii);assert.equal(sourceText(unicode),unicode); 59 assert.throws(()=>sourceText(unicode+'x'),/limit/); 60 assert.throws(()=>sourceText(ascii+'x'),/limit/); 61 assert.throws(()=>sourceText(' \n '),/Enter/); 62 assert.throws(()=>sourceText('x\udc00'),/surrogate/); 63 const g=new Graph({nodes:1_000_000,fuel:2_000_000}); 64 const encoded=sourceLiteral(g,unicode); 65 assert.ok(g.stats.nodes>250000);assert.equal(readText(g,encoded,SOURCE_LIMIT),unicode); 66 });