build: switch from prettier to rome as code formatter
[poolifier.git] / tests / pools / thread / fixed.test.js
CommitLineData
a61a0724 1const { expect } = require('expect')
cdace0e5 2const { FixedThreadPool, PoolEvents } = require('../../../lib')
dbca3be9 3const { TaskFunctions } = require('../../test-types')
1dcbfefc 4const { waitPoolEvents, waitWorkerEvents } = require('../../test-utils')
506c2a14 5
079de991 6describe('Fixed thread pool test suite', () => {
e1ffb94f 7 const numberOfThreads = 6
4e377863 8 const tasksConcurrency = 2
e1ffb94f
JB
9 const pool = new FixedThreadPool(
10 numberOfThreads,
11 './tests/worker-files/thread/testWorker.js',
12 {
8ebe6c30 13 errorHandler: (e) => console.error(e)
e1ffb94f
JB
14 }
15 )
594bfb84
JB
16 const queuePool = new FixedThreadPool(
17 numberOfThreads,
18 './tests/worker-files/thread/testWorker.js',
19 {
20 enableTasksQueue: true,
21 tasksQueueOptions: {
4e377863 22 concurrency: tasksConcurrency
594bfb84 23 },
8ebe6c30 24 errorHandler: (e) => console.error(e)
594bfb84
JB
25 }
26 )
e1ffb94f
JB
27 const emptyPool = new FixedThreadPool(
28 numberOfThreads,
29 './tests/worker-files/thread/emptyWorker.js',
73bfd59d 30 { exitHandler: () => console.info('empty pool worker exited') }
e1ffb94f
JB
31 )
32 const echoPool = new FixedThreadPool(
33 numberOfThreads,
34 './tests/worker-files/thread/echoWorker.js'
35 )
36 const errorPool = new FixedThreadPool(
37 numberOfThreads,
38 './tests/worker-files/thread/errorWorker.js',
39 {
8ebe6c30 40 errorHandler: (e) => console.error(e)
e1ffb94f
JB
41 }
42 )
43 const asyncErrorPool = new FixedThreadPool(
44 numberOfThreads,
45 './tests/worker-files/thread/asyncErrorWorker.js',
46 {
8ebe6c30 47 errorHandler: (e) => console.error(e)
e1ffb94f
JB
48 }
49 )
50 const asyncPool = new FixedThreadPool(
51 numberOfThreads,
52 './tests/worker-files/thread/asyncWorker.js'
53 )
54
0e2503fc
JB
55 after('Destroy all pools', async () => {
56 // We need to clean up the resources after our test
57 await echoPool.destroy()
58 await asyncPool.destroy()
59 await errorPool.destroy()
7c0ba920 60 await asyncErrorPool.destroy()
0e2503fc 61 await emptyPool.destroy()
594bfb84 62 await queuePool.destroy()
0e2503fc
JB
63 })
64
506c2a14 65 it('Verify that the function is executed in a worker thread', async () => {
6db75ad9 66 let result = await pool.execute({
dbca3be9 67 function: TaskFunctions.fibonacci
6db75ad9 68 })
024daf59 69 expect(result).toBe(75025)
6db75ad9 70 result = await pool.execute({
dbca3be9 71 function: TaskFunctions.factorial
6db75ad9 72 })
70a4f5ea 73 expect(result).toBe(9.33262154439441e157)
506c2a14 74 })
75
318d4156 76 it('Verify that is possible to invoke the execute() method without input', async () => {
106744f7 77 const result = await pool.execute()
30b963d4 78 expect(result).toStrictEqual({ ok: 1 })
106744f7 79 })
80
1dcbfefc 81 it("Verify that 'ready' event is emitted", async () => {
079de991
JB
82 const pool1 = new FixedThreadPool(
83 numberOfThreads,
84 './tests/worker-files/thread/testWorker.js',
85 {
8ebe6c30 86 errorHandler: (e) => console.error(e)
079de991
JB
87 }
88 )
89 let poolReady = 0
66293e7d 90 pool1.emitter.on(PoolEvents.ready, () => ++poolReady)
1dcbfefc 91 await waitPoolEvents(pool1, 'ready', 1)
216541b6
JB
92 expect(poolReady).toBe(1)
93 })
94
1dcbfefc 95 it("Verify that 'busy' event is emitted", () => {
7c0ba920 96 let poolBusy = 0
aee46736 97 pool.emitter.on(PoolEvents.busy, () => ++poolBusy)
7c0ba920 98 for (let i = 0; i < numberOfThreads * 2; i++) {
8cbb82eb 99 pool.execute()
7c0ba920 100 }
14916bf9
JB
101 // The `busy` event is triggered when the number of submitted tasks at once reach the number of fixed pool workers.
102 // So in total numberOfThreads + 1 times for a loop submitting up to numberOfThreads * 2 tasks to the fixed pool.
103 expect(poolBusy).toBe(numberOfThreads + 1)
7c0ba920
JB
104 })
105
594bfb84 106 it('Verify that tasks queuing is working', async () => {
d3d4b67d 107 const promises = new Set()
4e377863 108 const maxMultiplier = 3 // Must be greater than tasksConcurrency
594bfb84 109 for (let i = 0; i < numberOfThreads * maxMultiplier; i++) {
d3d4b67d 110 promises.add(queuePool.execute())
594bfb84 111 }
d3d4b67d 112 expect(promises.size).toBe(numberOfThreads * maxMultiplier)
594bfb84 113 for (const workerNode of queuePool.workerNodes) {
f59e1027 114 expect(workerNode.usage.tasks.executing).toBeLessThanOrEqual(
594bfb84
JB
115 queuePool.opts.tasksQueueOptions.concurrency
116 )
f59e1027 117 expect(workerNode.usage.tasks.executed).toBe(0)
4e377863
JB
118 expect(workerNode.usage.tasks.queued).toBe(
119 maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency
120 )
121 expect(workerNode.usage.tasks.maxQueued).toBe(
122 maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency
123 )
594bfb84 124 }
4e377863
JB
125 expect(queuePool.info.executingTasks).toBe(
126 numberOfThreads * queuePool.opts.tasksQueueOptions.concurrency
127 )
6b27d407 128 expect(queuePool.info.queuedTasks).toBe(
4e377863
JB
129 numberOfThreads *
130 (maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency)
6b27d407
JB
131 )
132 expect(queuePool.info.maxQueuedTasks).toBe(
4e377863
JB
133 numberOfThreads *
134 (maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency)
d3d4b67d 135 )
594bfb84
JB
136 await Promise.all(promises)
137 for (const workerNode of queuePool.workerNodes) {
f59e1027 138 expect(workerNode.usage.tasks.executing).toBe(0)
4e377863 139 expect(workerNode.usage.tasks.executed).toBe(maxMultiplier)
f59e1027 140 expect(workerNode.usage.tasks.queued).toBe(0)
4e377863
JB
141 expect(workerNode.usage.tasks.maxQueued).toBe(
142 maxMultiplier - queuePool.opts.tasksQueueOptions.concurrency
143 )
594bfb84
JB
144 }
145 })
146
106744f7 147 it('Verify that is possible to have a worker that return undefined', async () => {
148 const result = await emptyPool.execute()
6db75ad9 149 expect(result).toBeUndefined()
106744f7 150 })
151
152 it('Verify that data are sent to the worker correctly', async () => {
153 const data = { f: 10 }
154 const result = await echoPool.execute(data)
e1ffb94f 155 expect(result).toStrictEqual(data)
106744f7 156 })
157
9ea61037 158 it('Verify that transferable objects are sent to the worker correctly', async () => {
9ea61037
JB
159 let error
160 let result
161 try {
f05314ff
JB
162 result = await pool.execute(undefined, undefined, [
163 new ArrayBuffer(16),
164 new MessageChannel().port1
165 ])
9ea61037
JB
166 } catch (e) {
167 error = e
168 }
169 expect(result).toStrictEqual({ ok: 1 })
170 expect(error).toBeUndefined()
f05314ff
JB
171 try {
172 result = await pool.execute(undefined, undefined, [
173 new SharedArrayBuffer(16)
174 ])
175 } catch (e) {
176 error = e
177 }
178 expect(result).toStrictEqual({ ok: 1 })
179 expect(error).toStrictEqual(
180 new TypeError('Found invalid object in transferList')
181 )
9ea61037
JB
182 })
183
7c0ba920 184 it('Verify that error handling is working properly:sync', async () => {
106744f7 185 const data = { f: 10 }
d46660cd 186 let taskError
8ebe6c30 187 errorPool.emitter.on(PoolEvents.taskError, (e) => {
d46660cd
JB
188 taskError = e
189 })
106744f7 190 let inError
191 try {
192 await errorPool.execute(data)
193 } catch (e) {
194 inError = e
195 }
7c0ba920
JB
196 expect(inError).toBeDefined()
197 expect(inError).toBeInstanceOf(Error)
198 expect(inError.message).toBeDefined()
8620fb25 199 expect(typeof inError.message === 'string').toBe(true)
985d0e79
JB
200 expect(inError.message).toBe('Error Message from ThreadWorker')
201 expect(taskError).toStrictEqual({
ff128cc9 202 name: 'default',
985d0e79
JB
203 message: new Error('Error Message from ThreadWorker'),
204 data
205 })
18482cec
JB
206 expect(
207 errorPool.workerNodes.some(
8ebe6c30 208 (workerNode) => workerNode.usage.tasks.failed === 1
18482cec
JB
209 )
210 ).toBe(true)
7c0ba920
JB
211 })
212
213 it('Verify that error handling is working properly:async', async () => {
214 const data = { f: 10 }
4f0b85b3 215 let taskError
8ebe6c30 216 asyncErrorPool.emitter.on(PoolEvents.taskError, (e) => {
4f0b85b3
JB
217 taskError = e
218 })
7c0ba920
JB
219 let inError
220 try {
221 await asyncErrorPool.execute(data)
222 } catch (e) {
223 inError = e
224 }
225 expect(inError).toBeDefined()
226 expect(inError).toBeInstanceOf(Error)
227 expect(inError.message).toBeDefined()
8620fb25 228 expect(typeof inError.message === 'string').toBe(true)
985d0e79
JB
229 expect(inError.message).toBe('Error Message from ThreadWorker:async')
230 expect(taskError).toStrictEqual({
ff128cc9 231 name: 'default',
985d0e79
JB
232 message: new Error('Error Message from ThreadWorker:async'),
233 data
234 })
18482cec
JB
235 expect(
236 asyncErrorPool.workerNodes.some(
8ebe6c30 237 (workerNode) => workerNode.usage.tasks.failed === 1
18482cec
JB
238 )
239 ).toBe(true)
106744f7 240 })
241
7784f548 242 it('Verify that async function is working properly', async () => {
243 const data = { f: 10 }
15e5141f 244 const startTime = performance.now()
7784f548 245 const result = await asyncPool.execute(data)
15e5141f 246 const usedTime = performance.now() - startTime
e1ffb94f 247 expect(result).toStrictEqual(data)
32d490eb 248 expect(usedTime).toBeGreaterThanOrEqual(2000)
7784f548 249 })
250
506c2a14 251 it('Shutdown test', async () => {
bac873bd 252 const exitPromise = waitWorkerEvents(pool, 'exit', numberOfThreads)
1f9a5a44 253 await pool.destroy()
bdacc2d2
JB
254 const numberOfExitEvents = await exitPromise
255 expect(numberOfExitEvents).toBe(numberOfThreads)
506c2a14 256 })
257
90082c8c 258 it('Verify that thread pool options are checked', async () => {
f59e1027 259 const workerFilePath = './tests/worker-files/thread/testWorker.js'
90082c8c
JB
260 let pool1 = new FixedThreadPool(numberOfThreads, workerFilePath)
261 expect(pool1.opts.workerOptions).toBeUndefined()
262 await pool1.destroy()
263 pool1 = new FixedThreadPool(numberOfThreads, workerFilePath, {
264 workerOptions: {
265 env: { TEST: 'test' },
266 name: 'test'
267 }
268 })
269 expect(pool1.opts.workerOptions).toStrictEqual({
270 env: { TEST: 'test' },
271 name: 'test'
272 })
273 await pool1.destroy()
274 })
275
506c2a14 276 it('Should work even without opts in input', async () => {
76b1e974 277 const pool1 = new FixedThreadPool(
e1ffb94f 278 numberOfThreads,
76b1e974
S
279 './tests/worker-files/thread/testWorker.js'
280 )
6db75ad9 281 const res = await pool1.execute()
30b963d4 282 expect(res).toStrictEqual({ ok: 1 })
0e2503fc
JB
283 // We need to clean up the resources after our test
284 await pool1.destroy()
506c2a14 285 })
8d3782fa
JB
286
287 it('Verify that a pool with zero worker fails', async () => {
288 expect(
289 () => new FixedThreadPool(0, './tests/worker-files/thread/testWorker.js')
2431bdb4 290 ).toThrowError('Cannot instantiate a fixed pool with zero worker')
8d3782fa 291 })
506c2a14 292})