Files
esengine/packages/network/tests/SyncVar.test.ts
2025-08-08 15:41:37 +08:00

261 lines
9.4 KiB
TypeScript

import { SyncVar, getSyncVarMetadata, SyncVarManager } from '../src/SyncVar';
import { createNetworkComponent } from '../src/SyncVar/SyncVarFactory';
import { createSyncVarProxy } from '../src/SyncVar/SyncVarProxy';
import { NetworkComponent } from '../src/NetworkComponent';
// 测试用的组件类
class TestPlayerComponent extends NetworkComponent {
private _hasAuthority: boolean = false;
public hasAuthority(): boolean {
return this._hasAuthority;
}
public setAuthority(hasAuthority: boolean): void {
this._hasAuthority = hasAuthority;
}
@SyncVar()
public health: number = 100;
@SyncVar({ hook: 'onNameChanged' })
public playerName: string = 'Player';
@SyncVar({ authorityOnly: true })
public isReady: boolean = false;
@SyncVar()
public position = { x: 0, y: 0 };
// Hook回调函数
public onNameChangedCallCount = 0;
public lastNameChange: { oldName: string; newName: string } | null = null;
onNameChanged(oldName: string, newName: string) {
this.onNameChangedCallCount++;
this.lastNameChange = { oldName, newName };
console.log(`Name changed: ${oldName} -> ${newName}`);
}
}
class TestComponentWithoutSyncVar extends NetworkComponent {
public normalField: number = 42;
public hasAuthority(): boolean { return true; }
}
describe('SyncVar系统测试', () => {
beforeEach(() => {
// 清理SyncVar管理器
const manager = SyncVarManager.Instance;
manager['_componentChanges'].clear();
manager['_lastSyncTimes'].clear();
});
describe('装饰器和元数据', () => {
test('应该正确收集SyncVar元数据', () => {
const metadata = getSyncVarMetadata(TestPlayerComponent);
expect(metadata.length).toBe(4);
const healthMeta = metadata.find(m => m.propertyKey === 'health');
expect(healthMeta).toBeDefined();
expect(healthMeta!.fieldNumber).toBe(1);
expect(healthMeta!.options.hook).toBeUndefined();
const nameMeta = metadata.find(m => m.propertyKey === 'playerName');
expect(nameMeta).toBeDefined();
expect(nameMeta!.options.hook).toBe('onNameChanged');
const readyMeta = metadata.find(m => m.propertyKey === 'isReady');
expect(readyMeta).toBeDefined();
expect(readyMeta!.options.authorityOnly).toBe(true);
});
test('没有SyncVar的组件应该返回空元数据', () => {
const metadata = getSyncVarMetadata(TestComponentWithoutSyncVar);
expect(metadata.length).toBe(0);
});
});
describe('代理和变化检测', () => {
test('代理应该能检测到字段变化', () => {
const instance = new TestPlayerComponent();
const proxy = createSyncVarProxy(instance);
// 修改SyncVar字段
proxy.health = 80;
const changes = proxy.getSyncVarChanges();
expect(changes.length).toBe(1);
expect(changes[0].propertyKey).toBe('health');
expect(changes[0].oldValue).toBe(100);
expect(changes[0].newValue).toBe(80);
});
test('非SyncVar字段不应该被记录', () => {
class TestMixedComponent extends NetworkComponent {
@SyncVar()
public syncField: number = 1;
public normalField: number = 2;
public hasAuthority(): boolean { return true; }
}
const instance = new TestMixedComponent();
const proxy = createSyncVarProxy(instance);
// 修改SyncVar字段
proxy.syncField = 10;
// 修改普通字段
proxy.normalField = 20;
const changes = proxy.getSyncVarChanges();
expect(changes.length).toBe(1);
expect(changes[0].propertyKey).toBe('syncField');
});
test('Hook回调应该被触发', () => {
const instance = new TestPlayerComponent();
const proxy = createSyncVarProxy(instance);
// 修改带hook的字段
proxy.playerName = 'NewPlayer';
expect(proxy.onNameChangedCallCount).toBe(1);
expect(proxy.lastNameChange).toEqual({
oldName: 'Player',
newName: 'NewPlayer'
});
});
test('相同值不应该触发变化记录', () => {
const instance = new TestPlayerComponent();
const proxy = createSyncVarProxy(instance);
// 设置相同的值
proxy.health = 100; // 原始值就是100
const changes = proxy.getSyncVarChanges();
expect(changes.length).toBe(0);
});
});
describe('同步数据创建和应用', () => {
test('应该能创建同步数据', () => {
const instance = new TestPlayerComponent();
const proxy = createSyncVarProxy(instance);
// 修改多个字段
proxy.health = 75;
proxy.playerName = 'Hero';
const syncData = proxy.createSyncVarData();
expect(syncData).not.toBeNull();
expect(syncData.componentType).toBe('TestPlayerComponent');
expect(syncData.fieldUpdates.length).toBe(2);
});
test('没有变化时不应该创建同步数据', () => {
const instance = new TestPlayerComponent();
const proxy = createSyncVarProxy(instance);
const syncData = proxy.createSyncVarData();
expect(syncData).toBeNull();
});
test('应该能应用同步数据', () => {
const sourceInstance = new TestPlayerComponent();
const sourceProxy = createSyncVarProxy(sourceInstance);
const targetInstance = new TestPlayerComponent();
const targetProxy = createSyncVarProxy(targetInstance);
// 修改源实例
sourceProxy.health = 60;
sourceProxy.playerName = 'Warrior';
// 创建同步数据
const syncData = sourceProxy.createSyncVarData();
expect(syncData).not.toBeNull();
// 应用到目标实例
targetProxy.applySyncVarData(syncData);
// 验证目标实例的值已更新
expect(targetProxy.health).toBe(60);
expect(targetProxy.playerName).toBe('Warrior');
// 验证hook被触发
expect(targetProxy.onNameChangedCallCount).toBe(1);
});
});
describe('对象类型同步', () => {
test('应该能同步对象类型', () => {
const instance = new TestPlayerComponent();
const proxy = createSyncVarProxy(instance);
// 修改对象字段
proxy.position = { x: 100, y: 200 };
const changes = proxy.getSyncVarChanges();
expect(changes.length).toBe(1);
expect(changes[0].propertyKey).toBe('position');
expect(changes[0].newValue).toEqual({ x: 100, y: 200 });
});
test('对象浅比较应该正确工作', () => {
const instance = new TestPlayerComponent();
const proxy = createSyncVarProxy(instance);
// 设置相同的对象值
proxy.position = { x: 0, y: 0 }; // 原始值
const changes = proxy.getSyncVarChanges();
expect(changes.length).toBe(0); // 应该没有变化
});
});
describe('工厂函数', () => {
test('createNetworkComponent应该为有SyncVar的组件创建代理', () => {
const component = createNetworkComponent(TestPlayerComponent);
expect(component.hasSyncVars()).toBe(true);
// 测试代理功能
component.health = 90;
const changes = component.getSyncVarChanges();
expect(changes.length).toBe(1);
});
test('createNetworkComponent应该为没有SyncVar的组件返回原实例', () => {
const component = createNetworkComponent(TestComponentWithoutSyncVar);
expect(component.hasSyncVars()).toBe(false);
// 修改普通字段不应该有变化记录
component.normalField = 999;
const changes = component.getSyncVarChanges();
expect(changes.length).toBe(0);
});
});
describe('管理器统计', () => {
test('应该能获取管理器统计信息', () => {
const component1 = createNetworkComponent(TestPlayerComponent);
const component2 = createNetworkComponent(TestPlayerComponent);
component1.health = 80;
component2.health = 70;
component2.playerName = 'Test';
const manager = SyncVarManager.Instance;
const stats = manager.getStats();
expect(stats.totalComponents).toBe(2);
expect(stats.totalChanges).toBe(3); // 1 + 2 = 3
expect(stats.pendingChanges).toBe(3);
});
});
});