tricu

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

gc.test.mjs (3286B)


      1 import test from 'node:test';
      2 import assert from 'node:assert/strict';
      3 import {readFile} from 'node:fs/promises';
      4 import {Graph} from '../dist/graph.js';
      5 import {text,natural,readText} from '../dist/codecs.js';
      6 test('tracing preserves sharing and stable handles; stale IDs are never reused',()=>{
      7  const g=new Graph({fuel:10000}),garbage=g.stem(g.leaf);let root=g.leaf;
      8  for(let i=0;i<31;i++)root=g.fork(root,root);
      9  const before=g.preview(root);g.collect([root]);
     10  assert.equal(g.stats.nodes,32);assert.equal(g.stats.reclaimed,1);
     11  assert.deepEqual(g.preview(root),before);
     12  const fresh=g.stem(g.leaf);assert.notEqual(fresh,garbage);
     13  assert.throws(()=>g.observe(garbage),/Invalid node handle/);
     14  assert.equal(g.observe(root).node.left,g.observe(root).node.right);
     15 });
     16 test('marking traces application/ref edges without forcing discarded divergence',()=>{
     17  const g=new Graph({fuel:10000}),q=g.fromTernary('212110021100'),loop=g.app(q,q);
     18  const value=g.stem(loop);g.collect([value]);assert.equal(g.observe(value).node.child,loop);
     19  const result=g.app(g.fromTernary('200'),value);
     20  assert.equal(g.force(result),g.leaf);g.collect([result]);
     21  assert.equal(g.stats.nodes,2);assert.equal(g.force(result),g.leaf);
     22 });
     23 test('automatic reducer collections preserve every pre-entry caller handle',()=>{
     24  const g=new Graph({nodes:1000,fuel:100000},{threshold:8});
     25  const caller=g.fromTernary('2100'),I=g.fromTernary('21100');
     26  let app=g.leaf;for(let i=0;i<60;i++)app=g.app(I,app);
     27  assert.equal(g.force(app),g.leaf);assert.equal(g.toTernary(caller),'2100');
     28  assert.ok(g.stats.collections>0);g.collect([app,caller]);
     29  assert.ok(g.stats.reclaimed>50);assert.equal(g.force(app),g.leaf);
     30 });
     31 test('GC-enabled evaluator matches all 300 Haskell cases including all frame variants',async()=>{
     32  const corpus=JSON.parse(await readFile(new URL('./conformance.json',import.meta.url)));
     33  for(const c of corpus.cases){
     34   const g=new Graph({nodes:1000,fuel:100000},{threshold:8});
     35   const fn=g.fromTernary(c.program),args=c.arguments.map(x=>g.fromTernary(x));
     36   const result=g.apply(fn,args);assert.equal(g.toTernary(result),c.result);
     37   g.collect([result]);assert.equal(g.toTernary(result),c.result);
     38  }
     39 });
     40 test('collection is budgeted and commits atomically; no high-survival trace storm',()=>{
     41  const g=new Graph({fuel:5}),value=g.stem(g.leaf);
     42  assert.throws(()=>g.collect([value]),/fuel/);assert.equal(g.stats.nodes,2);assert.equal(g.stats.collections,0);
     43  const high=new Graph({nodes:40,fuel:2000},{threshold:2});let root=high.leaf;
     44  for(let i=0;i<20;i++)root=high.stem(root);
     45  assert.equal(high.force(root),root);assert.ok(high.stats.collections<=2);
     46 });
     47 test('explicit Infinity profile removes graph/fuel/stack ceilings without changing defaults',()=>{
     48  const bounded=new Graph({nodes:2,fuel:100,stack:1});bounded.stem(bounded.leaf);
     49  assert.throws(()=>bounded.stem(bounded.leaf),/allocation/);
     50  const g=new Graph({nodes:Infinity,fuel:Infinity,stack:Infinity},{threshold:8});
     51  const I=g.fromTernary('21100');let root=g.leaf;
     52  for(let i=0;i<100;i++)root=g.app(I,root);
     53  assert.equal(g.force(root),g.leaf);assert.equal(g.stats.fuel,Infinity);
     54  assert.ok(g.stats.collections>0);assert.ok(g.stats.work>100);
     55  for(const fuel of [NaN,-Infinity,0,-1,1.5])assert.throws(()=>new Graph({fuel}),/budget/);
     56 });