diff --git a/src/commands.mjs b/src/commands.mjs index aa69f35..a773a14 100644 --- a/src/commands.mjs +++ b/src/commands.mjs @@ -137,12 +137,16 @@ const COMMANDS = [ summary: "pause for N milliseconds (blocking)", // Synchronous/blocking so it pauses inline in the simple no-`await` style: // `while (alive) { work(); sleep(1000); }` actually waits each iteration. - run(_ctx, ...args) { + run(ctx, ...args) { const raw = args[0] ?? 0; const ms = Number(raw); if (!Number.isFinite(ms) || ms < 0) { throw new Error(`moshscript: sleep(ms) requires a finite non-negative number, got ${JSON.stringify(raw)}`); } + if (ctx.dryRun) { + ctx.out(` ⏱ sleep(${ms}) → would pause for ${ms}ms`); + return; + } if (ms > 0) Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); }, }, diff --git a/test/commands.test.mjs b/test/commands.test.mjs index 2f9c3ef..db01a04 100644 --- a/test/commands.test.mjs +++ b/test/commands.test.mjs @@ -66,16 +66,35 @@ test("stop() flips the ctx alive flag off", async () => { }); test("sleep accepts zero milliseconds (no-op, synchronous)", () => { - assert.equal(verb("sleep")({}, 0), undefined); + assert.equal(verb("sleep")({ dryRun: false }, 0), undefined); }); test("sleep throws synchronously on a negative duration", () => { assert.throws( - () => verb("sleep")({}, -1), + () => verb("sleep")({ dryRun: false }, -1), /sleep\(ms\) requires a finite non-negative number/ ); }); +test("sleep in dry-run narrates without blocking", () => { + const ctx = createCtx(); + const originalWait = Atomics.wait; + let waited = false; + Atomics.wait = () => { + waited = true; + return "timed-out"; + }; + + try { + assert.equal(verb("sleep")(ctx, 60_000), undefined); + } finally { + Atomics.wait = originalWait; + } + + assert.equal(waited, false); + assert.match(ctx.lines.join("\n"), /would pause for 60000ms/); +}); + test("the vocabulary exposes summaries for `moshcode commands`", () => { for (const cmd of moshVocabulary().all()) { assert.equal(typeof cmd.name, "string");