| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
import { expect, test } from "bun:test"; |
| 8 |
import { FAST, makeBudget, measure, PATIENCE, SLOW, WINDOW } from "../../static/engine/budget.js"; |
| 9 |
|
| 10 |
|
| 11 |
function run(budget: ReturnType<typeof makeBudget>, ms: number, seconds: number, from = 0) { |
| 12 |
let at = from; |
| 13 |
let moves = 0; |
| 14 |
const end = from + seconds; |
| 15 |
while (at < end) { |
| 16 |
at += ms / 1000; |
| 17 |
if (measure(budget, ms, at)) moves++; |
| 18 |
} |
| 19 |
return { moves, at }; |
| 20 |
} |
| 21 |
|
| 22 |
test("a second over budget costs exactly one rung, not four", () => { |
| 23 |
const budget = makeBudget(0); |
| 24 |
const { moves } = run(budget, SLOW + 8, WINDOW * 1.5); |
| 25 |
expect(moves).toBe(1); |
| 26 |
expect(budget.level).toBe(1); |
| 27 |
}); |
| 28 |
|
| 29 |
test("frames inside the budget leave the level alone", () => { |
| 30 |
const budget = makeBudget(2); |
| 31 |
run(budget, (SLOW + FAST) / 2, 10); |
| 32 |
expect(budget.level).toBe(2); |
| 33 |
}); |
| 34 |
|
| 35 |
test("headroom gives a rung back, but not before two windows of it", () => { |
| 36 |
const budget = makeBudget(0); |
| 37 |
let at = run(budget, SLOW + 8, WINDOW * 1.2).at; |
| 38 |
expect(budget.level).toBe(1); |
| 39 |
at = run(budget, 8, WINDOW * 1.2, at).at; |
| 40 |
expect(budget.level).toBe(1); |
| 41 |
run(budget, 8, WINDOW * 1.2, at); |
| 42 |
expect(budget.level).toBe(0); |
| 43 |
}); |
| 44 |
|
| 45 |
test("it never refines past the rung the window started on", () => { |
| 46 |
const budget = makeBudget(2); |
| 47 |
run(budget, 4, 20); |
| 48 |
expect(budget.level).toBe(2); |
| 49 |
expect(budget.good).toBeLessThanOrEqual(PATIENCE); |
| 50 |
}); |
| 51 |
|
| 52 |
test("it stops at the coarsest rung rather than walking off the ladder", () => { |
| 53 |
const budget = makeBudget(0, 3); |
| 54 |
run(budget, 60, 40); |
| 55 |
expect(budget.level).toBe(3); |
| 56 |
}); |
| 57 |
|
| 58 |
test("one slow window, then one fast, does not oscillate", () => { |
| 59 |
const budget = makeBudget(0); |
| 60 |
let at = 0; |
| 61 |
const levels: number[] = []; |
| 62 |
for (let i = 0; i < 8; i++) { |
| 63 |
at = run(budget, i % 2 === 0 ? SLOW + 8 : 6, WINDOW * 1.05, at).at; |
| 64 |
levels.push(budget.level); |
| 65 |
} |
| 66 |
|
| 67 |
|
| 68 |
expect(levels.filter((l, i) => i > 0 && l < levels[i - 1]!).length).toBe(0); |
| 69 |
}); |
| 70 |
|
| 71 |
test("the window reports the frame rate and the mean it decided on", () => { |
| 72 |
const budget = makeBudget(0); |
| 73 |
run(budget, 20, WINDOW * 1.1); |
| 74 |
expect(budget.mean).toBeCloseTo(20, 1); |
| 75 |
expect(budget.fps).toBeGreaterThan(45); |
| 76 |
expect(budget.fps).toBeLessThan(55); |
| 77 |
}); |