/** The global clock: one scaled step a frame, which is what the world, the particles and the shaders share. */ import { beforeEach, describe, expect, it } from 'vitest'; import { effectiveScale, frameWithin, GameClock, MAX_FRAME, timeStep } from '../../src/core/time'; describe('effectiveScale', () => { it('is the scale itself while running', () => { expect(effectiveScale(1, false)).toBe(1); expect(effectiveScale(2.5, false)).toBe(2.5); }); it('is zero while paused, whatever the scale says', () => { expect(effectiveScale(1, true)).toBe(0); expect(effectiveScale(4, true)).toBe(0); }); it('is zero for anything that is not a positive number', () => { expect(effectiveScale(0, false)).toBe(0); expect(effectiveScale(-1, false)).toBe(0); expect(effectiveScale(Number.NaN, false)).toBe(0); expect(effectiveScale(Number.POSITIVE_INFINITY, false)).toBe(0); }); }); describe('timeStep', () => { it('is the frame itself at scale 1', () => { expect(timeStep(0.016, 1, false)).toBeCloseTo(0.016, 9); }); it('caps the frame before scaling it', () => { // A stalled frame must not jump the game, and at scale 2 the cap is still on the frame, so // twice speed really is twice the step. expect(timeStep(5, 1, false)).toBe(MAX_FRAME); expect(timeStep(5, 2, false)).toBe(MAX_FRAME * 2); }); it('never runs backwards', () => { expect(timeStep(-0.5, 1, false)).toBe(0); }); }); describe('frameWithin (#435)', () => { it('cuts the frame that would go past the time left, at the clock scale', () => { expect(frameWithin(1 / 60, 1, 0.005)).toBeCloseTo(0.005, 12); expect(timeStep(frameWithin(1 / 60, 2, 0.005), 2, false)).toBeCloseTo(0.005, 12); }); it('leaves a frame that stays short of it, and one the clock does not move by', () => { expect(frameWithin(1 / 60, 1, 1)).toBe(1 / 60); expect(frameWithin(1 / 60, 0, 0.005)).toBe(1 / 60); expect(frameWithin(1 / 60, 1, -1)).toBe(0); }); }); describe('GameClock', () => { let clock: GameClock; beforeEach(() => { clock = new GameClock(); }); it('starts at zero and runs in real time', () => { expect(clock.now).toBe(0); expect(clock.scale).toBe(1); expect(clock.advance(0.02)).toBeCloseTo(0.02, 9); clock.advance(0.02); expect(clock.now).toBeCloseTo(0.04, 9); }); it('stands still while paused and picks up where it stopped', () => { clock.advance(0.05); expect(clock.advance(0.05, true)).toBe(0); expect(clock.advance(0.05, true)).toBe(0); expect(clock.now).toBeCloseTo(0.05, 9); clock.advance(0.05); expect(clock.now).toBeCloseTo(0.1, 9); }); it('runs at the scale it is set to', () => { clock.scale = 2; clock.advance(0.03); expect(clock.now).toBeCloseTo(0.06, 9); clock.scale = 0; clock.advance(0.03); expect(clock.now).toBeCloseTo(0.06, 9); }); it('goes back to zero on reset', () => { clock.advance(0.08); clock.reset(); expect(clock.now).toBe(0); }); });