112 lines
4.3 KiB
JavaScript
112 lines
4.3 KiB
JavaScript
// scripts/runtime_min_test.js — runtime 引擎本地最小运行验证(Node 18+)
|
||
//
|
||
// 用法:node scripts/runtime_min_test.js
|
||
// 功能:vm 模拟 window 加载 wwwroot/runtime.js,逐项验证 W-10a~W-10i,
|
||
// 输出 PASS/FAIL 与汇总,fail=0 时退出码 0(可接入 CI/部署门禁)。
|
||
//
|
||
// 预期输出(全 PASS):
|
||
// PASS W-10a 实体树构建 (entities=5)
|
||
// PASS W-10i 独立模式降级初始化
|
||
// PASS W-10c/W-10d 事件路由到实体脚本执行 (hero.state=done)
|
||
// PASS W-10b 实体状态机流转 (idle->active->done)
|
||
// PASS W-10e 定时器按间隔触发 (box1.y 增大)
|
||
// PASS W-10f 碰撞检测触发 onCollide 日志
|
||
// PASS W-10g 暂停/恢复
|
||
// PASS W-10h 错误降级不崩溃 (errorCount 递增, running 仍 true)
|
||
// PASS W-10h 失败实体标记 failed
|
||
// ==== RESULT: pass=9 fail=0 ====
|
||
'use strict';
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const vm = require('vm');
|
||
|
||
const jsPath = path.join(__dirname, '..', 'wwwroot', 'runtime.js');
|
||
const src = fs.readFileSync(jsPath, 'utf8');
|
||
|
||
const sandbox = {
|
||
window: {},
|
||
console: console,
|
||
Date: Date,
|
||
setTimeout: setTimeout,
|
||
clearTimeout: clearTimeout,
|
||
};
|
||
sandbox.window.window = sandbox.window;
|
||
vm.createContext(sandbox);
|
||
vm.runInContext(src, sandbox);
|
||
|
||
const RuntimeEngine = sandbox.window.RuntimeEngine;
|
||
const sleep = function (ms) { return new Promise(function (r) { setTimeout(r, ms); }); };
|
||
|
||
let pass = 0, fail = 0;
|
||
function check(name, cond, extra) {
|
||
if (cond) { pass++; console.log('PASS ' + name + (extra ? ' | ' + extra : '')); }
|
||
else { fail++; console.log('FAIL ' + name + (extra ? ' | ' + extra : '')); }
|
||
}
|
||
|
||
(async function () {
|
||
// ---- W-10i + W-10a:独立模式初始化 / 实体树 ----
|
||
const rt = new RuntimeEngine({ worldId: 't1', tickMs: 8 });
|
||
const st = rt.initWorld(null);
|
||
check('W-10a 实体树构建', rt.getEntities().length === 5,
|
||
'entities=' + rt.getEntities().length);
|
||
check('W-10i 独立模式降级初始化',
|
||
st.mode === 'independent' && st.degraded === true && st.running === false,
|
||
'mode=' + st.mode + ' degraded=' + st.degraded);
|
||
|
||
const evtLog = [];
|
||
rt.on('log', function (m) { evtLog.push(m.message); });
|
||
|
||
// ---- W-10c/W-10d:点击 hero -> click 事件路由到脚本执行 ----
|
||
rt.trigger('hero', 'click');
|
||
rt.processEvents();
|
||
const hero = rt.getEntity('hero');
|
||
check('W-10c/W-10d 事件路由到实体脚本执行', hero.state === 'done',
|
||
'hero.state=' + hero.state);
|
||
check('W-10b 实体状态机流转',
|
||
evtLog.some(function (m) { return m.indexOf('路由事件 click') >= 0; }) &&
|
||
hero.state === 'done', 'log含路由记录');
|
||
|
||
// ---- W-10e 定时器:box1 绑定 script_timer,3s 语义 -> 测试用 10ms ----
|
||
rt.start();
|
||
const box1 = rt.getEntity('box1');
|
||
const y0 = box1.y;
|
||
rt.addTimer(10, function () { rt.dispatchEvent('timer', 'box1', {}); });
|
||
await sleep(120); // 多个 tick 周期,定时器应已触发且脚本已路由
|
||
check('W-10e 定时器按间隔触发', rt.getEntity('box1').y > y0,
|
||
'box1.y=' + rt.getEntity('box1').y + ' (from ' + y0 + ')');
|
||
|
||
// ---- W-10f 碰撞检测:hero 移入 box1 包围盒 ----
|
||
const boxNow = rt.getEntity('box1');
|
||
hero.x = boxNow.x;
|
||
hero.y = boxNow.y;
|
||
const before = evtLog.length;
|
||
rt.checkCollisions();
|
||
check('W-10f 碰撞检测触发', evtLog.length > before &&
|
||
evtLog.slice(before).some(function (m) { return m.indexOf('碰撞') >= 0; }),
|
||
evtLog.slice(before).join(';'));
|
||
|
||
// ---- W-10g 暂停/恢复 ----
|
||
rt.pause();
|
||
check('W-10g 暂停', rt.getStatus().paused === true);
|
||
rt.resume();
|
||
check('W-10g 恢复', rt.getStatus().paused === false && rt.getStatus().running === true);
|
||
|
||
// ---- W-10h 错误降级:点击 box2(script_fail 故意抛错) ----
|
||
const errBefore = rt.getStatus().errorCount;
|
||
rt.trigger('box2', 'click');
|
||
rt.processEvents();
|
||
const st2 = rt.getStatus();
|
||
check('W-10h 错误降级不崩溃',
|
||
st2.errorCount > errBefore && st2.running === true,
|
||
'errors=' + st2.errorCount + ' running=' + st2.running);
|
||
check('W-10h 失败实体标记 failed', rt.getEntity('box2').state === 'failed',
|
||
'box2.state=' + rt.getEntity('box2').state);
|
||
|
||
rt.stop();
|
||
console.log('\n==== RESULT: pass=' + pass + ' fail=' + fail + ' ====');
|
||
process.exit(fail === 0 ? 0 : 1);
|
||
})().catch(function (e) {
|
||
console.error('TEST CRASH:', e);
|
||
process.exit(2);
|
||
});
|