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
@@ -87,7 +87,6 @@ export async function nestedBehavior(t) {
|
||||
${MAKE_OBJECT}
|
||||
scene.objects.push(make('nb-box', 'crate', '', 700, 'Nested box'), make('nb-logic', 'nb-caller', 'nb-box', 700, 'Caller'));
|
||||
`);
|
||||
await sleep(400);
|
||||
|
||||
// 19b. The call really runs, and the value really comes back: the box moves at dx × scale, and
|
||||
// dx is nothing the caller holds – it is what the nested graph returned. Without the result
|
||||
@@ -95,7 +94,6 @@ export async function nestedBehavior(t) {
|
||||
const boxX = async () => (await object('nb-box')).position.x;
|
||||
const idleX = await boxX();
|
||||
await t.play();
|
||||
await sleep(300);
|
||||
const before = (await entity('nb-box')).x;
|
||||
await sleep(1000);
|
||||
const after = (await entity('nb-box')).x;
|
||||
@@ -106,7 +104,6 @@ export async function nestedBehavior(t) {
|
||||
`moved ${speed.toFixed(1)} px in ~1 s – 2 × ${SCALE} means the returned value arrived, 0 would mean nothing came back`,
|
||||
);
|
||||
await stop();
|
||||
await sleep(300);
|
||||
check(
|
||||
'the stored object is untouched by the run, as in any play session',
|
||||
(await boxX()) === idleX,
|
||||
@@ -115,10 +112,7 @@ export async function nestedBehavior(t) {
|
||||
|
||||
// 19c. The node in the graph carries the *other* behavior's ports and name – neither is part of
|
||||
// its own type.
|
||||
await evaluate(
|
||||
`__gm.state.select({ kind: 'node', assetId: 'nb-caller', nodeId: 'nb-call', nodeIds: ['nb-call'] })`,
|
||||
);
|
||||
await sleep(300);
|
||||
await t.select({ kind: 'node', assetId: 'nb-caller', nodeId: 'nb-call', nodeIds: ['nb-call'] });
|
||||
const inPort = await portAt('nb-call', 'in1', false);
|
||||
const outPort = await portAt('nb-call', 'out1', true);
|
||||
check(
|
||||
@@ -137,27 +131,19 @@ export async function nestedBehavior(t) {
|
||||
// 19d. Declaring a port on one behavior reaches the node in the other one, live. This is the
|
||||
// check that a port list derived from another graph is really derived and not copied.
|
||||
check('the calling node has no second input yet', !(await portAt('nb-call', 'in2', false)));
|
||||
await evaluate(
|
||||
`__gm.state.select({ kind: 'node', assetId: 'nb-double', nodeId: 'nb-start', nodeIds: ['nb-start'] })`,
|
||||
);
|
||||
await sleep(300);
|
||||
await t.select({ kind: 'node', assetId: 'nb-double', nodeId: 'nb-start', nodeIds: ['nb-start'] });
|
||||
const addButton = await locate('+ Input');
|
||||
check('the start node offers its inputs in the inspector', Boolean(addButton), JSON.stringify(addButton));
|
||||
await shot('38-behavior-ports');
|
||||
await click(addButton.x, addButton.y);
|
||||
await sleep(200);
|
||||
await clickText('Number');
|
||||
await sleep(300);
|
||||
const declared = await startPorts();
|
||||
check(
|
||||
'adding an input declares it on the start node',
|
||||
declared.length === 2 && declared[1].key === 'in2' && declared[1].kind === 'number',
|
||||
JSON.stringify(declared),
|
||||
);
|
||||
await evaluate(
|
||||
`__gm.state.select({ kind: 'node', assetId: 'nb-caller', nodeId: 'nb-call', nodeIds: ['nb-call'] })`,
|
||||
);
|
||||
await sleep(300);
|
||||
await t.select({ kind: 'node', assetId: 'nb-caller', nodeId: 'nb-call', nodeIds: ['nb-call'] });
|
||||
check(
|
||||
'and the node in the other graph grew that input without being touched',
|
||||
Boolean(await portAt('nb-call', 'in2', false)),
|
||||
@@ -165,7 +151,6 @@ export async function nestedBehavior(t) {
|
||||
|
||||
// 19e. Undo takes the port back, and with it the port on the calling node.
|
||||
await undo();
|
||||
await sleep(300);
|
||||
const afterUndo = await startPorts();
|
||||
check(
|
||||
'undo removes it in both graphs',
|
||||
@@ -180,14 +165,11 @@ export async function nestedBehavior(t) {
|
||||
const idsBefore = await nodeIds();
|
||||
const bare = await json('__gm.graph.emptySpot()');
|
||||
await click(bare.x, bare.y, 'right');
|
||||
await sleep(150);
|
||||
check('node palette opens', Boolean(await locate('Search nodes…')));
|
||||
await typeText('Double');
|
||||
await sleep(200);
|
||||
// Enter takes the first match. NOT a click on the label: "Double" also names the asset in the
|
||||
// list on the left, and that is what a click by text would find first.
|
||||
await press('Enter');
|
||||
await sleep(300);
|
||||
const added = (await asset('nb-caller')).graph.nodes.find((n) => !idsBefore.includes(n.id));
|
||||
check(
|
||||
'picking a behavior in the palette drops in a node already set to it',
|
||||
@@ -197,7 +179,6 @@ export async function nestedBehavior(t) {
|
||||
JSON.stringify(added && { type: added.type, params: added.params }),
|
||||
);
|
||||
await undo();
|
||||
await sleep(200);
|
||||
|
||||
// 19g. Dragging a behavior out of the asset panel drops a call into the graph, where the
|
||||
// pointer let go of it.
|
||||
@@ -213,7 +194,6 @@ export async function nestedBehavior(t) {
|
||||
});
|
||||
const dropAt = spot(0.25, 0.75);
|
||||
await t.dragAsset('nb-double', dropAt.x, dropAt.y);
|
||||
await sleep(300);
|
||||
const dropped = await newNodeIn(beforeDrag);
|
||||
check(
|
||||
'a behavior dragged from the asset panel becomes a call node',
|
||||
@@ -231,7 +211,6 @@ export async function nestedBehavior(t) {
|
||||
const beforeEmpty = await nodeIds();
|
||||
const emptyRow = await json(`__gm.assetRow('nb-empty')`);
|
||||
await t.dragAsset('nb-empty', spot(0.6, 0.75).x, spot(0.6, 0.75).y);
|
||||
await sleep(300);
|
||||
const emptyCall = await newNodeIn(beforeEmpty);
|
||||
check(
|
||||
'a behavior without a start node can be added just the same',
|
||||
@@ -255,7 +234,6 @@ export async function nestedBehavior(t) {
|
||||
const beforeSelf = await nodeIds();
|
||||
const selfRow = await json(`__gm.assetRow('nb-caller')`);
|
||||
await t.dragAsset('nb-caller', spot(0.45, 0.5).x, spot(0.45, 0.5).y);
|
||||
await sleep(300);
|
||||
const afterSelf = await nodeIds();
|
||||
const { notice } = await t.state();
|
||||
// `dragging` tells a refusal apart from a drag that never arrived – both leave the graph alone.
|
||||
@@ -277,7 +255,6 @@ export async function nestedBehavior(t) {
|
||||
check('the graph shown is still the caller', (await evaluate('__gm.graph.current()')) === 'nb-caller');
|
||||
check('no breadcrumb on a graph opened from the outside', (await json('__gm.graph.crumbs()')).length === 0);
|
||||
await t.doubleClick(callNode.x, callNode.y);
|
||||
await sleep(400);
|
||||
const opened = await evaluate('__gm.graph.current()');
|
||||
check(
|
||||
'double-clicking a call node opens the behavior it runs',
|
||||
@@ -307,7 +284,6 @@ export async function nestedBehavior(t) {
|
||||
await trail(),
|
||||
);
|
||||
await t.doubleClick(inner.x, inner.y);
|
||||
await sleep(400);
|
||||
check(
|
||||
'each call opened further down adds a crumb',
|
||||
(await evaluate('__gm.graph.current()')) === 'nb-empty' && (await trail()) === 'Caller › Double › Empty*',
|
||||
@@ -330,7 +306,6 @@ export async function nestedBehavior(t) {
|
||||
const clickCrumb = async (name) => {
|
||||
const crumb = (await crumbs()).find((c) => c.name === name);
|
||||
await click(crumb.x, crumb.y);
|
||||
await sleep(300);
|
||||
};
|
||||
await clickCrumb('Double');
|
||||
await shot('39c-breadcrumb-back');
|
||||
@@ -350,12 +325,10 @@ export async function nestedBehavior(t) {
|
||||
const enterDouble = async () => {
|
||||
const call = await json(`__gm.graph.nodeAt('nb-call')`);
|
||||
await t.doubleClick(call.x, call.y);
|
||||
await sleep(400);
|
||||
};
|
||||
await enterDouble();
|
||||
const emptyAgain = await json(`__gm.assetRow('nb-empty')`);
|
||||
await click(emptyAgain.x, emptyAgain.y);
|
||||
await sleep(300);
|
||||
check(
|
||||
'choosing another behavior in the asset panel ends the trail',
|
||||
(await evaluate('__gm.graph.current()')) === 'nb-empty' && (await crumbs()).length === 0,
|
||||
@@ -366,10 +339,8 @@ export async function nestedBehavior(t) {
|
||||
await enterDouble();
|
||||
const picker = await json('__gm.graph.pickerAt()');
|
||||
await click(picker.x, picker.y);
|
||||
await sleep(150);
|
||||
const emptyItem = await locate('Empty', true);
|
||||
await click(emptyItem.x, emptyItem.y);
|
||||
await sleep(300);
|
||||
check(
|
||||
'choosing another behavior in the picker ends the trail',
|
||||
(await evaluate('__gm.graph.current()')) === 'nb-empty' && (await crumbs()).length === 0,
|
||||
@@ -385,7 +356,6 @@ export async function nestedBehavior(t) {
|
||||
p.assets = p.assets.filter((a) => !mine.includes(a.id));
|
||||
scene.objects = scene.objects.filter((o) => o.id !== 'nb-box' && o.id !== 'nb-logic');
|
||||
`);
|
||||
await sleep(300);
|
||||
const left = await json(`{
|
||||
objects: __gm.sceneObjects().filter((o) => o.id.startsWith('nb-')).length,
|
||||
assets: __gm.state.project.assets.filter((a) => a.id.startsWith('nb-')).length,
|
||||
|
||||
Reference in new issue
Block a user