X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=tests%2Fpools%2Fthread%2Ffixed.test.js;h=84871e738d3d3184677b1110ad0078671f0e35a5;hb=660689f017615c233020793967822c31975e5aa0;hp=4c90207cb9ffb61c7a764688a38536b22b5cb65e;hpb=70e79e39c8c80f90dc8875cc74f2d5c4c077cf15;p=poolifier.git diff --git a/tests/pools/thread/fixed.test.js b/tests/pools/thread/fixed.test.js index 4c90207c..84871e73 100644 --- a/tests/pools/thread/fixed.test.js +++ b/tests/pools/thread/fixed.test.js @@ -1,10 +1,12 @@ const { expect } = require('expect') const { FixedThreadPool, PoolEvents } = require('../../../lib') -const { WorkerFunctions } = require('../../test-types') -const TestUtils = require('../../test-utils') +const { TaskFunctions } = require('../../test-types') +const { waitPoolEvents, waitWorkerEvents } = require('../../test-utils') +const { DEFAULT_TASK_NAME } = require('../../../lib/utils') describe('Fixed thread pool test suite', () => { const numberOfThreads = 6 + const tasksConcurrency = 2 const pool = new FixedThreadPool( numberOfThreads, './tests/worker-files/thread/testWorker.js', @@ -18,7 +20,7 @@ describe('Fixed thread pool test suite', () => { { enableTasksQueue: true, tasksQueueOptions: { - concurrency: 2 + concurrency: tasksConcurrency }, errorHandler: e => console.error(e) } @@ -26,7 +28,7 @@ describe('Fixed thread pool test suite', () => { const emptyPool = new FixedThreadPool( numberOfThreads, './tests/worker-files/thread/emptyWorker.js', - { exitHandler: () => console.log('empty pool worker exited') } + { exitHandler: () => console.info('empty pool worker exited') } ) const echoPool = new FixedThreadPool( numberOfThreads, @@ -63,26 +65,43 @@ describe('Fixed thread pool test suite', () => { it('Verify that the function is executed in a worker thread', async () => { let result = await pool.execute({ - function: WorkerFunctions.fibonacci + function: TaskFunctions.fibonacci }) - expect(result).toBe(121393) + expect(result).toBe(75025) result = await pool.execute({ - function: WorkerFunctions.factorial + function: TaskFunctions.factorial }) expect(result).toBe(9.33262154439441e157) }) it('Verify that is possible to invoke the execute() method without input', async () => { const result = await pool.execute() - expect(result).toBe(false) + expect(result).toStrictEqual({ ok: 1 }) + }) + + it("Verify that 'ready' event is emitted", async () => { + const pool = new FixedThreadPool( + numberOfThreads, + './tests/worker-files/thread/testWorker.js', + { + errorHandler: e => console.error(e) + } + ) + let poolReady = 0 + pool.emitter.on(PoolEvents.ready, () => ++poolReady) + await waitPoolEvents(pool, PoolEvents.ready, 1) + expect(poolReady).toBe(1) + await pool.destroy() }) it("Verify that 'busy' event is emitted", async () => { + const promises = new Set() let poolBusy = 0 pool.emitter.on(PoolEvents.busy, () => ++poolBusy) for (let i = 0; i < numberOfThreads * 2; i++) { - pool.execute() + promises.add(pool.execute()) } + await Promise.all(promises) // The `busy` event is triggered when the number of submitted tasks at once reach the number of fixed pool workers. // So in total numberOfThreads + 1 times for a loop submitting up to numberOfThreads * 2 tasks to the fixed pool. expect(poolBusy).toBe(numberOfThreads + 1) @@ -90,32 +109,61 @@ describe('Fixed thread pool test suite', () => { it('Verify that tasks queuing is working', async () => { const promises = new Set() - const maxMultiplier = 2 + const maxMultiplier = 3 // Must be greater than tasksConcurrency for (let i = 0; i < numberOfThreads * maxMultiplier; i++) { promises.add(queuePool.execute()) } expect(promises.size).toBe(numberOfThreads * maxMultiplier) for (const workerNode of queuePool.workerNodes) { - expect(workerNode.tasksUsage.running).toBeLessThanOrEqual( + expect(workerNode.usage.tasks.executing).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executing).toBeLessThanOrEqual( queuePool.opts.tasksQueueOptions.concurrency ) - expect(workerNode.tasksUsage.ran).toBe(0) - expect(workerNode.tasksQueue.size).toBeGreaterThan(0) + expect(workerNode.usage.tasks.executed).toBe(0) + expect(workerNode.usage.tasks.queued).toBe( + maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency + ) + expect(workerNode.usage.tasks.maxQueued).toBe( + maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency + ) + expect(workerNode.usage.tasks.stolen).toBe(0) } - expect(queuePool.info.runningTasks).toBe(numberOfThreads) + expect(queuePool.info.executedTasks).toBe(0) + expect(queuePool.info.executingTasks).toBe( + numberOfThreads * queuePool.opts.tasksQueueOptions.concurrency + ) expect(queuePool.info.queuedTasks).toBe( - numberOfThreads * maxMultiplier - numberOfThreads + numberOfThreads * + (maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency) ) expect(queuePool.info.maxQueuedTasks).toBe( - numberOfThreads * maxMultiplier - numberOfThreads + numberOfThreads * + (maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency) ) + expect(queuePool.info.backPressure).toBe(false) + expect(queuePool.info.stolenTasks).toBe(0) await Promise.all(promises) for (const workerNode of queuePool.workerNodes) { - expect(workerNode.tasksUsage.running).toBe(0) - expect(workerNode.tasksUsage.ran).toBeGreaterThan(0) - expect(workerNode.tasksUsage.ran).toBeLessThanOrEqual(maxMultiplier) - expect(workerNode.tasksQueue.size).toBe(0) + expect(workerNode.usage.tasks.executing).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.executing).toBeLessThanOrEqual( + numberOfThreads * maxMultiplier + ) + expect(workerNode.usage.tasks.executed).toBe(maxMultiplier) + expect(workerNode.usage.tasks.queued).toBe(0) + expect(workerNode.usage.tasks.maxQueued).toBe( + maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency + ) + expect(workerNode.usage.tasks.stolen).toBeGreaterThanOrEqual(0) + expect(workerNode.usage.tasks.stolen).toBeLessThanOrEqual( + numberOfThreads * maxMultiplier + ) } + expect(queuePool.info.executedTasks).toBe(numberOfThreads * maxMultiplier) + expect(queuePool.info.backPressure).toBe(false) + expect(queuePool.info.stolenTasks).toBeGreaterThanOrEqual(0) + expect(queuePool.info.stolenTasks).toBeLessThanOrEqual( + numberOfThreads * maxMultiplier + ) }) it('Verify that is possible to have a worker that return undefined', async () => { @@ -129,6 +177,32 @@ describe('Fixed thread pool test suite', () => { expect(result).toStrictEqual(data) }) + it('Verify that transferable objects are sent to the worker correctly', async () => { + let error + let result + try { + result = await pool.execute(undefined, undefined, [ + new ArrayBuffer(16), + new MessageChannel().port1 + ]) + } catch (e) { + error = e + } + expect(result).toStrictEqual({ ok: 1 }) + expect(error).toBeUndefined() + try { + result = await pool.execute(undefined, undefined, [ + new SharedArrayBuffer(16) + ]) + } catch (e) { + error = e + } + expect(result).toStrictEqual({ ok: 1 }) + expect(error).toStrictEqual( + new TypeError('Found invalid object in transferList') + ) + }) + it('Verify that error handling is working properly:sync', async () => { const data = { f: 10 } let taskError @@ -147,12 +221,13 @@ describe('Fixed thread pool test suite', () => { expect(typeof inError.message === 'string').toBe(true) expect(inError.message).toBe('Error Message from ThreadWorker') expect(taskError).toStrictEqual({ - error: new Error('Error Message from ThreadWorker'), - errorData: data + name: DEFAULT_TASK_NAME, + message: new Error('Error Message from ThreadWorker'), + data }) expect( errorPool.workerNodes.some( - workerNode => workerNode.tasksUsage.error === 1 + workerNode => workerNode.usage.tasks.failed === 1 ) ).toBe(true) }) @@ -175,12 +250,13 @@ describe('Fixed thread pool test suite', () => { expect(typeof inError.message === 'string').toBe(true) expect(inError.message).toBe('Error Message from ThreadWorker:async') expect(taskError).toStrictEqual({ - error: new Error('Error Message from ThreadWorker:async'), - errorData: data + name: DEFAULT_TASK_NAME, + message: new Error('Error Message from ThreadWorker:async'), + data }) expect( asyncErrorPool.workerNodes.some( - workerNode => workerNode.tasksUsage.error === 1 + workerNode => workerNode.usage.tasks.failed === 1 ) ).toBe(true) }) @@ -195,44 +271,61 @@ describe('Fixed thread pool test suite', () => { }) it('Shutdown test', async () => { - const exitPromise = TestUtils.waitWorkerExits(pool, numberOfThreads) + const exitPromise = waitWorkerEvents(pool, 'exit', numberOfThreads) + let poolDestroy = 0 + pool.emitter.on(PoolEvents.destroy, () => ++poolDestroy) await pool.destroy() const numberOfExitEvents = await exitPromise + expect(pool.started).toBe(false) + expect(pool.workerNodes.length).toBe(0) expect(numberOfExitEvents).toBe(numberOfThreads) + expect(poolDestroy).toBe(1) }) it('Verify that thread pool options are checked', async () => { - const workerFilePath = './tests/worker-files/cluster/testWorker.js' - let pool1 = new FixedThreadPool(numberOfThreads, workerFilePath) - expect(pool1.opts.workerOptions).toBeUndefined() - await pool1.destroy() - pool1 = new FixedThreadPool(numberOfThreads, workerFilePath, { + const workerFilePath = './tests/worker-files/thread/testWorker.js' + let pool = new FixedThreadPool(numberOfThreads, workerFilePath) + expect(pool.opts.workerOptions).toBeUndefined() + await pool.destroy() + pool = new FixedThreadPool(numberOfThreads, workerFilePath, { workerOptions: { env: { TEST: 'test' }, name: 'test' } }) - expect(pool1.opts.workerOptions).toStrictEqual({ + expect(pool.opts.workerOptions).toStrictEqual({ env: { TEST: 'test' }, name: 'test' }) - await pool1.destroy() + await pool.destroy() }) it('Should work even without opts in input', async () => { - const pool1 = new FixedThreadPool( - numberOfThreads, - './tests/worker-files/thread/testWorker.js' - ) - const res = await pool1.execute() - expect(res).toBe(false) + const workerFilePath = './tests/worker-files/thread/testWorker.js' + const pool = new FixedThreadPool(numberOfThreads, workerFilePath) + const res = await pool.execute() + expect(res).toStrictEqual({ ok: 1 }) // We need to clean up the resources after our test - await pool1.destroy() + await pool.destroy() + }) + + it('Verify destroyWorkerNode()', async () => { + const workerFilePath = './tests/worker-files/thread/testWorker.js' + const pool = new FixedThreadPool(numberOfThreads, workerFilePath) + const workerNodeKey = 0 + let exitEvent = 0 + pool.workerNodes[workerNodeKey].worker.on('exit', () => { + ++exitEvent + }) + await expect(pool.destroyWorkerNode(workerNodeKey)).resolves.toBeUndefined() + expect(exitEvent).toBe(1) + expect(pool.workerNodes.length).toBe(numberOfThreads - 1) + await pool.destroy() }) it('Verify that a pool with zero worker fails', async () => { expect( () => new FixedThreadPool(0, './tests/worker-files/thread/testWorker.js') - ).toThrowError('Cannot instantiate a fixed pool with no worker') + ).toThrowError('Cannot instantiate a fixed pool with zero worker') }) })