新增更多覆盖测试
This commit is contained in:
190
tests/Utils/Extensions/NumberExtension.test.ts
Normal file
190
tests/Utils/Extensions/NumberExtension.test.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
import { NumberExtension } from '../../../src/Utils/Extensions/NumberExtension';
|
||||
|
||||
describe('NumberExtension - 数字扩展工具类测试', () => {
|
||||
describe('toNumber 方法测试', () => {
|
||||
it('应该能够转换数字类型', () => {
|
||||
expect(NumberExtension.toNumber(42)).toBe(42);
|
||||
expect(NumberExtension.toNumber(0)).toBe(0);
|
||||
expect(NumberExtension.toNumber(-42)).toBe(-42);
|
||||
expect(NumberExtension.toNumber(3.14)).toBe(3.14);
|
||||
expect(NumberExtension.toNumber(-3.14)).toBe(-3.14);
|
||||
});
|
||||
|
||||
it('应该能够转换字符串数字', () => {
|
||||
expect(NumberExtension.toNumber('42')).toBe(42);
|
||||
expect(NumberExtension.toNumber('0')).toBe(0);
|
||||
expect(NumberExtension.toNumber('-42')).toBe(-42);
|
||||
expect(NumberExtension.toNumber('3.14')).toBe(3.14);
|
||||
expect(NumberExtension.toNumber('-3.14')).toBe(-3.14);
|
||||
});
|
||||
|
||||
it('应该能够转换科学计数法字符串', () => {
|
||||
expect(NumberExtension.toNumber('1e5')).toBe(100000);
|
||||
expect(NumberExtension.toNumber('1.5e2')).toBe(150);
|
||||
expect(NumberExtension.toNumber('2e-3')).toBe(0.002);
|
||||
});
|
||||
|
||||
it('应该能够转换十六进制字符串', () => {
|
||||
expect(NumberExtension.toNumber('0xFF')).toBe(255);
|
||||
expect(NumberExtension.toNumber('0x10')).toBe(16);
|
||||
expect(NumberExtension.toNumber('0x0')).toBe(0);
|
||||
});
|
||||
|
||||
it('应该能够转换布尔值', () => {
|
||||
expect(NumberExtension.toNumber(true)).toBe(1);
|
||||
expect(NumberExtension.toNumber(false)).toBe(0);
|
||||
});
|
||||
|
||||
it('undefined 和 null 应该返回0', () => {
|
||||
expect(NumberExtension.toNumber(undefined)).toBe(0);
|
||||
expect(NumberExtension.toNumber(null)).toBe(0);
|
||||
});
|
||||
|
||||
it('应该能够处理空字符串和空白字符串', () => {
|
||||
expect(NumberExtension.toNumber('')).toBe(0);
|
||||
expect(NumberExtension.toNumber(' ')).toBe(0);
|
||||
expect(NumberExtension.toNumber('\t')).toBe(0);
|
||||
expect(NumberExtension.toNumber('\n')).toBe(0);
|
||||
});
|
||||
|
||||
it('无效的字符串应该返回NaN', () => {
|
||||
expect(Number.isNaN(NumberExtension.toNumber('abc'))).toBe(true);
|
||||
expect(Number.isNaN(NumberExtension.toNumber('hello'))).toBe(true);
|
||||
expect(Number.isNaN(NumberExtension.toNumber('12abc'))).toBe(true);
|
||||
});
|
||||
|
||||
it('应该能够转换数组(第一个元素)', () => {
|
||||
expect(NumberExtension.toNumber([42])).toBe(42);
|
||||
expect(NumberExtension.toNumber(['42'])).toBe(42);
|
||||
expect(NumberExtension.toNumber([])).toBe(0);
|
||||
});
|
||||
|
||||
it('应该能够转换Date对象(时间戳)', () => {
|
||||
const date = new Date(2023, 0, 1);
|
||||
const timestamp = date.getTime();
|
||||
expect(NumberExtension.toNumber(date)).toBe(timestamp);
|
||||
});
|
||||
|
||||
it('应该能够处理BigInt转换', () => {
|
||||
expect(NumberExtension.toNumber(BigInt(42))).toBe(42);
|
||||
expect(NumberExtension.toNumber(BigInt(0))).toBe(0);
|
||||
});
|
||||
|
||||
it('应该能够处理Infinity和-Infinity', () => {
|
||||
expect(NumberExtension.toNumber(Infinity)).toBe(Infinity);
|
||||
expect(NumberExtension.toNumber(-Infinity)).toBe(-Infinity);
|
||||
expect(NumberExtension.toNumber('Infinity')).toBe(Infinity);
|
||||
expect(NumberExtension.toNumber('-Infinity')).toBe(-Infinity);
|
||||
});
|
||||
|
||||
it('对象转换应该调用valueOf或toString', () => {
|
||||
const objWithValueOf = {
|
||||
valueOf: () => 42
|
||||
};
|
||||
expect(NumberExtension.toNumber(objWithValueOf)).toBe(42);
|
||||
|
||||
const objWithToString = {
|
||||
toString: () => '123'
|
||||
};
|
||||
expect(NumberExtension.toNumber(objWithToString)).toBe(123);
|
||||
});
|
||||
|
||||
it('复杂对象应该返回NaN', () => {
|
||||
const complexObj = { a: 1, b: 2 };
|
||||
expect(Number.isNaN(NumberExtension.toNumber(complexObj))).toBe(true);
|
||||
});
|
||||
|
||||
it('Symbol转换应该抛出错误', () => {
|
||||
expect(() => {
|
||||
NumberExtension.toNumber(Symbol('test'));
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('应该处理特殊数值', () => {
|
||||
expect(NumberExtension.toNumber(Number.MAX_VALUE)).toBe(Number.MAX_VALUE);
|
||||
expect(NumberExtension.toNumber(Number.MIN_VALUE)).toBe(Number.MIN_VALUE);
|
||||
expect(NumberExtension.toNumber(Number.MAX_SAFE_INTEGER)).toBe(Number.MAX_SAFE_INTEGER);
|
||||
expect(NumberExtension.toNumber(Number.MIN_SAFE_INTEGER)).toBe(Number.MIN_SAFE_INTEGER);
|
||||
});
|
||||
|
||||
it('应该处理parseFloat可解析的字符串', () => {
|
||||
// NumberExtension.toNumber使用Number(),不支持parseFloat的部分解析
|
||||
expect(Number.isNaN(NumberExtension.toNumber('42.5px'))).toBe(true);
|
||||
expect(Number.isNaN(NumberExtension.toNumber('100%'))).toBe(true);
|
||||
expect(Number.isNaN(NumberExtension.toNumber('3.14em'))).toBe(true);
|
||||
});
|
||||
|
||||
it('边界情况测试', () => {
|
||||
// 非常大的数字
|
||||
expect(NumberExtension.toNumber('1e308')).toBe(1e308);
|
||||
|
||||
// 非常小的数字
|
||||
expect(NumberExtension.toNumber('1e-308')).toBe(1e-308);
|
||||
|
||||
// 精度问题
|
||||
expect(NumberExtension.toNumber('0.1')).toBe(0.1);
|
||||
expect(NumberExtension.toNumber('0.2')).toBe(0.2);
|
||||
});
|
||||
|
||||
it('应该处理带符号的字符串', () => {
|
||||
expect(NumberExtension.toNumber('+42')).toBe(42);
|
||||
expect(NumberExtension.toNumber('+3.14')).toBe(3.14);
|
||||
expect(NumberExtension.toNumber('-0')).toBe(-0);
|
||||
});
|
||||
|
||||
it('应该处理八进制字符串(不推荐使用)', () => {
|
||||
// 注意:现代JavaScript中八进制字面量是不推荐的
|
||||
expect(NumberExtension.toNumber('010')).toBe(10); // 被当作十进制处理
|
||||
});
|
||||
|
||||
it('性能测试 - 大量转换应该高效', () => {
|
||||
const testValues = [42, '123', true, null, undefined, '3.14'];
|
||||
const startTime = performance.now();
|
||||
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
testValues.forEach(value => {
|
||||
NumberExtension.toNumber(value);
|
||||
});
|
||||
}
|
||||
|
||||
const endTime = performance.now();
|
||||
expect(endTime - startTime).toBeLessThan(100); // 应该在100ms内完成
|
||||
});
|
||||
});
|
||||
|
||||
describe('类型兼容性测试', () => {
|
||||
it('应该与Number()函数行为一致', () => {
|
||||
const testValues = [
|
||||
42, '42', true, false, '', ' ',
|
||||
'3.14', 'abc', [], [42], {}, Infinity, -Infinity
|
||||
];
|
||||
|
||||
testValues.forEach(value => {
|
||||
const extensionResult = NumberExtension.toNumber(value);
|
||||
const nativeResult = Number(value);
|
||||
|
||||
if (Number.isNaN(extensionResult) && Number.isNaN(nativeResult)) {
|
||||
// 两个都是NaN,认为相等
|
||||
expect(true).toBe(true);
|
||||
} else {
|
||||
expect(extensionResult).toBe(nativeResult);
|
||||
}
|
||||
});
|
||||
|
||||
// 特殊处理null和undefined的情况
|
||||
expect(NumberExtension.toNumber(null)).toBe(0);
|
||||
expect(NumberExtension.toNumber(undefined)).toBe(0);
|
||||
expect(Number(null)).toBe(0);
|
||||
expect(Number(undefined)).toBeNaN();
|
||||
});
|
||||
|
||||
it('应该正确处理特殊的相等性', () => {
|
||||
// -0 和 +0
|
||||
expect(Object.is(NumberExtension.toNumber('-0'), -0)).toBe(true);
|
||||
expect(Object.is(NumberExtension.toNumber('+0'), +0)).toBe(true);
|
||||
|
||||
// NaN
|
||||
expect(Number.isNaN(NumberExtension.toNumber('notANumber'))).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
296
tests/Utils/Extensions/TypeUtils.test.ts
Normal file
296
tests/Utils/Extensions/TypeUtils.test.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import { TypeUtils } from '../../../src/Utils/Extensions/TypeUtils';
|
||||
|
||||
describe('TypeUtils - 类型工具类测试', () => {
|
||||
// 测试用的类和对象
|
||||
class TestClass {
|
||||
constructor(public value: number = 0) {}
|
||||
}
|
||||
|
||||
class AnotherTestClass {
|
||||
constructor(public name: string = '') {}
|
||||
}
|
||||
|
||||
function TestFunction() {
|
||||
// @ts-ignore
|
||||
this.prop = 'test';
|
||||
}
|
||||
|
||||
describe('getType 方法测试', () => {
|
||||
it('应该能够获取基本类型对象的构造函数', () => {
|
||||
expect(TypeUtils.getType(42)).toBe(Number);
|
||||
expect(TypeUtils.getType('hello')).toBe(String);
|
||||
expect(TypeUtils.getType(true)).toBe(Boolean);
|
||||
expect(TypeUtils.getType(false)).toBe(Boolean);
|
||||
});
|
||||
|
||||
it('应该能够获取数组的构造函数', () => {
|
||||
expect(TypeUtils.getType([])).toBe(Array);
|
||||
expect(TypeUtils.getType([1, 2, 3])).toBe(Array);
|
||||
expect(TypeUtils.getType(new Array())).toBe(Array);
|
||||
});
|
||||
|
||||
it('应该能够获取对象的构造函数', () => {
|
||||
expect(TypeUtils.getType({})).toBe(Object);
|
||||
expect(TypeUtils.getType(new Object())).toBe(Object);
|
||||
});
|
||||
|
||||
it('应该能够获取Date对象的构造函数', () => {
|
||||
expect(TypeUtils.getType(new Date())).toBe(Date);
|
||||
});
|
||||
|
||||
it('应该能够获取RegExp对象的构造函数', () => {
|
||||
expect(TypeUtils.getType(/test/)).toBe(RegExp);
|
||||
expect(TypeUtils.getType(new RegExp('test'))).toBe(RegExp);
|
||||
});
|
||||
|
||||
it('应该能够获取Error对象的构造函数', () => {
|
||||
expect(TypeUtils.getType(new Error())).toBe(Error);
|
||||
expect(TypeUtils.getType(new TypeError())).toBe(TypeError);
|
||||
expect(TypeUtils.getType(new ReferenceError())).toBe(ReferenceError);
|
||||
});
|
||||
|
||||
it('应该能够获取Map和Set的构造函数', () => {
|
||||
expect(TypeUtils.getType(new Map())).toBe(Map);
|
||||
expect(TypeUtils.getType(new Set())).toBe(Set);
|
||||
expect(TypeUtils.getType(new WeakMap())).toBe(WeakMap);
|
||||
expect(TypeUtils.getType(new WeakSet())).toBe(WeakSet);
|
||||
});
|
||||
|
||||
it('应该能够获取Promise的构造函数', () => {
|
||||
const promise = Promise.resolve(42);
|
||||
expect(TypeUtils.getType(promise)).toBe(Promise);
|
||||
});
|
||||
|
||||
it('应该能够获取自定义类实例的构造函数', () => {
|
||||
const instance = new TestClass(42);
|
||||
expect(TypeUtils.getType(instance)).toBe(TestClass);
|
||||
});
|
||||
|
||||
it('应该能够区分不同的自定义类', () => {
|
||||
const testInstance = new TestClass(42);
|
||||
const anotherInstance = new AnotherTestClass('test');
|
||||
|
||||
expect(TypeUtils.getType(testInstance)).toBe(TestClass);
|
||||
expect(TypeUtils.getType(anotherInstance)).toBe(AnotherTestClass);
|
||||
expect(TypeUtils.getType(testInstance)).not.toBe(AnotherTestClass);
|
||||
});
|
||||
|
||||
it('应该能够获取函数构造的对象的构造函数', () => {
|
||||
// @ts-ignore
|
||||
const instance = new TestFunction();
|
||||
expect(TypeUtils.getType(instance)).toBe(TestFunction);
|
||||
});
|
||||
|
||||
it('应该能够获取内置类型包装器的构造函数', () => {
|
||||
expect(TypeUtils.getType(new Number(42))).toBe(Number);
|
||||
expect(TypeUtils.getType(new String('hello'))).toBe(String);
|
||||
expect(TypeUtils.getType(new Boolean(true))).toBe(Boolean);
|
||||
});
|
||||
|
||||
it('应该能够获取Symbol的构造函数', () => {
|
||||
const sym = Symbol('test');
|
||||
expect(TypeUtils.getType(sym)).toBe(Symbol);
|
||||
});
|
||||
|
||||
it('应该能够获取BigInt的构造函数', () => {
|
||||
const bigInt = BigInt(42);
|
||||
expect(TypeUtils.getType(bigInt)).toBe(BigInt);
|
||||
});
|
||||
|
||||
it('应该处理具有修改过构造函数的对象', () => {
|
||||
const obj = {};
|
||||
// @ts-ignore - 测试边界情况
|
||||
obj.constructor = TestClass;
|
||||
expect(TypeUtils.getType(obj)).toBe(TestClass);
|
||||
});
|
||||
|
||||
it('应该处理继承关系', () => {
|
||||
class Parent {
|
||||
constructor(public value: number = 0) {}
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
constructor(value: number = 0, public name: string = '') {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
|
||||
const childInstance = new Child(42, 'test');
|
||||
expect(TypeUtils.getType(childInstance)).toBe(Child);
|
||||
expect(TypeUtils.getType(childInstance)).not.toBe(Parent);
|
||||
});
|
||||
|
||||
it('应该处理原型链修改的情况', () => {
|
||||
class Original {}
|
||||
class Modified {}
|
||||
|
||||
const instance = new Original();
|
||||
// 修改原型链
|
||||
Object.setPrototypeOf(instance, Modified.prototype);
|
||||
|
||||
// 构造函数属性应该仍然指向Modified
|
||||
expect(TypeUtils.getType(instance)).toBe(Modified);
|
||||
});
|
||||
|
||||
it('应该处理null原型的对象', () => {
|
||||
const nullProtoObj = Object.create(null);
|
||||
// 没有constructor属性的对象会返回undefined
|
||||
const result = TypeUtils.getType(nullProtoObj);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('应该处理ArrayBuffer和TypedArray', () => {
|
||||
expect(TypeUtils.getType(new ArrayBuffer(8))).toBe(ArrayBuffer);
|
||||
expect(TypeUtils.getType(new Uint8Array(8))).toBe(Uint8Array);
|
||||
expect(TypeUtils.getType(new Int32Array(8))).toBe(Int32Array);
|
||||
expect(TypeUtils.getType(new Float64Array(8))).toBe(Float64Array);
|
||||
});
|
||||
|
||||
it('应该处理生成器函数和生成器对象', () => {
|
||||
function* generatorFunction() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
}
|
||||
|
||||
const generator = generatorFunction();
|
||||
expect(TypeUtils.getType(generator)).toBe(Object.getPrototypeOf(generator).constructor);
|
||||
});
|
||||
|
||||
it('应该处理async函数返回的Promise', () => {
|
||||
async function asyncFunction() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
const asyncResult = asyncFunction();
|
||||
expect(TypeUtils.getType(asyncResult)).toBe(Promise);
|
||||
});
|
||||
});
|
||||
|
||||
describe('边界情况和错误处理', () => {
|
||||
it('应该处理undefined输入', () => {
|
||||
expect(() => {
|
||||
TypeUtils.getType(undefined);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('应该处理null输入', () => {
|
||||
expect(() => {
|
||||
TypeUtils.getType(null);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('应该处理构造函数被删除的对象', () => {
|
||||
const obj = new TestClass();
|
||||
// @ts-ignore - 测试边界情况
|
||||
delete obj.constructor;
|
||||
|
||||
// 应该回退到原型链上的constructor
|
||||
expect(TypeUtils.getType(obj)).toBe(TestClass);
|
||||
});
|
||||
|
||||
it('应该处理constructor属性被重写为非函数的情况', () => {
|
||||
const obj = {};
|
||||
// @ts-ignore - 测试边界情况
|
||||
obj.constructor = 'not a function';
|
||||
|
||||
expect(TypeUtils.getType(obj)).toBe('not a function');
|
||||
});
|
||||
});
|
||||
|
||||
describe('性能测试', () => {
|
||||
it('大量类型获取应该高效', () => {
|
||||
const testObjects = [
|
||||
42, 'string', true, [], {}, new Date(), new TestClass(),
|
||||
new Map(), new Set(), Symbol('test'), BigInt(42)
|
||||
];
|
||||
|
||||
const startTime = performance.now();
|
||||
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
testObjects.forEach(obj => {
|
||||
TypeUtils.getType(obj);
|
||||
});
|
||||
}
|
||||
|
||||
const endTime = performance.now();
|
||||
expect(endTime - startTime).toBeLessThan(100); // 应该在100ms内完成
|
||||
});
|
||||
});
|
||||
|
||||
describe('实际使用场景测试', () => {
|
||||
it('应该能够用于类型检查', () => {
|
||||
function isInstanceOf(obj: any, Constructor: any): boolean {
|
||||
return TypeUtils.getType(obj) === Constructor;
|
||||
}
|
||||
|
||||
expect(isInstanceOf(42, Number)).toBe(true);
|
||||
expect(isInstanceOf('hello', String)).toBe(true);
|
||||
expect(isInstanceOf([], Array)).toBe(true);
|
||||
expect(isInstanceOf(new TestClass(), TestClass)).toBe(true);
|
||||
expect(isInstanceOf(new TestClass(), AnotherTestClass)).toBe(false);
|
||||
});
|
||||
|
||||
it('应该能够用于多态类型识别', () => {
|
||||
class Animal {
|
||||
constructor(public name: string) {}
|
||||
}
|
||||
|
||||
class Dog extends Animal {
|
||||
constructor(name: string, public breed: string) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
|
||||
class Cat extends Animal {
|
||||
constructor(name: string, public color: string) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
|
||||
const animals = [
|
||||
new Dog('Buddy', 'Golden Retriever'),
|
||||
new Cat('Whiskers', 'Orange'),
|
||||
new Animal('Generic')
|
||||
];
|
||||
|
||||
const types = animals.map(animal => TypeUtils.getType(animal));
|
||||
expect(types).toEqual([Dog, Cat, Animal]);
|
||||
});
|
||||
|
||||
it('应该能够用于工厂模式', () => {
|
||||
class ComponentFactory {
|
||||
static create(type: any, ...args: any[]) {
|
||||
return new type(...args);
|
||||
}
|
||||
|
||||
static getTypeName(instance: any): string {
|
||||
return TypeUtils.getType(instance).name;
|
||||
}
|
||||
}
|
||||
|
||||
const testInstance = ComponentFactory.create(TestClass, 42);
|
||||
expect(testInstance).toBeInstanceOf(TestClass);
|
||||
expect(testInstance.value).toBe(42);
|
||||
expect(ComponentFactory.getTypeName(testInstance)).toBe('TestClass');
|
||||
});
|
||||
|
||||
it('应该能够用于序列化/反序列化的类型信息', () => {
|
||||
function serialize(obj: any): string {
|
||||
return JSON.stringify({
|
||||
type: TypeUtils.getType(obj).name,
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
function getTypeFromSerialized(serialized: string): string {
|
||||
return JSON.parse(serialized).type;
|
||||
}
|
||||
|
||||
const testObj = new TestClass(42);
|
||||
const serialized = serialize(testObj);
|
||||
const typeName = getTypeFromSerialized(serialized);
|
||||
|
||||
expect(typeName).toBe('TestClass');
|
||||
});
|
||||
});
|
||||
});
|
||||
309
tests/Utils/Timers/Timer.test.ts
Normal file
309
tests/Utils/Timers/Timer.test.ts
Normal file
@@ -0,0 +1,309 @@
|
||||
import { Timer } from '../../../src/Utils/Timers/Timer';
|
||||
import { ITimer } from '../../../src/Utils/Timers/ITimer';
|
||||
import { Time } from '../../../src/Utils/Time';
|
||||
|
||||
// Mock Time.deltaTime
|
||||
jest.mock('../../../src/Utils/Time', () => ({
|
||||
Time: {
|
||||
deltaTime: 0.016 // 默认16ms,约60FPS
|
||||
}
|
||||
}));
|
||||
|
||||
describe('Timer - 定时器测试', () => {
|
||||
let timer: Timer;
|
||||
let mockCallback: jest.Mock;
|
||||
let mockContext: any;
|
||||
|
||||
beforeEach(() => {
|
||||
timer = new Timer();
|
||||
mockCallback = jest.fn();
|
||||
mockContext = { id: 'test-context' };
|
||||
|
||||
// 重置deltaTime
|
||||
(Time as any).deltaTime = 0.016;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
timer.unload();
|
||||
});
|
||||
|
||||
describe('基本初始化和属性', () => {
|
||||
it('应该能够创建定时器实例', () => {
|
||||
expect(timer).toBeInstanceOf(Timer);
|
||||
expect(timer.isDone).toBe(false);
|
||||
expect(timer.elapsedTime).toBe(0);
|
||||
});
|
||||
|
||||
it('应该能够初始化定时器', () => {
|
||||
timer.initialize(1.0, false, mockContext, mockCallback);
|
||||
|
||||
expect(timer.context).toBe(mockContext);
|
||||
expect(timer._timeInSeconds).toBe(1.0);
|
||||
expect(timer._repeats).toBe(false);
|
||||
expect(timer._onTime).toBeDefined();
|
||||
});
|
||||
|
||||
it('应该能够获取泛型上下文', () => {
|
||||
interface TestContext {
|
||||
id: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
const testContext: TestContext = { id: 'test', value: 42 };
|
||||
timer.initialize(1.0, false, testContext, mockCallback);
|
||||
|
||||
const context = timer.getContext<TestContext>();
|
||||
expect(context.id).toBe('test');
|
||||
expect(context.value).toBe(42);
|
||||
});
|
||||
});
|
||||
|
||||
describe('定时器tick逻辑', () => {
|
||||
beforeEach(() => {
|
||||
timer.initialize(1.0, false, mockContext, mockCallback);
|
||||
});
|
||||
|
||||
it('应该正确累加经过时间', () => {
|
||||
(Time as any).deltaTime = 0.5;
|
||||
timer.tick(); // 先累加时间
|
||||
expect(timer.elapsedTime).toBe(0.5);
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('当经过时间超过目标时间时应该触发回调', () => {
|
||||
// 第一次tick:累加时间到1.1,但还没检查触发条件
|
||||
(Time as any).deltaTime = 1.1;
|
||||
timer.tick();
|
||||
expect(timer.elapsedTime).toBe(1.1);
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
|
||||
// 第二次tick:检查条件并触发
|
||||
(Time as any).deltaTime = 0.1;
|
||||
timer.tick();
|
||||
expect(mockCallback).toHaveBeenCalledWith(timer);
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
expect(timer.isDone).toBe(true); // 非重复定时器应该完成
|
||||
});
|
||||
|
||||
it('应该在触发后调整剩余时间', () => {
|
||||
// 第一次累加到1.5
|
||||
(Time as any).deltaTime = 1.5;
|
||||
timer.tick();
|
||||
expect(timer.elapsedTime).toBe(1.5);
|
||||
|
||||
// 第二次检查并触发:1.5 - 1.0 = 0.5,然后加上当前的deltaTime
|
||||
(Time as any).deltaTime = 0.3;
|
||||
timer.tick();
|
||||
expect(timer.elapsedTime).toBe(0.8); // 0.5 + 0.3
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('重复定时器', () => {
|
||||
beforeEach(() => {
|
||||
timer.initialize(1.0, true, mockContext, mockCallback);
|
||||
});
|
||||
|
||||
it('重复定时器不应该自动标记为完成', () => {
|
||||
// 累加时间超过目标
|
||||
(Time as any).deltaTime = 1.1;
|
||||
timer.tick();
|
||||
|
||||
// 检查并触发,但不应该标记为完成
|
||||
(Time as any).deltaTime = 0.1;
|
||||
const isDone = timer.tick();
|
||||
|
||||
expect(isDone).toBe(false);
|
||||
expect(timer.isDone).toBe(false);
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('重复定时器可以多次触发', () => {
|
||||
// 第一次触发
|
||||
(Time as any).deltaTime = 1.1;
|
||||
timer.tick(); // 累加时间
|
||||
(Time as any).deltaTime = 0.1;
|
||||
timer.tick(); // 触发
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
|
||||
// 第二次触发 - 从剩余的0.1开始
|
||||
(Time as any).deltaTime = 0.9; // 0.1 + 0.9 = 1.0
|
||||
timer.tick(); // 累加到1.0
|
||||
(Time as any).deltaTime = 0.1;
|
||||
timer.tick(); // 检查并触发
|
||||
expect(mockCallback).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('定时器控制方法', () => {
|
||||
beforeEach(() => {
|
||||
timer.initialize(1.0, false, mockContext, mockCallback);
|
||||
});
|
||||
|
||||
it('reset应该重置经过时间', () => {
|
||||
(Time as any).deltaTime = 0.5;
|
||||
timer.tick();
|
||||
expect(timer.elapsedTime).toBe(0.5);
|
||||
|
||||
timer.reset();
|
||||
expect(timer.elapsedTime).toBe(0);
|
||||
expect(timer.isDone).toBe(false);
|
||||
});
|
||||
|
||||
it('stop应该标记定时器为完成', () => {
|
||||
timer.stop();
|
||||
expect(timer.isDone).toBe(true);
|
||||
});
|
||||
|
||||
it('停止的定时器不应该触发回调', () => {
|
||||
timer.stop();
|
||||
(Time as any).deltaTime = 2.0;
|
||||
const isDone = timer.tick();
|
||||
|
||||
expect(isDone).toBe(true);
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('上下文绑定', () => {
|
||||
it('回调应该正确绑定到上下文', () => {
|
||||
let contextValue = 0;
|
||||
const testContext = {
|
||||
value: 42,
|
||||
callback: function(this: any, timer: ITimer) {
|
||||
contextValue = this.value;
|
||||
}
|
||||
};
|
||||
|
||||
timer.initialize(1.0, false, testContext, testContext.callback);
|
||||
|
||||
// 触发定时器
|
||||
(Time as any).deltaTime = 1.1;
|
||||
timer.tick(); // 累加时间
|
||||
(Time as any).deltaTime = 0.1;
|
||||
timer.tick(); // 触发
|
||||
|
||||
expect(contextValue).toBe(42);
|
||||
});
|
||||
});
|
||||
|
||||
describe('内存管理', () => {
|
||||
it('unload应该清空对象引用', () => {
|
||||
timer.initialize(1.0, false, mockContext, mockCallback);
|
||||
|
||||
timer.unload();
|
||||
|
||||
expect(timer.context).toBeNull();
|
||||
expect(timer._onTime).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('边界情况', () => {
|
||||
it('零秒定时器应该立即触发', () => {
|
||||
timer.initialize(0, false, mockContext, mockCallback);
|
||||
|
||||
// 第一次累加时间
|
||||
(Time as any).deltaTime = 0.001;
|
||||
timer.tick();
|
||||
expect(timer.elapsedTime).toBe(0.001);
|
||||
|
||||
// 第二次检查并触发(elapsedTime > 0)
|
||||
timer.tick();
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('负数时间定时器应该立即触发', () => {
|
||||
timer.initialize(-1, false, mockContext, mockCallback);
|
||||
|
||||
(Time as any).deltaTime = 0.001;
|
||||
timer.tick(); // 累加时间,elapsedTime = 0.001 > -1
|
||||
timer.tick(); // 检查并触发
|
||||
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('极小deltaTime应该正确累积', () => {
|
||||
timer.initialize(0.1, false, mockContext, mockCallback);
|
||||
(Time as any).deltaTime = 0.05;
|
||||
|
||||
// 第一次不触发
|
||||
timer.tick();
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
expect(timer.elapsedTime).toBe(0.05);
|
||||
|
||||
// 第二次累加到0.1,但条件是 > 0.1 才触发,所以不触发
|
||||
timer.tick();
|
||||
expect(timer.elapsedTime).toBe(0.1);
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
|
||||
// 第三次累加到0.11,但在检查之前elapsedTime还是0.1,所以不触发
|
||||
(Time as any).deltaTime = 0.01; // 0.1 + 0.01 = 0.11 > 0.1
|
||||
timer.tick();
|
||||
expect(timer.elapsedTime).toBe(0.11);
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
|
||||
// 第四次检查并触发(elapsedTime = 0.11 > 0.1)
|
||||
(Time as any).deltaTime = 0.01; // 保持相同的deltaTime
|
||||
timer.tick();
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('性能测试', () => {
|
||||
it('大量tick调用应该高效', () => {
|
||||
timer.initialize(1000, false, mockContext, mockCallback);
|
||||
(Time as any).deltaTime = 0.016;
|
||||
|
||||
const startTime = performance.now();
|
||||
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
timer.tick();
|
||||
}
|
||||
|
||||
const endTime = performance.now();
|
||||
expect(endTime - startTime).toBeLessThan(100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('实际使用场景', () => {
|
||||
it('延迟执行功能', () => {
|
||||
let executed = false;
|
||||
|
||||
timer.initialize(1.0, false, null, () => {
|
||||
executed = true;
|
||||
});
|
||||
|
||||
// 累加时间但不触发
|
||||
(Time as any).deltaTime = 0.9;
|
||||
timer.tick();
|
||||
expect(executed).toBe(false);
|
||||
|
||||
// 继续累加到超过目标时间
|
||||
(Time as any).deltaTime = 0.2; // 总共1.1 > 1.0
|
||||
timer.tick();
|
||||
expect(executed).toBe(false); // 还没检查触发条件
|
||||
|
||||
// 下一次tick才会检查并触发
|
||||
timer.tick();
|
||||
expect(executed).toBe(true);
|
||||
});
|
||||
|
||||
it('重复任务执行', () => {
|
||||
let counter = 0;
|
||||
timer.initialize(0.5, true, null, () => {
|
||||
counter++;
|
||||
});
|
||||
|
||||
// 第一次触发 - 需要超过0.5
|
||||
(Time as any).deltaTime = 0.6;
|
||||
timer.tick(); // 累加到0.6,检查 0.6 > 0.5,触发,剩余0.1
|
||||
timer.tick(); // 再加0.6变成0.7,检查 0.1 <= 0.5不触发,最后加0.6变成0.7
|
||||
expect(counter).toBe(1);
|
||||
|
||||
// 第二次触发条件
|
||||
(Time as any).deltaTime = 0.3; // 0.7 + 0.3 = 1.0 > 0.5 应该触发
|
||||
timer.tick(); // 检查并触发第二次
|
||||
expect(counter).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user