codeceptjs 4.0.2-beta.3 → 4.0.2-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/command/workers/runTests.js +42 -25
- package/lib/workers.js +21 -1
- package/package.json +1 -1
|
@@ -19,6 +19,18 @@ const stderr = ''
|
|
|
19
19
|
|
|
20
20
|
const { options, tests, testRoot, workerIndex, poolMode } = workerData
|
|
21
21
|
|
|
22
|
+
// Global error handlers to prevent worker from hanging
|
|
23
|
+
process.on('uncaughtException', (err) => {
|
|
24
|
+
console.error(`[Worker ${workerIndex}] Uncaught exception:`, err.message)
|
|
25
|
+
console.error(err.stack)
|
|
26
|
+
process.exit(1)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
30
|
+
console.error(`[Worker ${workerIndex}] Unhandled rejection:`, reason)
|
|
31
|
+
process.exit(1)
|
|
32
|
+
})
|
|
33
|
+
|
|
22
34
|
// hide worker output
|
|
23
35
|
// In pool mode, only suppress output if debug is NOT enabled
|
|
24
36
|
// In regular mode, hide result output but allow step output in verbose/debug
|
|
@@ -116,7 +128,10 @@ initPromise = (async function () {
|
|
|
116
128
|
// We'll reload test files fresh for each test request
|
|
117
129
|
} else {
|
|
118
130
|
// Legacy mode - filter tests upfront
|
|
131
|
+
console.log(`[Worker ${workerIndex}] Starting test filtering. Assigned ${tests.length} test UIDs`)
|
|
119
132
|
filterTests()
|
|
133
|
+
const finalCount = mocha.suite.total()
|
|
134
|
+
console.log(`[Worker ${workerIndex}] After filtering: ${finalCount} tests to run`)
|
|
120
135
|
}
|
|
121
136
|
|
|
122
137
|
// run tests
|
|
@@ -126,6 +141,7 @@ initPromise = (async function () {
|
|
|
126
141
|
await runTests()
|
|
127
142
|
} else {
|
|
128
143
|
// No tests to run, close the worker
|
|
144
|
+
console.error(`[Worker ${workerIndex}] ERROR: No tests found after filtering! Assigned ${tests.length} UIDs but none matched.`)
|
|
129
145
|
parentPort?.close()
|
|
130
146
|
}
|
|
131
147
|
} catch (err) {
|
|
@@ -140,6 +156,7 @@ async function runTests() {
|
|
|
140
156
|
try {
|
|
141
157
|
await codecept.bootstrap()
|
|
142
158
|
} catch (err) {
|
|
159
|
+
console.error(`[Worker ${workerIndex}] Bootstrap error:`, err.message)
|
|
143
160
|
throw new Error(`Error while running bootstrap file :${err}`)
|
|
144
161
|
}
|
|
145
162
|
listenToParentThread()
|
|
@@ -147,8 +164,15 @@ async function runTests() {
|
|
|
147
164
|
disablePause()
|
|
148
165
|
try {
|
|
149
166
|
await codecept.run()
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.error(`[Worker ${workerIndex}] Runtime error:`, err.message)
|
|
169
|
+
throw err
|
|
150
170
|
} finally {
|
|
151
|
-
|
|
171
|
+
try {
|
|
172
|
+
await codecept.teardown()
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error(`[Worker ${workerIndex}] Teardown error:`, err.message)
|
|
175
|
+
}
|
|
152
176
|
}
|
|
153
177
|
}
|
|
154
178
|
|
|
@@ -336,24 +360,24 @@ function filterTests() {
|
|
|
336
360
|
mocha.files = files
|
|
337
361
|
mocha.loadFiles()
|
|
338
362
|
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
allLoadedTests.push({ uid: test.uid, title: test.fullTitle() });
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
console.log(`[Worker ${workerIndex}] Loaded ${allLoadedTests.length} tests, expecting ${tests.length} tests`);
|
|
349
|
-
|
|
350
|
-
const loadedUids = new Set(allLoadedTests.map(t => t.uid));
|
|
351
|
-
const missingTests = tests.filter(uid => !loadedUids.has(uid));
|
|
352
|
-
|
|
353
|
-
if (missingTests.length > 0) {
|
|
354
|
-
console.log(`[Worker ${workerIndex}] WARNING: ${missingTests.length} assigned tests not found in loaded files`);
|
|
355
|
-
console.log(`[Worker ${workerIndex}] Missing UIDs:`, missingTests);
|
|
363
|
+
// Collect all loaded tests for debugging
|
|
364
|
+
const allLoadedTests = [];
|
|
365
|
+
mocha.suite.eachTest(test => {
|
|
366
|
+
if (test) {
|
|
367
|
+
allLoadedTests.push({ uid: test.uid, title: test.fullTitle() });
|
|
356
368
|
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
console.log(`[Worker ${workerIndex}] Loaded ${allLoadedTests.length} tests from ${files.length} files`);
|
|
372
|
+
console.log(`[Worker ${workerIndex}] Expecting ${tests.length} test UIDs`);
|
|
373
|
+
|
|
374
|
+
const loadedUids = new Set(allLoadedTests.map(t => t.uid));
|
|
375
|
+
const missingTests = tests.filter(uid => !loadedUids.has(uid));
|
|
376
|
+
|
|
377
|
+
if (missingTests.length > 0) {
|
|
378
|
+
console.error(`[Worker ${workerIndex}] ERROR: ${missingTests.length} assigned tests NOT FOUND in loaded files!`);
|
|
379
|
+
console.error(`[Worker ${workerIndex}] Missing UIDs:`, missingTests);
|
|
380
|
+
console.error(`[Worker ${workerIndex}] Available UIDs:`, Array.from(loadedUids).slice(0, 5), '...');
|
|
357
381
|
}
|
|
358
382
|
|
|
359
383
|
// Recursively filter tests in all suites (including nested ones)
|
|
@@ -367,13 +391,6 @@ function filterTests() {
|
|
|
367
391
|
for (const suite of mocha.suite.suites) {
|
|
368
392
|
filterSuiteTests(suite)
|
|
369
393
|
}
|
|
370
|
-
|
|
371
|
-
// Verify final test count
|
|
372
|
-
if (options.debug || options.verbose) {
|
|
373
|
-
let finalCount = 0;
|
|
374
|
-
mocha.suite.eachTest(() => finalCount++);
|
|
375
|
-
console.log(`[Worker ${workerIndex}] After filtering: ${finalCount} tests will run`);
|
|
376
|
-
}
|
|
377
394
|
}
|
|
378
395
|
|
|
379
396
|
function initializeListeners() {
|
package/lib/workers.js
CHANGED
|
@@ -542,8 +542,22 @@ class Workers extends EventEmitter {
|
|
|
542
542
|
if (this.isPoolMode) {
|
|
543
543
|
this.activeWorkers.set(worker, { available: true, workerIndex: null })
|
|
544
544
|
}
|
|
545
|
+
|
|
546
|
+
// Track last activity time to detect hanging workers
|
|
547
|
+
let lastActivity = Date.now()
|
|
548
|
+
const workerTimeout = 300000 // 5 minutes
|
|
549
|
+
|
|
550
|
+
const timeoutChecker = setInterval(() => {
|
|
551
|
+
const elapsed = Date.now() - lastActivity
|
|
552
|
+
if (elapsed > workerTimeout) {
|
|
553
|
+
console.error(`[Main] Worker appears to be hanging (no activity for ${Math.floor(elapsed/1000)}s). Terminating...`)
|
|
554
|
+
clearInterval(timeoutChecker)
|
|
555
|
+
worker.terminate()
|
|
556
|
+
}
|
|
557
|
+
}, 30000) // Check every 30 seconds
|
|
545
558
|
|
|
546
559
|
worker.on('message', message => {
|
|
560
|
+
lastActivity = Date.now() // Update activity timestamp
|
|
547
561
|
output.process(message.workerIndex)
|
|
548
562
|
|
|
549
563
|
// Handle test requests for pool mode
|
|
@@ -660,11 +674,17 @@ class Workers extends EventEmitter {
|
|
|
660
674
|
})
|
|
661
675
|
|
|
662
676
|
worker.on('error', err => {
|
|
677
|
+
console.error(`[Main] Worker error:`, err.message || err)
|
|
663
678
|
this.errors.push(err)
|
|
664
679
|
})
|
|
665
680
|
|
|
666
|
-
worker.on('exit', () => {
|
|
681
|
+
worker.on('exit', (code) => {
|
|
682
|
+
clearInterval(timeoutChecker)
|
|
667
683
|
this.closedWorkers += 1
|
|
684
|
+
|
|
685
|
+
if (code !== 0) {
|
|
686
|
+
console.error(`[Main] Worker exited with code ${code}`)
|
|
687
|
+
}
|
|
668
688
|
|
|
669
689
|
if (this.isPoolMode) {
|
|
670
690
|
// Pool mode: finish when all workers have exited and no more tests
|