aboutsummaryrefslogtreecommitdiffstats
path: root/prototypes/workbench/value-input.test.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'prototypes/workbench/value-input.test.mjs')
-rw-r--r--prototypes/workbench/value-input.test.mjs50
1 files changed, 50 insertions, 0 deletions
diff --git a/prototypes/workbench/value-input.test.mjs b/prototypes/workbench/value-input.test.mjs
new file mode 100644
index 0000000..3044048
--- /dev/null
+++ b/prototypes/workbench/value-input.test.mjs
@@ -0,0 +1,50 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import {readFile} from 'node:fs/promises';
+import {spawnSync} from 'node:child_process';
+import {compile} from './web/compiler.mjs';
+import {createRuntime} from './web/runtime.mjs';
+const wasm=await readFile(new URL('./web/core.wasm',import.meta.url));
+const source=await readFile(new URL('./examples/document.pure',import.meta.url),'utf8');
+
+test('numeric controls use explicit change context and cannot read pointer or frame payloads',()=>{
+ const p=compile(source);assert.equal(p.states.length,1);assert.equal(p.views.length,0);assert.equal(p.handlers[0].event,33);
+ for(const s of [
+ 'state x=0\ninput n=value\non n.down {x=1}',
+ 'state x=0\ninput k=Space\non k.change {x=1}',
+ 'state x=0\ninput n=value\non n.change {x=event_x}',
+ 'state x=0\non frame {x=event_value}',
+ 'input n=value\ntext v(0,0,event_value)',
+ 'record R {x=0}\nitem a=R()\ninput k=Space\nrule r(t:R){t.x=event_value}\non k.down use r(a)',
+ 'state event_value=0',
+ ])assert.throws(()=>compile(s),s);
+});
+
+test('numeric input handles scalar rules, rollback, cache invalidation and signed zero',async()=>{
+ const r=await createRuntime(wasm,{cacheEntries:32});
+ const code='record R {x=10}\nitem a=R()\ninput n=value\nrule r(t:R){t.x=event_value}\non n.change use r(a)\ntext v(0,0,a.x)';
+ r.apply(code);r.snapshot();r.snapshot();r.change('n',25);assert.equal(r.snapshot().views[0].value,25);
+ r.change('n',-0);assert(Object.is(r.snapshot().states['a.x'],-0));assert.deepEqual(r.snapshot().held,[]);
+ const before=r.snapshot();assert.throws(()=>r.change('n',Infinity));assert.throws(()=>r.down('n'));assert.throws(()=>r.point('n','down',1,2));
+ assert.equal(r.snapshot().successfulEvents,before.successfulEvents);
+ r.apply(code+'\non n.change {a.x=1/0}');assert.equal(r.change('n',80),4);assert(Object.is(r.snapshot().states['a.x'],-0));
+ assert.equal(r.snapshot().lastFailure.value,80);assert.throws(()=>r.exportDocument(),/오류/);
+ r.apply(code);const saved=r.exportDocument();const next=await createRuntime(wasm);next.restoreDocument(saved);next.change('n',32);assert.equal(next.snapshot().states['a.x'],32);
+});
+
+test('the document policy receives range, keyboard and pointer requests without layouts or text in VM state',async()=>{
+ const r=await createRuntime(wasm);r.apply(source);
+ for(const [value,expected] of [[60,60],[1000,80],[-1000,20],[45.5,45.5]]){assert.equal(r.change('split',value,'control'),0);assert.deepEqual(r.snapshot().states,{ratio:expected});}
+ r.down('reset');r.up('reset');assert.deepEqual(r.snapshot().states,{ratio:50});
+ assert.equal(r.snapshot().views.length,0);assert.equal(r.snapshot().memory.activeStateBytes,8);
+ const published=[];const target={begin(){},view(){assert.fail('no declared views');},value(){assert.fail('no view values');},state(i,v){published.push(v);},end(n){assert.equal(n,0);}};
+ r.present(target);assert.deepEqual(published,[50]);
+ r.apply(source.replace('20, 80','30, 70'));r.change('split',10);assert.equal(r.snapshot().states.ratio,30);
+ const current=r.currentProgram;assert.throws(()=>r.apply(source+'\non split.down {ratio=0}'));assert.equal(r.currentProgram,current);
+});
+
+test('CLI supplies the same numeric control value and rejects nonfinite input',()=>{
+ const run=input=>spawnSync(process.execPath,[new URL('./cli.mjs',import.meta.url).pathname,new URL('./examples/document.pure',import.meta.url).pathname],{input,encoding:'utf8'});
+ const ok=run('change split 95\nquit\n');assert.equal(ok.status,0,ok.stderr);assert.equal(JSON.parse(ok.stdout.trim()).states.ratio,80);
+ const bad=run('change split NaN\nquit\n');assert.equal(bad.status,1);assert.match(bad.stderr,/유한/);
+});