Skip to content

Commit 83e1d37

Browse files
JohnMcLearclaude
andcommitted
test(flake): per-test event-loop yield in the mocha root hook, scoped to etherpad's own backend specs
PR #7854's first iteration added the yield to 6 known-dying spec files (pad.ts, importexportGetPost.ts, socketio.ts, messages.ts, import.ts, clientvar_rev_consistency.ts). Linux backend matrix passed, proving the yield doesn't break the affected tests' own state-sharing assumptions. But the very next Win+plugins run captured **death #13 in sessionsAndGroups.ts**, a 7th file outside the scoped fix. The flake migrated rather than being suppressed. That's strong evidence the trigger is the rapid-sequential-test pattern in general, not specific files. Replace the per-file scope with a root-level `mochaHooks.beforeEach` yield in diagnostics.ts, gated on a file-path check: yield for ether/etherpad's own specs in `tests/backend/specs/`, SKIP for plugin tests loaded from `../node_modules/ep_*/static/tests/backend/specs/`. The plugin-test skip exists because PR #7844 demonstrated that an unconditional global yield breaks `ep_subscript_and_superscript`'s `returns HTML with Subscript HTML tags` series — those plugin tests share state across describe-block boundaries and don't tolerate any microtask reordering. The file-path check preserves PR #7844's finding without re-breaking those tests. Files modified: - src/tests/backend/diagnostics.ts: root beforeEach yield, scoped Per-file changes from the previous commit are reverted — root scope supersedes them and there's no point yielding twice per test. Test plan unchanged from the original PR: - Linux ± plugins must pass. - Windows ± plugins flake rate: ~22% pre-fix. Post-fix, run the CI 5-10x and compare. If unchanged, cadence is ruled out as the trigger and we look at per-test pathologies (jose CNG on Windows, libuv IOCP edge cases unrelated to load). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c4d2467 commit 83e1d37

1 file changed

Lines changed: 19 additions & 1 deletion

File tree

src/tests/backend/diagnostics.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,28 @@ for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK'] as const) {
210210
// resolution fails us. A `start` line per test gives sub-millisecond
211211
// resolution on which test was on the rails when the process died.
212212
export const mochaHooks = {
213-
beforeEach(this: any) {
213+
async beforeEach(this: any) {
214214
if (this.currentTest) {
215215
currentTest = this.currentTest.fullTitle();
216216
diag(`test start: ${currentTest}`);
217+
// Per-test event-loop yield for ether/etherpad's own backend tests
218+
// (not plugin tests). Mitigation against the Windows silent-ELIFECYCLE
219+
// flake: 13 captures so far show V8 starvation 200-400 ms before each
220+
// kill in test files that fire 50-100 short tests in sequence. The
221+
// initial scoped fix in PR #7854 covered 6 known-dying files but the
222+
// flake migrated to a 7th (sessionsAndGroups.ts) on the next run —
223+
// so the trigger is the broader rapid-sequential-test pattern, not
224+
// specific files. A root-level yield covers all our backend tests.
225+
//
226+
// Plugin tests (loaded from `../node_modules/ep_*/static/tests/
227+
// backend/specs`) are SKIPPED via file-path check because PR #7844
228+
// demonstrated the global variant breaks ep_subscript_and_superscript
229+
// tests that share state across describe-block boundaries. The check
230+
// is conservative: only ether/etherpad's own specs get the yield.
231+
const file = this.currentTest.file as string | undefined;
232+
if (file && !/node_modules[/\\]ep_/.test(file)) {
233+
await new Promise((r) => setImmediate(r));
234+
}
217235
// Drop a node-report at test-boundary granularity when the inter-report
218236
// gap is wide enough. Run 26399285213's rerun caught the kill on the
219237
// socketio.ts duplicate-author test, but the previous boundary write

0 commit comments

Comments
 (0)