scense_runtime/scripts/runtime_min_test.js
2026-08-29 21:10:24 +08:00

112 lines
4.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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_timer3s 语义 -> 测试用 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 错误降级:点击 box2script_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);
});