[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

test: gate the concurrent deadlock resolution on the timeout report

Vladimir Sheremet (Jul 27, 2026, 5:29 PM +0200) d3f05b5a 0d93f4a4

+28 -8
+28 -8
test/e2e/test/concurrent.test.ts
··· 10 10 // <- * 11 11 // <------ 12 12 13 - const deadlockSource = ` 13 + // In the deadlocking variant "c" resolves the deadlock only after "b" reported 14 + // its timeout: whether a deadlocked test is reported as timed out depends on 15 + // its own elapsed time the moment the deadlock resolves, and "b" starts its 16 + // clock a few event-loop turns after "a", so an unconditional resolve can 17 + // release "b" while it is still within its own budget. The passing variant 18 + // must not gate: nothing fails there, so the gate would never open. 19 + function deadlockSource(gateOnTimeout: boolean) { 20 + return ` 14 21 import { describe, expect, test } from 'vitest' 15 22 import { createDefer } from '@vitest/utils/helpers' 16 23 ··· 20 27 createDefer<void>(), 21 28 createDefer<void>(), 22 29 ] 30 + const bTimedOut = createDefer<void>() 23 31 24 32 test('a', async () => { 25 33 expect(1).toBe(1) ··· 27 35 await defers[2] 28 36 }) 29 37 30 - test('b', async () => { 38 + test('b', async ({ onTestFailed }) => { 39 + onTestFailed(() => { 40 + bTimedOut.resolve() 41 + }) 31 42 expect(1).toBe(1) 32 43 await defers[0] 33 44 defers[1].resolve() ··· 37 48 test('c', async () => { 38 49 expect(1).toBe(1) 39 50 await defers[1] 51 + ${gateOnTimeout ? 'await bTimedOut' : ''} 40 52 defers[2].resolve() 41 53 }) 42 54 }) 43 55 ` 56 + } 44 57 45 58 test('deadlocks with insufficient maxConcurrency', async () => { 46 59 const { errorTree } = await runInlineTests({ 47 - 'basic.test.ts': deadlockSource, 60 + 'basic.test.ts': deadlockSource(true), 48 61 }, { 49 62 maxConcurrency: 2, 50 63 testTimeout: 500, ··· 74 87 75 88 test('passes when maxConcurrency is high enough', async () => { 76 89 const { stderr, errorTree } = await runInlineTests({ 77 - 'basic.test.ts': deadlockSource, 90 + 'basic.test.ts': deadlockSource(false), 78 91 }, { 79 92 maxConcurrency: 3, 80 93 }) ··· 93 106 `) 94 107 }) 95 108 96 - const suiteDeadlockSource = ` 109 + function suiteDeadlockSource(gateOnTimeout: boolean) { 110 + return ` 97 111 import { describe, expect, test } from 'vitest' 98 112 import { createDefer } from '@vitest/utils/helpers' 99 113 ··· 103 117 createDefer<void>(), 104 118 createDefer<void>(), 105 119 ] 120 + const bTimedOut = createDefer<void>() 106 121 107 122 describe('1st suite', () => { 108 123 test('a', async () => { ··· 111 126 await defers[2] 112 127 }) 113 128 114 - test('b', async () => { 129 + test('b', async ({ onTestFailed }) => { 130 + onTestFailed(() => { 131 + bTimedOut.resolve() 132 + }) 115 133 expect(1).toBe(1) 116 134 await defers[0] 117 135 defers[1].resolve() ··· 123 141 test('c', async () => { 124 142 expect(1).toBe(1) 125 143 await defers[1] 144 + ${gateOnTimeout ? 'await bTimedOut' : ''} 126 145 defers[2].resolve() 127 146 }) 128 147 }) 129 148 }) 130 149 ` 150 + } 131 151 132 152 test('suite deadlocks with insufficient maxConcurrency', async () => { 133 153 const { errorTree } = await runInlineTests({ 134 - 'basic.test.ts': suiteDeadlockSource, 154 + 'basic.test.ts': suiteDeadlockSource(true), 135 155 }, { 136 156 maxConcurrency: 2, 137 157 testTimeout: 500, ··· 162 182 163 183 test('suite passes when maxConcurrency is high enough', async () => { 164 184 const { stderr, errorTree } = await runInlineTests({ 165 - 'basic.test.ts': suiteDeadlockSource, 185 + 'basic.test.ts': suiteDeadlockSource(false), 166 186 }, { 167 187 maxConcurrency: 3, 168 188 })