explodorama / commit
Fix red CI: longer test timeout, faster Decoy demo, demo generation perf test
src/sim/demo.ts | 14 ++++++++++++++ test/perf.test.ts | 28 ++++++++++++++++++++++++++++ vite.config.ts | 5 ++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/sim/demo.ts b/src/sim/demo.ts index b67fc1a..e1067d8 100644 --- a/src/sim/demo.ts +++ b/src/sim/demo.ts @@ -174,6 +174,19 @@ const inBlast = (t: Turn, p: Vec) => explosions(t).some((e) => e.tiles.some((q) /** A charge lying on a free floor tile two steps from the player (or `n`), to walk over first. */ const dropping = (kind: ChargeId, n = 2) => (s: GameState, rng: Rng) => dropNear(s, rng, kind, n) +/** + * A nudge for the Decoy scene: head for its drop, then put it down. Random play rarely chains + * pickup, use and a kill (seeds 1-6 took 3.2 s to find, now 0.9 s); a little randomness stays so + * the scenes differ. (Tried for Freeze too: no faster, its slow part is the kill while frozen.) + */ +const fetchAndUse = (kind: 'decoy') => (s: GameState, rng: Rng): Play | null => { + if (rng.next() < 0.15) return null + if (isAvailable(s, kind)) return playOf(SCENES[kind], s, randomTarget(s, rng, kind)) + const drop = s.drops.find((d) => d.kind === kind) + const next = drop && playerPath(s, drop.pos)?.[0] + return next && isAvailable(s, 'move') && isValidTarget(s, 'move', next) ? playOf(SCENES[kind], s, { id: 'move', target: next }) : null +} + /** A test scene's entry (sim/testScenes.ts). */ const testDef = (kind: TestKind): SceneDef => ({ caption: TEST_SCENES[kind].caption, @@ -465,6 +478,7 @@ export const SCENES: Record<SceneKind, SceneDef> = { keyCaption: 'Decoy: {V} and {arrows} (or the {shift} cursor, then {V}) put down a copy of you that Seekers chase instead.', enemies: ['seeker', 'seeker'], prepare: dropping('decoy'), + steer: fetchAndUse('decoy'), shows: (t, seen) => seen.picked.has('decoy') && kills(t).length > 0 && !!t.prev.decoy, }, // One charge at a time (user question, 2026-10-25: "is there already a demo for the charge diff --git a/test/perf.test.ts b/test/perf.test.ts new file mode 100644 index 0000000..a89f199 --- /dev/null +++ b/test/perf.test.ts @@ -0,0 +1,28 @@ +import { expect, it } from 'vitest' +import { generateScene, SCENE_KINDS } from '../src/sim/demo' + +/** + * Perf budget for demo generation. Scenes are found by random play, so a scene kind whose moment + * is a rare chain gets slow without anything failing (CI went red on the Decoy tests' default + * 5 s timeout, 2026-11-01). Each seed of each kind is timed; the slowest are printed, and any seed + * over the budget fails here, naming the kind. The budget is several times the slowest seen + * (Freeze, ~1.1 s on a laptop) to leave room for a slow CI runner. + */ +const SEEDS = [1, 2, 3] +const BUDGET_MS = 4000 + +it(`every demo scene is found within ${BUDGET_MS} ms a seed`, () => { + const times: { kind: string; seed: number; ms: number }[] = [] + for (const kind of SCENE_KINDS) { + for (const seed of SEEDS) { + const t = performance.now() + generateScene(kind, seed) + times.push({ kind, seed, ms: Math.round(performance.now() - t) }) + } + } + times.sort((a, b) => b.ms - a.ms) + console.log('slowest demo scenes:\n' + times.slice(0, 5).map((t) => ` ${t.kind} seed ${t.seed}: ${t.ms} ms`).join('\n')) + const slow = times.filter((t) => t.ms > BUDGET_MS) + expect(slow, `over ${BUDGET_MS} ms: ${slow.map((t) => `${t.kind}#${t.seed} ${t.ms} ms`).join(', ')}`).toEqual([]) + // Every kind runs three times, so this test as a whole is slow (~9 s here): its own timeout. +}, 120_000) diff --git a/vite.config.ts b/vite.config.ts index 812e5e8..44eabee 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'vite' +import { defineConfig } from 'vitest/config' import { qrcode } from 'vite-plugin-qrcode' import { execSync } from 'node:child_process' import { readFileSync } from 'node:fs' @@ -64,4 +64,7 @@ export default defineConfig({ // Sprites stay separate files rather than being inlined into the script: the art for enemies and // pickups that aren't in the game yet then costs nothing until something draws it. build: { target: 'es2022', assetsInlineLimit: 0 }, + // The decoy demo tests replay seeds 1-6 turn by turn: ~3 s here, over vitest's default 5 s on + // the slower CI runner (CI was red from 2026-11-01 for that reason alone). + test: { testTimeout: 30000 }, })