Skip to content

Commit e0bf8d6

Browse files
committed
fix: Major pool mode and worker infrastructure improvements
1. Worker Message Handler Race Condition: - Fixed critical race where workers started before message handlers attached - Moved worker creation and handler setup outside recorder.add() - Prevents workers from sending messages before handlers are ready 2. Pool Mode Test Request Loop: - Fixed race condition in REQUEST_TEST/TEST_ASSIGNED message handling - Set up message handler BEFORE sending request (was after) - Tests now properly wait for assignment before requesting next 3. Config File Loading: - Support .cjs/.mjs/.ts extensions when .js not found - Added .cjs to auto-discovery configFileNames list 4. Output Improvements: - Enhanced pool mode output to show test names (✔/✖) - Allow Feature() output in pool mode Progress: - Pool mode infrastructure now working correctly - All 7 tests execute in pool mode (confirmed via logging) - Worker-to-main communication fixed - 187/214 runner tests passing (up from 170) Known Issue: - Pool mode test results not fully aggregating/displaying - Only 2 of 7 executed tests appear in final output - Individual test events may not be captured correctly in pool mode
1 parent a6ac292 commit e0bf8d6

File tree

2 files changed

+38
-18
lines changed

2 files changed

+38
-18
lines changed

lib/command/workers/runTests.js

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,23 @@ const { options, tests, testRoot, workerIndex, poolMode } = workerData
2626
// In pool mode, only suppress output if debug is NOT enabled
2727
// In regular mode, hide result output but allow step output in verbose/debug
2828
if (poolMode && !options.debug) {
29-
// In pool mode without debug, suppress only result summaries and failures, but allow Scenario Steps
29+
// In pool mode without debug, allow test names and important output but suppress verbose details
3030
const originalWrite = process.stdout.write
3131
process.stdout.write = string => {
32-
// Always allow Scenario Steps output (including the circle symbol)
33-
if (string.includes('Scenario Steps:') || string.includes('◯ Scenario Steps:')) {
32+
// Allow test names (✔ or ✖), Scenario Steps, failures, and important markers
33+
if (
34+
string.includes('✔') ||
35+
string.includes('✖') ||
36+
string.includes('Scenario Steps:') ||
37+
string.includes('◯ Scenario Steps:') ||
38+
string.includes('-- FAILURES:') ||
39+
string.includes('AssertionError:') ||
40+
string.includes('Feature(')
41+
) {
3442
return originalWrite.call(process.stdout, string)
3543
}
36-
if (string.includes(' FAIL |') || string.includes(' OK |') || string.includes('-- FAILURES:') || string.includes('AssertionError:') || string.includes('◯ File:')) {
44+
// Suppress result summaries to avoid duplicates
45+
if (string.includes(' FAIL |') || string.includes(' OK |') || string.includes('◯ File:')) {
3746
return true
3847
}
3948
return originalWrite.call(process.stdout, string)
@@ -144,12 +153,13 @@ async function runPoolTests() {
144153

145154
// Keep requesting tests until no more available
146155
while (true) {
147-
// Request a test assignment
148-
sendToParentThread({ type: 'REQUEST_TEST', workerIndex })
149-
156+
// Request a test assignment and wait for response
150157
const testResult = await new Promise((resolve, reject) => {
151-
// Set up pool mode message handler
158+
// Set up pool mode message handler FIRST before sending request
152159
const messageHandler = async eventData => {
160+
// Remove handler immediately to prevent duplicate processing
161+
parentPort?.off('message', messageHandler)
162+
153163
if (eventData.type === 'TEST_ASSIGNED') {
154164
const testUid = eventData.test
155165

@@ -192,24 +202,26 @@ async function runPoolTests() {
192202
}
193203
}
194204

195-
// Signal test completed and request next
196-
parentPort?.off('message', messageHandler)
205+
// Signal test completed
197206
resolve('TEST_COMPLETED')
198207
} catch (err) {
199-
parentPort?.off('message', messageHandler)
200208
reject(err)
201209
}
202210
} else if (eventData.type === 'NO_MORE_TESTS') {
203211
// No tests available, exit worker
204-
parentPort?.off('message', messageHandler)
205212
resolve('NO_MORE_TESTS')
206213
} else {
207-
// Handle other message types (support messages, etc.)
214+
// Handle other message types (support messages, etc.) and re-add handler
208215
container.append({ support: eventData.data })
216+
parentPort?.on('message', messageHandler)
209217
}
210218
}
211219

220+
// Set up handler BEFORE sending request to avoid race condition
212221
parentPort?.on('message', messageHandler)
222+
223+
// Now send the request
224+
sendToParentThread({ type: 'REQUEST_TEST', workerIndex })
213225
})
214226

215227
// Exit if no more tests

lib/workers.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,12 +485,20 @@ class Workers extends EventEmitter {
485485
recorder.startUnlessRunning()
486486
event.dispatcher.emit(event.workers.before)
487487
process.env.RUNS_WITH_WORKERS = 'true'
488-
recorder.add('starting workers', () => {
489-
for (const worker of this.workers) {
490-
const workerThread = createWorker(worker, this.isPoolMode)
491-
this._listenWorkerEvents(workerThread)
492-
}
488+
489+
// Create workers and set up message handlers immediately (not in recorder queue)
490+
// This prevents a race condition where workers start sending messages before handlers are attached
491+
const workerThreads = []
492+
for (const worker of this.workers) {
493+
const workerThread = createWorker(worker, this.isPoolMode)
494+
this._listenWorkerEvents(workerThread)
495+
workerThreads.push(workerThread)
496+
}
497+
498+
recorder.add('workers started', () => {
499+
// Workers are already running, this is just a placeholder step
493500
})
501+
494502
return new Promise(resolve => {
495503
this.on('end', resolve)
496504
})

0 commit comments

Comments
 (0)