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
@@ -7,6 +7,7 @@ import { same } from '../lib/harness.mjs';
|
||||
*/
|
||||
export async function containers(t) {
|
||||
const {
|
||||
saved,
|
||||
check,
|
||||
sleep,
|
||||
evaluate,
|
||||
@@ -44,7 +45,6 @@ export async function containers(t) {
|
||||
await press('a', { ctrl: true });
|
||||
await typeText(String(value));
|
||||
await press('Enter');
|
||||
await sleep(200);
|
||||
};
|
||||
const notice = async () => {
|
||||
await evaluate("__gm.state.notice = ''");
|
||||
@@ -52,7 +52,6 @@ export async function containers(t) {
|
||||
};
|
||||
const selectObject = async (id) => {
|
||||
await select({ kind: 'object', holderId: 'scene1', objectId: id, objectIds: [id] });
|
||||
await sleep(250);
|
||||
};
|
||||
|
||||
// 9a. Reset both crates to known spots, then Ctrl-click the second row in the hierarchy:
|
||||
@@ -116,7 +115,6 @@ export async function containers(t) {
|
||||
const listedBeforeDelete = (await objects()).length;
|
||||
await select({ kind: 'object', holderId: 'scene1', objectId: 'o2', objectIds: ['o1', 'o2'] });
|
||||
await press('Delete');
|
||||
await sleep(150);
|
||||
check(
|
||||
'Delete removes the whole multi-selection with its subtrees',
|
||||
(await objects()).length === listedBeforeDelete - 5,
|
||||
@@ -131,7 +129,6 @@ export async function containers(t) {
|
||||
const listedBefore = (await objects()).length;
|
||||
await select({ kind: 'object', holderId: 'scene1', objectId: 'o2', objectIds: ['o1', 'o2'] });
|
||||
await clickText('Create container');
|
||||
await sleep(150);
|
||||
s = await state();
|
||||
const instanceA = s.selection?.kind === 'object' ? await object(s.selection.objectId) : null;
|
||||
const containerAsset = instanceA ? await asset(instanceA.assetId) : null;
|
||||
@@ -304,11 +301,8 @@ export async function containers(t) {
|
||||
const before = (await objects()).length;
|
||||
const noticeNow = await notice();
|
||||
await click(row.x, row.y, 'right');
|
||||
await sleep(150);
|
||||
await clickText('Add collider');
|
||||
await sleep(200);
|
||||
await press('Escape');
|
||||
await sleep(100);
|
||||
return { added: (await objects()).length - before, notice: await noticeNow() };
|
||||
};
|
||||
const refused = JSON.stringify({ added: 0, notice: '' });
|
||||
@@ -340,11 +334,9 @@ export async function containers(t) {
|
||||
JSON.stringify(fromPlain),
|
||||
);
|
||||
await undo();
|
||||
await sleep(200);
|
||||
await selectObject(redA);
|
||||
let noticeAfter = await notice();
|
||||
await press('Delete');
|
||||
await sleep(200);
|
||||
check(
|
||||
'removing resolved content is refused with a notice',
|
||||
(await noticeAfter()).includes('container template') && Boolean(await object(redA)),
|
||||
@@ -352,7 +344,6 @@ export async function containers(t) {
|
||||
);
|
||||
noticeAfter = await notice();
|
||||
await press('g', { ctrl: true });
|
||||
await sleep(200);
|
||||
check(
|
||||
'making a container out of resolved content is refused too',
|
||||
(await noticeAfter()).includes('container template'),
|
||||
@@ -360,7 +351,6 @@ export async function containers(t) {
|
||||
);
|
||||
const parentRow = await json("__gm.inspectorField('Parent')");
|
||||
await click(parentRow.x, parentRow.y);
|
||||
await sleep(150);
|
||||
check(
|
||||
'the Parent picker does not even open for resolved content',
|
||||
same(await json('__gm.overlayTexts()'), []),
|
||||
@@ -377,7 +367,6 @@ export async function containers(t) {
|
||||
const redARow = await rowAt(redA);
|
||||
const otherRow = await rowAt(instanceB.id);
|
||||
await drag(redARow.x, redARow.y, otherRow.x, otherRow.y);
|
||||
await sleep(250);
|
||||
check(
|
||||
're-parenting resolved content by dragging its row is refused',
|
||||
(await noticeAfter()).includes('container template') && (await object(redA)).parentId === instanceA.id,
|
||||
@@ -400,7 +389,6 @@ export async function containers(t) {
|
||||
await has('Follows a template of 5 object(s).'),
|
||||
);
|
||||
await clickText('Unpack');
|
||||
await sleep(250);
|
||||
check(
|
||||
'Unpack writes the content into the scene, keeping the ids and the listing',
|
||||
(await storedObjects()).length === storedBeforeUnpack + 5 &&
|
||||
@@ -429,19 +417,16 @@ export async function containers(t) {
|
||||
// not freeze the picker any more – the inspector has to read the holder, not the id.
|
||||
const unpackedParentRow = await json("__gm.inspectorField('Parent')");
|
||||
await click(unpackedParentRow.x, unpackedParentRow.y);
|
||||
await sleep(150);
|
||||
check(
|
||||
'unpacked content offers the Parent picker like any ordinary object',
|
||||
(await json('__gm.overlayTexts()')).includes('None'),
|
||||
JSON.stringify(await json('__gm.overlayTexts()')),
|
||||
);
|
||||
await press('Escape');
|
||||
await sleep(100);
|
||||
noticeAfter = await notice();
|
||||
const redCRow = await rowAt(redC);
|
||||
await click(redCRow.x, redCRow.y, 'right');
|
||||
await clickText('Unparent (move to top level)');
|
||||
await sleep(250);
|
||||
check(
|
||||
'unpacked content can be re-parented, which was refused while it followed the template',
|
||||
(await object(redC)).parentId === '' && (await noticeAfter()) === '',
|
||||
@@ -450,7 +435,6 @@ export async function containers(t) {
|
||||
noticeAfter = await notice();
|
||||
await selectObject(redC);
|
||||
await press('Delete');
|
||||
await sleep(250);
|
||||
check(
|
||||
'and it can be removed – the refusal was about following a template, not about the id',
|
||||
!(await object(redC)) && (await noticeAfter()) === '',
|
||||
@@ -458,7 +442,6 @@ export async function containers(t) {
|
||||
);
|
||||
await selectObject(instanceC.id);
|
||||
await press('Delete');
|
||||
await sleep(250);
|
||||
check(
|
||||
'removing the unpacked container takes the rest of its content with it',
|
||||
(await storedObjects()).length === storedBeforeUnpack - 1,
|
||||
@@ -475,7 +458,6 @@ export async function containers(t) {
|
||||
const beforeSpawn = await objects();
|
||||
await play();
|
||||
await press('Space');
|
||||
await sleep(250);
|
||||
const fresh = (await objects()).filter((o) => !beforeSpawn.some((b) => b.id === o.id));
|
||||
const spawnedRoot = fresh.find((o) => o.parentId === '');
|
||||
check(
|
||||
@@ -506,7 +488,6 @@ export async function containers(t) {
|
||||
check('a moved instance shows apply and revert on its X row', Boolean(applyButton) && (await has('↺')));
|
||||
await shot('27-apply-buttons');
|
||||
await click(applyButton.x, applyButton.y);
|
||||
await sleep(150);
|
||||
check(
|
||||
'apply writes x into the template and the untouched instance follows',
|
||||
(await templateRed()).position.x === -100 && (await object(redA)).position.x === -100,
|
||||
@@ -520,7 +501,6 @@ export async function containers(t) {
|
||||
);
|
||||
const revertButton = await evaluate(`__gm.locate('↺')`);
|
||||
await click(revertButton.x, revertButton.y);
|
||||
await sleep(150);
|
||||
const revertedB = (await storedObjects()).find((o) => o.id === instanceB.id);
|
||||
check(
|
||||
'revert drops the deviation, so the instance follows the template again',
|
||||
@@ -554,7 +534,6 @@ export async function containers(t) {
|
||||
);
|
||||
await shot('28-apply-menu');
|
||||
await clickText('Apply to asset');
|
||||
await sleep(150);
|
||||
check(
|
||||
'Apply to asset writes the value into the asset and drops the deviation',
|
||||
(await asset('crate')).props.width === 77 && (await object(redB)).overrides.width === undefined,
|
||||
@@ -567,7 +546,6 @@ export async function containers(t) {
|
||||
);
|
||||
await click(applyMenu.x, applyMenu.y);
|
||||
await clickText('Apply to container');
|
||||
await sleep(150);
|
||||
check(
|
||||
'Apply to container writes the override into the template, so every instance shows it',
|
||||
(await templateRed()).overrides.width === 77 && (await object(redA)).overrides.width === 77,
|
||||
@@ -597,7 +575,7 @@ export async function containers(t) {
|
||||
// The content must be nowhere in the saved scene, and reloading must resolve it back.
|
||||
await selectObject(redB);
|
||||
await setField('Y', 12);
|
||||
for (let i = 0; i < 30 && (await state()).save !== 'saved'; i++) await sleep(300);
|
||||
await saved();
|
||||
const savedScene = sceneOf(await api(`projects/${projectId}`));
|
||||
const savedInstance = savedScene.objects.find((o) => o.id === instanceB.id);
|
||||
check(
|
||||
|
||||
Reference in new issue
Block a user