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