maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
204 lines (162 loc) • 6.32 kB
text/typescript
import {describe, test, expect, afterEach} from 'vitest';
import {now, setNow, restoreNow, isTimeFrozen} from './time_control';
/**
* Helper to wait for real time to advance by at least the specified duration.
* Uses busy-wait to ensure real time passes even when test time is frozen.
*/
function waitForRealTime(ms: number): void {
const realPerformanceNow = typeof performance !== 'undefined' && performance && performance.now ?
performance.now.bind(performance) :
Date.now.bind(Date);
const start = realPerformanceNow();
while (realPerformanceNow() - start < ms) {
// busy wait
}
}
describe('time_control', () => {
afterEach(() => {
// Clean up: restore normal time flow after each test
restoreNow();
});
describe('now()', () => {
test('returns a valid number when not frozen', () => {
const currentTime = now();
expect(typeof currentTime).toBe('number');
expect(currentTime).toBeGreaterThanOrEqual(0);
});
test('advances over time when not frozen', () => {
const timeBeforeDelay = now();
const minimalDelayMs = 1;
waitForRealTime(minimalDelayMs);
const timeAfterDelay = now();
expect(timeAfterDelay).toBeGreaterThanOrEqual(timeBeforeDelay);
});
test('returns frozen time when time is set', () => {
const frozenTime = 123456.789;
setNow(frozenTime);
const time1 = now();
expect(time1).toBe(frozenTime);
const realTimeDelayMs = 10;
waitForRealTime(realTimeDelayMs);
const time2 = now();
expect(time2).toBe(frozenTime);
});
test('handles multiple freeze/unfreeze cycles', () => {
const frozenTime1 = 1000;
const frozenTime2 = 2000;
// First freeze
setNow(frozenTime1);
expect(now()).toBe(frozenTime1);
// Second freeze (override)
setNow(frozenTime2);
expect(now()).toBe(frozenTime2);
// Unfreeze
restoreNow();
const unfrozenTime = now();
expect(unfrozenTime).not.toBe(frozenTime2);
expect(unfrozenTime).toBeGreaterThanOrEqual(0);
// Freeze again
setNow(frozenTime1);
expect(now()).toBe(frozenTime1);
});
});
describe('setNow()', () => {
test('freezes time at specified timestamp', () => {
const timestamp = 99999.123;
setNow(timestamp);
expect(now()).toBe(timestamp);
expect(isTimeFrozen()).toBe(true);
});
test('freezes time at zero', () => {
setNow(0);
expect(now()).toBe(0);
expect(isTimeFrozen()).toBe(true);
});
test('freezes time at negative values', () => {
const negativeTime = -123.456;
setNow(negativeTime);
expect(now()).toBe(negativeTime);
expect(isTimeFrozen()).toBe(true);
});
});
describe('restoreNow()', () => {
test('unfreezes time and returns to real time', () => {
// Freeze time
const frozenTime = 5000;
setNow(frozenTime);
expect(now()).toBe(frozenTime);
expect(isTimeFrozen()).toBe(true);
// Unfreeze
restoreNow();
expect(isTimeFrozen()).toBe(false);
// Time should now be real and advance
const time1 = now();
expect(time1).not.toBe(frozenTime);
expect(time1).toBeGreaterThanOrEqual(0);
// Small delay
waitForRealTime(1);
const time2 = now();
expect(time2).toBeGreaterThanOrEqual(time1);
});
test('is safe to call multiple times', () => {
restoreNow();
expect(isTimeFrozen()).toBe(false);
restoreNow();
expect(isTimeFrozen()).toBe(false);
const time = now();
expect(typeof time).toBe('number');
expect(time).toBeGreaterThanOrEqual(0);
});
});
describe('isTimeFrozen()', () => {
test('correctly tracks frozen state', () => {
const initialFrozenState = isTimeFrozen();
expect(initialFrozenState).toBe(false);
setNow(12345);
const frozenStateAfterSet = isTimeFrozen();
expect(frozenStateAfterSet).toBe(true);
restoreNow();
const frozenStateAfterRestore = isTimeFrozen();
expect(frozenStateAfterRestore).toBe(false);
});
});
describe('integration scenarios', () => {
test('simulates frame-by-frame video recording', () => {
const fps = 60;
const frameTime = 1000 / fps; // ~16.67ms per frame
// Capture three frames at precise intervals
setNow(0);
const frame1 = now();
setNow(frameTime);
const frame2 = now();
setNow(frameTime * 2);
const frame3 = now();
// Verify frame times are precisely spaced
expect(frame1).toBe(0);
expect(frame2).toBeCloseTo(16.67, 2);
expect(frame3).toBeCloseTo(33.33, 2);
expect(frame2 - frame1).toBeCloseTo(frameTime, 2);
expect(frame3 - frame2).toBeCloseTo(frameTime, 2);
// Restore time
restoreNow();
expect(isTimeFrozen()).toBe(false);
});
test('handles animation playback control', () => {
// Pause animation at specific time
const pauseTime = 1500;
setNow(pauseTime);
// Animation should stay at pause time
expect(now()).toBe(pauseTime);
expect(now()).toBe(pauseTime);
expect(now()).toBe(pauseTime);
// Resume animation
restoreNow();
const resumeTime1 = now();
expect(resumeTime1).not.toBe(pauseTime);
// Time should advance normally
waitForRealTime(1);
const resumeTime2 = now();
expect(resumeTime2).toBeGreaterThan(resumeTime1);
});
});
});