/
RaskolnickOFF
/
3D
Обзор
Документация
Войти
/
RaskolnickOFF
/
3D
Код
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
js/tests/core.test.js
207 строк
9 KB
RaskolnickOFF
Structure update
06 июн 2026, 03:18
06 июн 2026, 03:18
9bc1c25
Код
Авторство
О чём код?
// js/tests/core.test.js import { Engine } from '../source/core/Engine.js'; import { Entity } from '../source/core/Entity.js'; import { EventBus } from '../source/core/EventBus.js'; import { SystemPipeline } from '../source/core/SystemPipeline.js'; import { isPersistent } from '../source/core/Components.js'; export function runCoreTests(assert) { // ======================================================================== // Engine // ======================================================================== { const pipeline = new SystemPipeline(); const engine = new Engine(pipeline); // createEntity const e = engine.createEntity(); assert(e.id === 0, 'Engine: createEntity id=0', 0, e.id); assert(e instanceof Entity, 'Engine: createEntity returns Entity'); // getEntity assert(engine.getEntity(0) === e, 'Engine: getEntity по id=0'); // getEntity — несуществующий assert(engine.getEntity(999) === undefined, 'Engine: getEntity несуществующий', undefined, engine.getEntity(999)); // with e.add('testComponent', 123); const found = engine.with('testComponent'); assert(found.length === 1, 'Engine: with находит 1 сущность', 1, found.length); assert(found[0] === e, 'Engine: with возвращает правильную сущность'); const notFound = engine.with('nonexistent'); assert(notFound.length === 0, 'Engine: with возвращает пустой массив', 0, notFound.length); // removeEntity engine.removeEntity(0); assert(engine.getEntity(0) === undefined, 'Engine: removeEntity удаляет', undefined, engine.getEntity(0)); assert(engine.count === 0, 'Engine: count после removeEntity', 0, engine.count); // clear const e2 = engine.createEntity(); const e3 = engine.createEntity(); assert(engine.count === 2, 'Engine: count перед clear', 2, engine.count); engine.clear(); assert(engine.count === 0, 'Engine: count после clear', 0, engine.count); assert(engine._nextId === 0, 'Engine: _nextId сброшен', 0, engine._nextId); // serializeAll + deserializeAndSpawn const e4 = engine.createEntity(); e4.add('health', 80); e4.add('velocity', { x: 1, y: 0, z: 0 }); // transient const data = engine.serializeAll(name => isPersistent(name)); // console.log('serializeAll result:', JSON.stringify(data)); assert(data.length === 1, 'Engine: serializeAll 1 сущность', 1, data.length); assert(data[0].components.health === 80, 'Engine: serializeAll health=80', 80, data[0].components.health); assert(!('velocity' in data[0].components), 'Engine: serializeAll velocity не попал (transient)'); engine.clear(); const spawned = engine.deserializeAndSpawn(data); assert(engine.count === 1, 'Engine: deserializeAndSpawn count=1', 1, engine.count); const restored = spawned.get(0); assert(restored !== undefined, 'Engine: deserializeAndSpawn сущность есть'); assert(restored.get('health') === 80, 'Engine: deserializeAndSpawn health=80', 80, restored.get('health')); assert(!restored.has('velocity'), 'Engine: deserializeAndSpawn velocity не восстановлен'); } // ======================================================================== // Entity // ======================================================================== { const e = new Entity(42); // id assert(e.id === 42, 'Entity: id=42', 42, e.id); // add + get e.add('hp', 100); assert(e.get('hp') === 100, 'Entity: add/get hp=100', 100, e.get('hp')); // has assert(e.has('hp'), 'Entity: has hp'); assert(!e.has('mana'), 'Entity: has mana (нет)'); // fluent add const result = e.add('x', 1).add('y', 2); assert(result === e, 'Entity: add возвращает this'); // remove e.remove('hp'); assert(!e.has('hp'), 'Entity: remove hp'); // повторный remove не падает e.remove('hp'); assert(!e.has('hp'), 'Entity: повторный remove не ломает'); // get несуществующего assert(e.get('nonexistent') === undefined, 'Entity: get несуществующего', undefined, e.get('nonexistent')); } // ======================================================================== // EventBus // ======================================================================== { const bus = new EventBus(); // on + emit const calls = []; const cb1 = (d) => calls.push({ name: 'cb1', value: d }); const cb2 = (d) => calls.push({ name: 'cb2', value: d }); bus.on('damage', cb1); bus.on('damage', cb2); bus.emit('damage', 25); assert(calls.length === 2, 'EventBus: on+emit оба колбэка', 2, calls.length); assert(calls[0].name === 'cb1', 'EventBus: порядок cb1 первый'); assert(calls[1].name === 'cb2', 'EventBus: порядок cb2 второй'); assert(calls[0].value === 25, 'EventBus: data=25', 25, calls[0].value); // off bus.off('damage', cb1); calls.length = 0; bus.emit('damage', 99); assert(calls.length === 1, 'EventBus: после off 1 колбэк', 1, calls.length); assert(calls[0].name === 'cb2', 'EventBus: остался cb2'); // off несуществующего — не падает bus.off('damage', cb1); // уже удалён bus.off('nonexistent', () => { }); // emit без подписчиков — не падает bus.emit('nonexistent', {}); assert(true, 'EventBus: emit без подписчиков не падает'); // off все — проверка что массив пуст bus.off('damage', cb2); calls.length = 0; bus.emit('damage', 50); assert(calls.length === 0, 'EventBus: все отписаны', 0, calls.length); } // ======================================================================== // SystemPipeline // ======================================================================== { const p = new SystemPipeline(); // add + execute const log = []; const sysA = { update: () => log.push('A') }; const sysB = { update: () => log.push('B') }; p.add('UPDATE', sysA); p.add('UPDATE', sysB); p.execute('UPDATE'); assert(log.length === 2, 'SystemPipeline: execute 2 системы', 2, log.length); assert(log[0] === 'A', 'SystemPipeline: порядок A', 'A', log[0]); assert(log[1] === 'B', 'SystemPipeline: порядок B', 'B', log[1]); // execute с аргументами const dtLog = []; p.add('PRE_UPDATE', { update: (dt) => dtLog.push(dt) }); p.execute('PRE_UPDATE', 0.016); assert(dtLog[0] === 0.016, 'SystemPipeline: execute с аргументом dt', 0.016, dtLog[0]); // passive не вызывается log.length = 0; const passive = {}; p.addPassive('UPDATE', passive); p.execute('UPDATE'); assert(log.length === 2, 'SystemPipeline: passive не вызывается', 2, log.length); // execute несуществующей фазы — не падает p.execute('NONEXISTENT'); assert(true, 'SystemPipeline: несуществующая фаза не падает'); // getPhaseOrder const order = p.getPhaseOrder(); assert(order.length === 4, 'SystemPipeline: 4 фазы', 4, order.length); assert(order[0] === 'PRE_UPDATE', 'SystemPipeline: первая PRE_UPDATE'); assert(order[3] === 'RENDER', 'SystemPipeline: последняя RENDER'); // addObserver log.length = 0; const observer = { update: () => log.push('OBS') }; p.addObserver('POST_UPDATE', observer); p.execute('POST_UPDATE'); assert(log[0] === 'OBS', 'SystemPipeline: observer вызывается'); } // ======================================================================== // Components // ======================================================================== { // isPersistent assert(isPersistent('health') === true, 'Components: health persistent'); assert(isPersistent('player') === true, 'Components: player persistent'); assert(isPersistent('aiState') === true, 'Components: aiState persistent'); assert(isPersistent('velocity') === false, 'Components: velocity transient'); assert(isPersistent('mesh') === false, 'Components: mesh transient'); assert(isPersistent('nonexistent') === false, 'Components: nonexistent → false'); } }