24c7debd3c86deda03f4e312682d60546ed07c61
[poolifier.git] / tests / pools / abstract / abstract-pool.test.js
1 const { expect } = require('expect')
2 const {
3 FixedClusterPool,
4 FixedThreadPool,
5 WorkerChoiceStrategies
6 } = require('../../../lib/index')
7
8 const numberOfWorkers = 1
9
10 const workerNotFoundInTasksUsageMapError = new Error(
11 'Worker could not be found in worker tasks usage map'
12 )
13
14 class StubPoolWithWorkerTasksUsageMapClear extends FixedThreadPool {
15 removeAllWorker () {
16 this.workersTasksUsage.clear()
17 }
18 }
19
20 class StubPoolWithIsMainMethod extends FixedThreadPool {
21 isMain () {
22 return false
23 }
24 }
25
26 describe('Abstract pool test suite', () => {
27 it('Simulate pool creation from a non main thread/process', () => {
28 expect(
29 () =>
30 new StubPoolWithIsMainMethod(
31 numberOfWorkers,
32 './tests/worker-files/thread/testWorker.js',
33 {
34 errorHandler: e => console.error(e)
35 }
36 )
37 ).toThrowError(new Error('Cannot start a pool from a worker!'))
38 })
39
40 it('Verify that filePath is checked', () => {
41 const expectedError = new Error(
42 'Please specify a file with a worker implementation'
43 )
44 expect(() => new FixedThreadPool(numberOfWorkers)).toThrowError(
45 expectedError
46 )
47 expect(() => new FixedThreadPool(numberOfWorkers, '')).toThrowError(
48 expectedError
49 )
50 })
51
52 it('Verify that numberOfWorkers is checked', () => {
53 expect(() => new FixedThreadPool()).toThrowError(
54 new Error(
55 'Cannot instantiate a pool without specifying the number of workers'
56 )
57 )
58 })
59
60 it('Verify that a negative number of workers is checked', () => {
61 expect(
62 () =>
63 new FixedClusterPool(-1, './tests/worker-files/cluster/testWorker.js')
64 ).toThrowError(
65 new Error('Cannot instantiate a pool with a negative number of workers')
66 )
67 })
68
69 it('Verify that a non integer number of workers is checked', () => {
70 expect(
71 () =>
72 new FixedThreadPool(0.25, './tests/worker-files/thread/testWorker.js')
73 ).toThrowError(
74 new Error(
75 'Cannot instantiate a pool with a non integer number of workers'
76 )
77 )
78 })
79
80 it('Verify that pool options are checked', () => {
81 let pool = new FixedThreadPool(
82 numberOfWorkers,
83 './tests/worker-files/thread/testWorker.js'
84 )
85 expect(pool.opts.enableEvents).toBe(true)
86 expect(pool.emitter).toBeDefined()
87 expect(pool.opts.workerChoiceStrategy).toBe(
88 WorkerChoiceStrategies.ROUND_ROBIN
89 )
90 expect(pool.opts.messageHandler).toBeUndefined()
91 expect(pool.opts.errorHandler).toBeUndefined()
92 expect(pool.opts.onlineHandler).toBeUndefined()
93 expect(pool.opts.exitHandler).toBeUndefined()
94 pool.destroy()
95 const testHandler = () => console.log('test handler executed')
96 pool = new FixedThreadPool(
97 numberOfWorkers,
98 './tests/worker-files/thread/testWorker.js',
99 {
100 workerChoiceStrategy: WorkerChoiceStrategies.LESS_RECENTLY_USED,
101 enableEvents: false,
102 messageHandler: testHandler,
103 errorHandler: testHandler,
104 onlineHandler: testHandler,
105 exitHandler: testHandler
106 }
107 )
108 expect(pool.opts.enableEvents).toBe(false)
109 expect(pool.emitter).toBeUndefined()
110 expect(pool.opts.workerChoiceStrategy).toBe(
111 WorkerChoiceStrategies.LESS_RECENTLY_USED
112 )
113 expect(pool.opts.messageHandler).toStrictEqual(testHandler)
114 expect(pool.opts.errorHandler).toStrictEqual(testHandler)
115 expect(pool.opts.onlineHandler).toStrictEqual(testHandler)
116 expect(pool.opts.exitHandler).toStrictEqual(testHandler)
117 pool.destroy()
118 })
119
120 it('Simulate worker not found during increaseWorkerRunningTasks', () => {
121 const pool = new StubPoolWithWorkerTasksUsageMapClear(
122 numberOfWorkers,
123 './tests/worker-files/cluster/testWorker.js'
124 )
125 // Simulate worker not found.
126 pool.removeAllWorker()
127 expect(() => pool.increaseWorkerRunningTasks()).toThrowError(
128 workerNotFoundInTasksUsageMapError
129 )
130 pool.destroy()
131 })
132
133 it('Simulate worker not found during decreaseWorkerRunningTasks', () => {
134 const pool = new StubPoolWithWorkerTasksUsageMapClear(
135 numberOfWorkers,
136 './tests/worker-files/cluster/testWorker.js',
137 {
138 errorHandler: e => console.error(e)
139 }
140 )
141 // Simulate worker not found.
142 pool.removeAllWorker()
143 expect(() => pool.decreaseWorkerRunningTasks()).toThrowError(
144 workerNotFoundInTasksUsageMapError
145 )
146 pool.destroy()
147 })
148
149 it('Simulate worker not found during stepWorkerRunTasks', () => {
150 const pool = new StubPoolWithWorkerTasksUsageMapClear(
151 numberOfWorkers,
152 './tests/worker-files/cluster/testWorker.js',
153 {
154 errorHandler: e => console.error(e)
155 }
156 )
157 // Simulate worker not found.
158 pool.removeAllWorker()
159 expect(() => pool.stepWorkerRunTasks()).toThrowError(
160 workerNotFoundInTasksUsageMapError
161 )
162 pool.destroy()
163 })
164
165 it('Simulate worker not found during updateWorkerTasksRunTime', () => {
166 const pool = new StubPoolWithWorkerTasksUsageMapClear(
167 numberOfWorkers,
168 './tests/worker-files/cluster/testWorker.js',
169 {
170 errorHandler: e => console.error(e)
171 }
172 )
173 // Simulate worker not found.
174 pool.removeAllWorker()
175 expect(() => pool.updateWorkerTasksRunTime()).toThrowError(
176 workerNotFoundInTasksUsageMapError
177 )
178 pool.destroy()
179 })
180
181 it('Verify that worker pool tasks usage are initialized', () => {
182 const pool = new FixedClusterPool(
183 numberOfWorkers,
184 './tests/worker-files/cluster/testWorker.js'
185 )
186 for (const tasksUsage of pool.workersTasksUsage.values()) {
187 expect(tasksUsage).toBeDefined()
188 expect(tasksUsage.run).toBe(0)
189 expect(tasksUsage.running).toBe(0)
190 expect(tasksUsage.runTime).toBe(0)
191 expect(tasksUsage.avgRunTime).toBe(0)
192 }
193 pool.destroy()
194 })
195
196 it('Verify that worker pool tasks usage are computed', async () => {
197 const pool = new FixedClusterPool(
198 numberOfWorkers,
199 './tests/worker-files/cluster/testWorker.js'
200 )
201 const promises = []
202 for (let i = 0; i < numberOfWorkers * 2; i++) {
203 promises.push(pool.execute({ test: 'test' }))
204 }
205 for (const tasksUsage of pool.workersTasksUsage.values()) {
206 expect(tasksUsage).toBeDefined()
207 expect(tasksUsage.run).toBe(0)
208 expect(tasksUsage.running).toBe(numberOfWorkers * 2)
209 expect(tasksUsage.runTime).toBe(0)
210 expect(tasksUsage.avgRunTime).toBe(0)
211 }
212 await Promise.all(promises)
213 for (const tasksUsage of pool.workersTasksUsage.values()) {
214 expect(tasksUsage).toBeDefined()
215 expect(tasksUsage.run).toBe(numberOfWorkers * 2)
216 expect(tasksUsage.running).toBe(0)
217 expect(tasksUsage.runTime).toBeGreaterThanOrEqual(0)
218 expect(tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
219 }
220 pool.destroy()
221 })
222
223 it("Verify that pool event emitter 'busy' event can register a callback", async () => {
224 const pool = new FixedThreadPool(
225 numberOfWorkers,
226 './tests/worker-files/thread/testWorker.js'
227 )
228 const promises = []
229 let poolBusy = 0
230 pool.emitter.on('busy', () => poolBusy++)
231 for (let i = 0; i < numberOfWorkers * 2; i++) {
232 promises.push(pool.execute({ test: 'test' }))
233 }
234 await Promise.all(promises)
235 // The `busy` event is triggered when the number of submitted tasks at once reach the number of fixed pool workers.
236 // So in total numberOfWorkers + 1 times for a loop submitting up to numberOfWorkers * 2 tasks to the fixed pool.
237 expect(poolBusy).toBe(numberOfWorkers + 1)
238 pool.destroy()
239 })
240 })