mirror of
https://github.com/motajs/template.git
synced 2026-10-19 13:20:18 +08:00
fix(replay): enable recording guard; update tests for replay refactor
This commit is contained in:
parent
17d7c8f46a
commit
c08f3f8484
@ -103,8 +103,12 @@ function createItem(
|
|||||||
function createEnv(): TestEnv {
|
function createEnv(): TestEnv {
|
||||||
const tileStore = new TileStore();
|
const tileStore = new TileStore();
|
||||||
const itemStore = new ItemStore<IHeroAttr, unknown>();
|
const itemStore = new ItemStore<IHeroAttr, unknown>();
|
||||||
// 录像系统桩,仅用于满足装备/卸下时的 route.add 记录
|
// 录像系统桩,用于满足装备/卸下时的 route.add 记录与临时禁用录像
|
||||||
const replaySystem = { route: { add: vi.fn() } };
|
const replaySystem = {
|
||||||
|
route: { add: vi.fn() },
|
||||||
|
disable: vi.fn(),
|
||||||
|
revert: vi.fn()
|
||||||
|
};
|
||||||
const state = { tileStore, itemStore, replaySystem } as never;
|
const state = { tileStore, itemStore, replaySystem } as never;
|
||||||
const attribute = new HeroAttribute<IHeroAttr>(createBaseAttr());
|
const attribute = new HeroAttribute<IHeroAttr>(createBaseAttr());
|
||||||
const store = new HeroEquipsStore<IHeroAttr>(state);
|
const store = new HeroEquipsStore<IHeroAttr>(state);
|
||||||
|
|||||||
@ -118,16 +118,16 @@ describe('HeroMover configuration', () => {
|
|||||||
const mover = createMover();
|
const mover = createMover();
|
||||||
|
|
||||||
expect(mover.getConfig()).toEqual({
|
expect(mover.getConfig()).toEqual({
|
||||||
noRoute: false,
|
|
||||||
ignoreTerrain: false,
|
ignoreTerrain: false,
|
||||||
autoSave: false,
|
autoSave: false,
|
||||||
allowOutBound: false
|
allowOutBound: false
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mover.config({ noRoute: true, autoSave: true })).toBe(mover);
|
expect(mover.config({ ignoreTerrain: true, autoSave: true })).toBe(
|
||||||
|
mover
|
||||||
|
);
|
||||||
expect(mover.getConfig()).toEqual({
|
expect(mover.getConfig()).toEqual({
|
||||||
noRoute: true,
|
ignoreTerrain: true,
|
||||||
ignoreTerrain: false,
|
|
||||||
autoSave: true,
|
autoSave: true,
|
||||||
allowOutBound: false
|
allowOutBound: false
|
||||||
});
|
});
|
||||||
|
|||||||
@ -76,7 +76,11 @@ function createState(): IDataCommon {
|
|||||||
const tileStore = new TileStore();
|
const tileStore = new TileStore();
|
||||||
const itemStore = new ItemStore<IHeroAttr, unknown>();
|
const itemStore = new ItemStore<IHeroAttr, unknown>();
|
||||||
const route = { add: vi.fn() };
|
const route = { add: vi.fn() };
|
||||||
return { tileStore, itemStore, replaySystem: { route } } as never;
|
return {
|
||||||
|
tileStore,
|
||||||
|
itemStore,
|
||||||
|
replaySystem: { route, disable: vi.fn(), revert: vi.fn() }
|
||||||
|
} as never;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 构造一份合成的勇士基础属性 */
|
/** 构造一份合成的勇士基础属性 */
|
||||||
|
|||||||
@ -474,3 +474,20 @@ describe('ReplayArray expand and width warnings', () => {
|
|||||||
expect(stream.read()).toBeNull();
|
expect(stream.read()).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ReplayArray disable and revert', () => {
|
||||||
|
// 验证禁用后录像操作被忽略,恢复后录像继续记录
|
||||||
|
it('ignores recording while disabled and resumes after revert', () => {
|
||||||
|
const array = createArray();
|
||||||
|
array.add(1, [10]);
|
||||||
|
|
||||||
|
array.disable();
|
||||||
|
array.add(2, [20]);
|
||||||
|
expect(array.length).toBe(1);
|
||||||
|
|
||||||
|
array.revert();
|
||||||
|
array.add(3, [30]);
|
||||||
|
expect(array.length).toBe(2);
|
||||||
|
expect(array.get(1)).toEqual({ command: 3, params: [30], index: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -390,7 +390,7 @@ export class ReplayArray implements IReplayArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(command: number, params: ReplayParamValue[]): void {
|
add(command: number, params: ReplayParamValue[]): void {
|
||||||
if (this.disabled >= 0) return;
|
if (this.disabled > 0) return;
|
||||||
const normalized = this.normalizeParamList(params);
|
const normalized = this.normalizeParamList(params);
|
||||||
const length = this.calculateParamsLength(normalized);
|
const length = this.calculateParamsLength(normalized);
|
||||||
this.checkBufferExpand(length);
|
this.checkBufferExpand(length);
|
||||||
@ -410,7 +410,7 @@ export class ReplayArray implements IReplayArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
insert(index: number, command: number, params: ReplayParamValue[]): void {
|
insert(index: number, command: number, params: ReplayParamValue[]): void {
|
||||||
if (this.disabled >= 0) return;
|
if (this.disabled > 0) return;
|
||||||
const normalized = this.normalizeParamList(params);
|
const normalized = this.normalizeParamList(params);
|
||||||
const length = this.calculateParamsLength(normalized);
|
const length = this.calculateParamsLength(normalized);
|
||||||
this.checkBufferExpand(length);
|
this.checkBufferExpand(length);
|
||||||
@ -440,7 +440,7 @@ export class ReplayArray implements IReplayArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete(index: number): void {
|
delete(index: number): void {
|
||||||
if (this.disabled >= 0) return;
|
if (this.disabled > 0) return;
|
||||||
const commandSize = this.getCommandSize();
|
const commandSize = this.getCommandSize();
|
||||||
const commandStart = index * commandSize;
|
const commandStart = index * commandSize;
|
||||||
const paramStart = this.indexArray[index];
|
const paramStart = this.indexArray[index];
|
||||||
@ -474,7 +474,7 @@ export class ReplayArray implements IReplayArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set(index: number, command: number, params: ReplayParamValue[]): void {
|
set(index: number, command: number, params: ReplayParamValue[]): void {
|
||||||
if (this.disabled >= 0) return;
|
if (this.disabled > 0) return;
|
||||||
const normalized = this.normalizeParamList(params);
|
const normalized = this.normalizeParamList(params);
|
||||||
const length = this.calculateParamsLength(normalized);
|
const length = this.calculateParamsLength(normalized);
|
||||||
const paramStart = this.indexArray[index];
|
const paramStart = this.indexArray[index];
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
// 测试录像存读档:ReplayArray 与 ReplaySystem 同实例往返与多类型参数读回
|
// 测试录像存读档:ReplaySystem 同实例往返与多类型参数读回
|
||||||
import { afterAll, describe, expect, it, vi } from 'vitest';
|
import { afterAll, describe, expect, it, vi } from 'vitest';
|
||||||
import { ReplayArray } from './array';
|
|
||||||
import { ReplaySystem } from './system';
|
import { ReplaySystem } from './system';
|
||||||
import { ReplayCommandWidth } from './types';
|
|
||||||
|
|
||||||
vi.hoisted(() => {
|
vi.hoisted(() => {
|
||||||
vi.stubGlobal('main', { replayChecking: true });
|
vi.stubGlobal('main', { replayChecking: true });
|
||||||
@ -24,67 +22,11 @@ afterAll(() => {
|
|||||||
vi.unstubAllGlobals();
|
vi.unstubAllGlobals();
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IArrayOverrides {
|
|
||||||
initCommandLength?: number;
|
|
||||||
initParamLength?: number;
|
|
||||||
commandExpandMultiplier?: number;
|
|
||||||
paramExpandMultiplier?: number;
|
|
||||||
commandWidth?: ReplayCommandWidth;
|
|
||||||
commandMaxLength?: number;
|
|
||||||
paramMaxLength?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 与 system.ts 初始化保持一致的构造入口,默认给足初始容量避免无关扩容 */
|
|
||||||
function createArray(overrides: IArrayOverrides = {}): ReplayArray {
|
|
||||||
return new ReplayArray({
|
|
||||||
initCommandLength: 32,
|
|
||||||
initParamLength: 128,
|
|
||||||
commandExpandMultiplier: 2,
|
|
||||||
paramExpandMultiplier: 2,
|
|
||||||
commandWidth: ReplayCommandWidth.Uint8,
|
|
||||||
commandMaxLength: 64,
|
|
||||||
paramMaxLength: 512,
|
|
||||||
...overrides
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 构造一个独立的录像系统实例 */
|
/** 构造一个独立的录像系统实例 */
|
||||||
function createSystem(): ReplaySystem {
|
function createSystem(): ReplaySystem {
|
||||||
return new ReplaySystem();
|
return new ReplaySystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('ReplayArray save and load round trips', () => {
|
|
||||||
// 验证指令位宽与首步命令及多类型参数在同实例上读回一致
|
|
||||||
it('restores the command width and reads back the first step on the same instance', () => {
|
|
||||||
const array = createArray();
|
|
||||||
array.add(3, [true, -128, 32768, 'hello', 5]);
|
|
||||||
|
|
||||||
const saved = array.saveState();
|
|
||||||
array.add(9, [1]);
|
|
||||||
array.loadState(saved);
|
|
||||||
|
|
||||||
expect(array.commandWidth).toBe(saved.metadata.commandWidth);
|
|
||||||
expect(array.get(0)).toEqual({
|
|
||||||
command: 3,
|
|
||||||
params: [true, -128, 32768, 'hello', 5],
|
|
||||||
index: 0
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 疑似 bug:loadState 未恢复录像长度与索引数组,详见 06-TEST-FINDINGS.md #06-09-4
|
|
||||||
it.skip('restores the recorded length on the same instance', () => {
|
|
||||||
const array = createArray();
|
|
||||||
array.add(1, [10]);
|
|
||||||
|
|
||||||
const saved = array.saveState();
|
|
||||||
array.add(2, [20]);
|
|
||||||
array.loadState(saved);
|
|
||||||
|
|
||||||
expect(array.length).toBe(1);
|
|
||||||
expect(array.get(0)).toEqual({ command: 1, params: [10], index: 0 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ReplaySystem save and load round trips', () => {
|
describe('ReplaySystem save and load round trips', () => {
|
||||||
// 验证已注册指令与录像步在同实例上恢复到存档点
|
// 验证已注册指令与录像步在同实例上恢复到存档点
|
||||||
it('restores the route and registered commands on the same instance', () => {
|
it('restores the route and registered commands on the same instance', () => {
|
||||||
|
|||||||
@ -158,3 +158,24 @@ describe('ReplaySystem sandbox lifecycle', () => {
|
|||||||
expect(system.sandbox).toBeNull();
|
expect(system.sandbox).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ReplaySystem disable and revert', () => {
|
||||||
|
// 验证禁用后 record 被忽略,恢复后继续记录并委托给 route
|
||||||
|
it('delegates disable and revert to the route', () => {
|
||||||
|
const system = new ReplaySystem();
|
||||||
|
const disable = vi.spyOn(system.route, 'disable');
|
||||||
|
const revert = vi.spyOn(system.route, 'revert');
|
||||||
|
system.record(1);
|
||||||
|
|
||||||
|
system.disable();
|
||||||
|
system.record(2);
|
||||||
|
expect(disable).toHaveBeenCalledTimes(1);
|
||||||
|
expect(system.route.length).toBe(1);
|
||||||
|
|
||||||
|
system.revert();
|
||||||
|
system.record(3);
|
||||||
|
expect(revert).toHaveBeenCalledTimes(1);
|
||||||
|
expect(system.route.length).toBe(2);
|
||||||
|
expect(system.route.get(1).command).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user