pcbjam/tests/asyncify/asyncify-races-scheduler.spec.ts
Gergő Törcsvári 4906b9fb56
design-b D-1b: delete the legacy handlesleep runtime from the injector
Doc 20 D-1, second slice: the injector now injects asyncify-scheduler.js
unconditionally — the WX_SCHEDULER=0 legacy opt-out, the
SHIM_DISABLE_HANDLESLEEP ablation skip, and the
SHIM_DISABLE_TRAMPOLINE_HEAL ablation skip are gone, and
scripts/common/shims/handlesleep.js is deleted (the scheduler subsumed
its capture/restore, fiber guard, and trampoline-heal duties in S2).
Comment-only scrubs point the remaining references at the scheduler
shim. .ci-cache-epoch bumped (shim/injector behavior changed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
2026-08-10 10:14:14 +02:00

122 lines
4.4 KiB
TypeScript

import { test, expect, tryLoadApp } from '../e2e/utils/fixtures';
/**
* S2 scheduler-core gates (docs/features/async/17 §3d N1/N4, §4 S2).
* Runs against the races harness; the scheduler shim is the only runtime
* (the legacy handlesleep opt-out was deleted at doc 20 D-1). The self-skip
* on shim-less glue is kept as a stale-build guard.
*
* N1 — single-writer tripwire: Asyncify.currData is an accessor; a pure-JS
* write without scheduler authorization beacons (and throws in strict mode).
* The meta-test INTRODUCES a stray writer and expects the tripwire to fire —
* proving the alarm works, not merely that nobody tripped it.
*
* N4 — wake-never-rewinds-mid-transition: across the races battery (which
* stages overlapping parks, nested modals, out-of-order wakes) the
* scheduler's books must be coherent at settle: no queued wake left, every
* deferral drained, zero unplanned strays, battery green.
*/
type SchedulerState = {
state(): string;
strayWrites: number;
strictStrays: boolean;
readyWakes: unknown[];
deferredWakes: number;
drainedWakes: number;
};
function findSummary(logs: string[]) {
return logs.find((log) => log.includes('[ASYNCIFY_RACES] SUMMARY'));
}
async function bootAndSettle(
page: import('@playwright/test').Page,
testLogger: { consoleLogs: string[] },
): Promise<boolean> {
await page.goto('/standalone/asyncify-races/races_test.html');
const loaded = await tryLoadApp(page, 30000);
expect(loaded, 'races harness should load').toBe(true);
await expect
.poll(() => findSummary(testLogger.consoleLogs) ?? null, {
timeout: 60000,
message: 'battery should emit its SUMMARY line',
})
.not.toBeNull();
return page.evaluate(
() => !!(globalThis as unknown as { __wxScheduler?: unknown }).__wxScheduler,
);
}
test.describe('S2 scheduler core (scheduler glue)', () => {
test('N4: battery leaves coherent books — wakes drained, no strays, battery green', async ({
page,
testLogger,
}) => {
test.setTimeout(180000);
const scheduler = await bootAndSettle(page, testLogger);
test.skip(!scheduler, 'legacy glue — scheduler core absent');
await expect
.poll(
() =>
page.evaluate(
() =>
(globalThis as unknown as { __wxScheduler: SchedulerState }).__wxScheduler
.readyWakes.length,
),
{ timeout: 30000, intervals: [250] },
)
.toBe(0);
const books = await page.evaluate(() => {
const S = (globalThis as unknown as { __wxScheduler: SchedulerState }).__wxScheduler;
return {
ready: S.readyWakes.length,
deferred: S.deferredWakes,
drained: S.drainedWakes,
strays: S.strayWrites,
state: S.state(),
};
});
console.log(`[TEST] scheduler books: ${books.state}`);
expect(books.ready, 'no wake left queued after settle').toBe(0);
expect(books.drained, 'every deferred wake was drained').toBe(books.deferred);
expect(books.strays, 'no stray currData writes during the battery').toBe(0);
const fails = testLogger.consoleLogs.filter((l) => l.includes('[ASYNCIFY_RACES] FAIL '));
expect(fails, 'battery green under the scheduler core').toEqual([]);
});
test('N1 meta: an introduced stray currData write trips the alarm', async ({
page,
testLogger,
}) => {
test.setTimeout(180000);
const scheduler = await bootAndSettle(page, testLogger);
test.skip(!scheduler, 'legacy glue — scheduler core absent');
const result = await page.evaluate(() => {
const S = (globalThis as unknown as { __wxScheduler: SchedulerState }).__wxScheduler;
const A = (globalThis as unknown as { Asyncify: { currData: number | null } }).Asyncify;
const before = S.strayWrites;
const saved = A.currData;
A.currData = saved; // value-preserving, still a stray WRITE
const counted = S.strayWrites === before + 1;
S.strictStrays = true;
let threw = false;
try {
A.currData = saved;
} catch {
threw = true;
}
S.strictStrays = false;
return { counted, threw };
});
expect(result.counted, 'stray write was counted').toBe(true);
expect(result.threw, 'strict mode throws on stray write').toBe(true);
const beacons = testLogger.consoleLogs.filter((l) => l.includes('stray-currdata-write'));
expect(beacons.length, 'stray write beaconed to the console').toBeGreaterThan(0);
});
});