A test that waits for a deadline somebody guessed is waiting for the wrong thing, so it waits for the editor (#93)
game-maker / suite (push) Successful in 6m48s
game-maker / suite (push) Successful in 6m48s
The suite spent 88% of its step time asleep: 564 s of steps, 496 s of
them in sleep(n) - fixed spans estimated by hand and then copied from
step to step. That is why it was slow, and why it flickered.
The signal is afterFrames(count, timeout) in ui/render.ts, exposed as
__gm.afterFrames(). It wakes the loop itself, because an editor that
draws nothing would never redeem a promise of a frame, and it gives up
after a timeout instead of hanging the run. settle() in the harness
waits two drawn frames (~32 ms) instead of 150-300 ms and falls back to
sleep(150) when the hook is missing (an old build): silently not
waiting would make every later check a coin toss.
Every action goes through it now - click, doubleClick, wheel, drag,
dragAsset, press, typeText, select, mutate, undo, redo - and with that
about 550 sleep() calls behind those actions became redundant, helpers
that are nothing but a mutate in a coat included. What a frame cannot
answer got its own condition instead: waitUntil(expr), play()/stop()
waiting on __gm.state.mode rather than on 300 ms, saved() for the
autosave (the two hand-written polling loops sampled at 300 ms for
something that arrives after a second), and showTab() as a real tool -
47 places called evaluate("__gm.showTab(...)") raw, and 24 of them
waited for nothing at all and read the old panel.
564 s -> 243 s of step time, the waiting share 88% -> 76%. Three kinds
of wait were kept, and each one bit when it was taken away: loading
bytes (textures, fonts), game time that a repaint cannot stand in for
(time scale, shader and particle clocks, the live values a running
graph carries), and the inertia the scroll step measures on purpose.
Two things the new speed uncovered, both real: 25-animation read the
frame menu unfiltered and had frozen an incomplete list (the CI failure
on c990dbc), and 33-render-on-change measured idleness while the
autosave of the step before was still painting - it waits for
saveStatus to leave dirty/saving now, so it measures that nothing
happens any more instead of that it waited long enough.
This commit is contained in:
1 parent
c990dbcb26
commit
7e5003b590
41 files changed
+362
-732
No files matched your search
+17
-15
@@ -35,14 +35,28 @@ const graphFor = (sceneId, soundId) => ({
|
||||
});
|
||||
|
||||
export async function loadScene(t) {
|
||||
const { check, sleep, json, evaluate, mutate, click, clickText, locate, press, play, stop, shot, has } = t;
|
||||
const {
|
||||
check,
|
||||
sleep,
|
||||
json,
|
||||
evaluate,
|
||||
mutate,
|
||||
click,
|
||||
clickText,
|
||||
locate,
|
||||
press,
|
||||
play,
|
||||
stop,
|
||||
shot,
|
||||
has,
|
||||
showTab,
|
||||
} = t;
|
||||
const playInfo = () => json('__gm.playInfo()');
|
||||
const playFrom = () => json('__gm.playFrom()');
|
||||
/** The switch is opened by position: its caption is elided at the width it has. */
|
||||
const openPlayFrom = async () => {
|
||||
const at = await json('__gm.playFromAt()');
|
||||
await click(at.x, at.y);
|
||||
await sleep(250);
|
||||
};
|
||||
const stats = () => json('__gm.sound.stats()');
|
||||
const waitFor = async (read, done, tries = 25) => {
|
||||
@@ -56,8 +70,7 @@ export async function loadScene(t) {
|
||||
|
||||
await press('Escape');
|
||||
await evaluate('__gm.state.select(null)');
|
||||
await evaluate("__gm.showTab('scene')");
|
||||
await sleep(200);
|
||||
await showTab('scene');
|
||||
|
||||
const firstId = await json('__gm.state.project.activeSceneId');
|
||||
const firstName = await json('__gm.state.activeScene.name');
|
||||
@@ -100,7 +113,6 @@ export async function loadScene(t) {
|
||||
props: { volume: 1, loop: true },
|
||||
file: ${JSON.stringify(file)},
|
||||
});`);
|
||||
await sleep(400);
|
||||
|
||||
// 35b. Without a start scene the switch says the active scene – the two questions have the same
|
||||
// answer until someone answers them differently.
|
||||
@@ -118,16 +130,13 @@ export async function loadScene(t) {
|
||||
// project holds every kind of asset, so the new scene is the last row of a list far longer
|
||||
// than the panel – selecting it brings it into view, which is what makes the click land.
|
||||
await evaluate("__gm.state.select({ kind: 'asset', assetId: 'ls-scene' })");
|
||||
await sleep(200);
|
||||
check(
|
||||
'selecting an asset scrolls its row into the panel',
|
||||
await has('Level 2'),
|
||||
JSON.stringify({ row: await json('__gm.assetRow("ls-scene")'), panel: await json('__gm.assetsRect()') }),
|
||||
);
|
||||
await clickText('Level 2', 'right');
|
||||
await sleep(250);
|
||||
await clickText('Set as start scene');
|
||||
await sleep(300);
|
||||
check(
|
||||
'the scene’s context menu names it the start scene, and the switch follows',
|
||||
(await json('__gm.state.project.startSceneId')) === 'ls-scene' && (await playFrom()).scene === 'Level 2',
|
||||
@@ -158,12 +167,10 @@ export async function loadScene(t) {
|
||||
// and not the other was the inconsistency. The run itself is untouched by it.
|
||||
const select = await json('__gm.sceneSelect()');
|
||||
await click(select.x, select.y);
|
||||
await sleep(250);
|
||||
const rows = await json('__gm.menuRows()');
|
||||
// The topmost hit, because the scene's name is on its row in the Assets panel as well.
|
||||
const entry = await locate('Level 2', true);
|
||||
await click(entry.x, entry.y);
|
||||
await sleep(300);
|
||||
const playing = await playInfo();
|
||||
check(
|
||||
'the scene selector stays usable in play mode, and the run runs on',
|
||||
@@ -208,13 +215,11 @@ export async function loadScene(t) {
|
||||
JSON.stringify({ humming, now: await stats() }),
|
||||
);
|
||||
await stop();
|
||||
await sleep(300);
|
||||
|
||||
// 35f. The switch is the way to run what you are working on instead – and it is remembered
|
||||
// between runs, because it says how you work rather than what the game does.
|
||||
await openPlayFrom();
|
||||
await clickText('Active scene');
|
||||
await sleep(300);
|
||||
await play();
|
||||
const fromActive = await playInfo();
|
||||
check(
|
||||
@@ -226,10 +231,8 @@ export async function loadScene(t) {
|
||||
JSON.stringify({ fromActive, switch: await playFrom() }),
|
||||
);
|
||||
await stop();
|
||||
await sleep(300);
|
||||
await openPlayFrom();
|
||||
await clickText('Start scene');
|
||||
await sleep(300);
|
||||
check(
|
||||
'the switch goes back to the start scene',
|
||||
(await playFrom()).mode === 'start' && (await playFrom()).scene === 'Level 2',
|
||||
@@ -243,7 +246,6 @@ export async function loadScene(t) {
|
||||
const gone = new Set(['ls-logic', 'ls-scene', 'ls-vars', 'ls-sound']);
|
||||
p.assets = p.assets.filter((a) => !gone.has(a.id));
|
||||
__gm.state.select(null);`);
|
||||
await sleep(300);
|
||||
check(
|
||||
'the step leaves nothing of its own behind',
|
||||
(await json('__gm.state.project.assets.filter((a) => a.id.startsWith("ls-")).length')) === 0 &&
|
||||
|
||||
Reference in new issue
Block a user