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