test: improve worker choice strategies testing
[poolifier.git] / tests / pools / selection-strategies / selection-strategies.test.js
CommitLineData
a61a0724 1const { expect } = require('expect')
a35560ba
S
2const {
3 WorkerChoiceStrategies,
4 DynamicThreadPool,
2ced693a
JB
5 FixedThreadPool,
6 FixedClusterPool
cdace0e5 7} = require('../../../lib')
86bf340d 8const { CircularArray } = require('../../../lib/circular-array')
a35560ba
S
9
10describe('Selection strategies test suite', () => {
e1ffb94f
JB
11 const min = 0
12 const max = 3
13
a35560ba
S
14 it('Verify that WorkerChoiceStrategies enumeration provides string values', () => {
15 expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN')
e4543b14
JB
16 expect(WorkerChoiceStrategies.LEAST_USED).toBe('LEAST_USED')
17 expect(WorkerChoiceStrategies.LEAST_BUSY).toBe('LEAST_BUSY')
23ff945a 18 expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE')
b3432a63
JB
19 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
20 'WEIGHTED_ROUND_ROBIN'
21 )
a35560ba
S
22 })
23
e843b904 24 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
e843b904
JB
25 const pool = new DynamicThreadPool(
26 min,
27 max,
28 './tests/worker-files/thread/testWorker.js'
29 )
30 expect(pool.opts.workerChoiceStrategy).toBe(
31 WorkerChoiceStrategies.ROUND_ROBIN
32 )
33 // We need to clean up the resources after our test
34 await pool.destroy()
35 })
36
594bfb84
JB
37 it('Verify available strategies are taken at pool creation', async () => {
38 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
39 const pool = new FixedThreadPool(
40 max,
41 './tests/worker-files/thread/testWorker.js',
42 { workerChoiceStrategy }
43 )
44 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
45 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
46 workerChoiceStrategy
47 )
48 await pool.destroy()
49 }
d2f7b7a2
JB
50 })
51
594bfb84
JB
52 it('Verify available strategies can be set after pool creation', async () => {
53 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
54 const pool = new DynamicThreadPool(
55 min,
56 max,
ec82cfa1 57 './tests/worker-files/thread/testWorker.js'
594bfb84
JB
58 )
59 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
60 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
61 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
62 workerChoiceStrategy
63 )
64 await pool.destroy()
65 }
66 })
67
68 it('Verify available strategies default internals at pool creation', async () => {
69 const pool = new FixedThreadPool(
e843b904
JB
70 max,
71 './tests/worker-files/thread/testWorker.js'
72 )
594bfb84
JB
73 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
74 if (workerChoiceStrategy === WorkerChoiceStrategies.ROUND_ROBIN) {
75 expect(
76 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
77 workerChoiceStrategy
78 ).nextWorkerNodeId
79 ).toBe(0)
80 } else if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
08f3f44c
JB
81 expect(
82 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
83 workerChoiceStrategy
b0d6ed8f 84 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
85 ).toBeInstanceOf(Array)
86 expect(
87 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
88 workerChoiceStrategy
b0d6ed8f 89 ).workersVirtualTaskEndTimestamp.length
08f3f44c 90 ).toBe(0)
594bfb84
JB
91 } else if (
92 workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
93 ) {
94 expect(
95 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
96 workerChoiceStrategy
97 ).currentWorkerNodeId
98 ).toBe(0)
99 expect(
100 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
101 workerChoiceStrategy
102 ).defaultWorkerWeight
103 ).toBeGreaterThan(0)
08f3f44c
JB
104 expect(
105 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
106 workerChoiceStrategy
107 ).workerVirtualTaskRunTime
108 ).toBe(0)
594bfb84
JB
109 }
110 }
e843b904
JB
111 await pool.destroy()
112 })
113
10fcfaf4 114 it('Verify ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
594bfb84 115 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
10fcfaf4
JB
116 let pool = new FixedThreadPool(
117 max,
d710242d 118 './tests/worker-files/thread/testWorker.js',
594bfb84 119 { workerChoiceStrategy }
10fcfaf4 120 )
10fcfaf4 121 expect(
86bf340d
JB
122 pool.workerChoiceStrategyContext.getRequiredStatistics()
123 ).toStrictEqual({
124 runTime: false,
125 avgRunTime: false,
126 medRunTime: false,
127 waitTime: false,
128 avgWaitTime: false,
129 medWaitTime: false
130 })
fd7ebd49 131 await pool.destroy()
10fcfaf4
JB
132 pool = new DynamicThreadPool(
133 min,
134 max,
d710242d 135 './tests/worker-files/thread/testWorker.js',
594bfb84 136 { workerChoiceStrategy }
10fcfaf4 137 )
10fcfaf4 138 expect(
86bf340d
JB
139 pool.workerChoiceStrategyContext.getRequiredStatistics()
140 ).toStrictEqual({
141 runTime: false,
142 avgRunTime: false,
143 medRunTime: false,
144 waitTime: false,
145 avgWaitTime: false,
146 medWaitTime: false
147 })
10fcfaf4
JB
148 // We need to clean up the resources after our test
149 await pool.destroy()
150 })
151
bdaf31cd 152 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
bdaf31cd
JB
153 const pool = new FixedThreadPool(
154 max,
155 './tests/worker-files/thread/testWorker.js',
156 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
157 )
bdaf31cd 158 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
ee9f5295 159 const promises = new Set()
a20f0ba5
JB
160 const maxMultiplier = 2
161 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 162 promises.add(pool.execute())
e211bc18
JB
163 }
164 await Promise.all(promises)
165 for (const workerNode of pool.workerNodes) {
166 expect(workerNode.tasksUsage).toStrictEqual({
167 run: expect.any(Number),
168 running: 0,
169 runTime: 0,
170 runTimeHistory: expect.any(CircularArray),
171 avgRunTime: 0,
172 medRunTime: 0,
173 waitTime: 0,
174 waitTimeHistory: expect.any(CircularArray),
175 avgWaitTime: 0,
176 medWaitTime: 0,
177 error: 0
178 })
179 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 180 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
bdaf31cd 181 }
bdaf31cd
JB
182 // We need to clean up the resources after our test
183 await pool.destroy()
184 })
185
186 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
bdaf31cd
JB
187 const pool = new DynamicThreadPool(
188 min,
189 max,
190 './tests/worker-files/thread/testWorker.js',
191 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
192 )
bdaf31cd 193 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
ee9f5295 194 const promises = new Set()
a20f0ba5
JB
195 const maxMultiplier = 2
196 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 197 promises.add(pool.execute())
e211bc18
JB
198 }
199 await Promise.all(promises)
200 for (const workerNode of pool.workerNodes) {
201 expect(workerNode.tasksUsage).toStrictEqual({
202 run: expect.any(Number),
203 running: 0,
204 runTime: 0,
205 runTimeHistory: expect.any(CircularArray),
206 avgRunTime: 0,
207 medRunTime: 0,
208 waitTime: 0,
209 waitTimeHistory: expect.any(CircularArray),
210 avgWaitTime: 0,
211 medWaitTime: 0,
212 error: 0
213 })
214 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 215 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
bdaf31cd 216 }
bdaf31cd
JB
217 // We need to clean up the resources after our test
218 await pool.destroy()
219 })
220
2ced693a 221 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
594bfb84 222 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
2ced693a
JB
223 let pool = new FixedClusterPool(
224 max,
594bfb84
JB
225 './tests/worker-files/cluster/testWorker.js',
226 { workerChoiceStrategy }
2ced693a
JB
227 )
228 let results = new Set()
229 for (let i = 0; i < max; i++) {
20dcad1a 230 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
2ced693a
JB
231 }
232 expect(results.size).toBe(max)
233 await pool.destroy()
594bfb84
JB
234 pool = new FixedThreadPool(
235 max,
236 './tests/worker-files/thread/testWorker.js',
237 { workerChoiceStrategy }
238 )
2ced693a
JB
239 results = new Set()
240 for (let i = 0; i < max; i++) {
20dcad1a 241 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
2ced693a
JB
242 }
243 expect(results.size).toBe(max)
244 await pool.destroy()
245 })
246
a6f7f1b4 247 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
594bfb84 248 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
a6f7f1b4
JB
249 let pool = new FixedThreadPool(
250 max,
251 './tests/worker-files/thread/testWorker.js',
252 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
253 )
38f6e859 254 expect(
95c83464 255 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 256 workerChoiceStrategy
f06e48d8 257 ).nextWorkerNodeId
b529c323 258 ).toBeDefined()
594bfb84 259 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 260 expect(
95c83464 261 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 262 pool.workerChoiceStrategyContext.workerChoiceStrategy
f06e48d8 263 ).nextWorkerNodeId
a6f7f1b4
JB
264 ).toBe(0)
265 await pool.destroy()
266 pool = new DynamicThreadPool(
267 min,
268 max,
269 './tests/worker-files/thread/testWorker.js',
270 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
271 )
38f6e859 272 expect(
95c83464 273 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 274 workerChoiceStrategy
f06e48d8 275 ).nextWorkerNodeId
b529c323 276 ).toBeDefined()
594bfb84 277 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 278 expect(
95c83464 279 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 280 pool.workerChoiceStrategyContext.workerChoiceStrategy
f06e48d8 281 ).nextWorkerNodeId
a6f7f1b4
JB
282 ).toBe(0)
283 // We need to clean up the resources after our test
284 await pool.destroy()
285 })
286
e4543b14
JB
287 it('Verify LEAST_USED strategy default tasks usage statistics requirements', async () => {
288 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
10fcfaf4
JB
289 let pool = new FixedThreadPool(
290 max,
d710242d 291 './tests/worker-files/thread/testWorker.js',
594bfb84 292 { workerChoiceStrategy }
10fcfaf4 293 )
10fcfaf4 294 expect(
86bf340d
JB
295 pool.workerChoiceStrategyContext.getRequiredStatistics()
296 ).toStrictEqual({
297 runTime: false,
298 avgRunTime: false,
299 medRunTime: false,
300 waitTime: false,
301 avgWaitTime: false,
302 medWaitTime: false
303 })
fd7ebd49 304 await pool.destroy()
10fcfaf4
JB
305 pool = new DynamicThreadPool(
306 min,
307 max,
d710242d 308 './tests/worker-files/thread/testWorker.js',
594bfb84 309 { workerChoiceStrategy }
10fcfaf4 310 )
10fcfaf4 311 expect(
86bf340d
JB
312 pool.workerChoiceStrategyContext.getRequiredStatistics()
313 ).toStrictEqual({
314 runTime: false,
315 avgRunTime: false,
316 medRunTime: false,
317 waitTime: false,
318 avgWaitTime: false,
319 medWaitTime: false
320 })
10fcfaf4
JB
321 // We need to clean up the resources after our test
322 await pool.destroy()
323 })
324
e4543b14 325 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
b98ec2e6
JB
326 const pool = new FixedThreadPool(
327 max,
328 './tests/worker-files/thread/testWorker.js',
e4543b14 329 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
b98ec2e6 330 )
e4543b14 331 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
ee9f5295 332 const promises = new Set()
a20f0ba5
JB
333 const maxMultiplier = 2
334 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 335 promises.add(pool.execute())
e211bc18
JB
336 }
337 await Promise.all(promises)
338 for (const workerNode of pool.workerNodes) {
339 expect(workerNode.tasksUsage).toStrictEqual({
340 run: expect.any(Number),
341 running: 0,
342 runTime: 0,
343 runTimeHistory: expect.any(CircularArray),
344 avgRunTime: 0,
345 medRunTime: 0,
346 waitTime: 0,
347 waitTimeHistory: expect.any(CircularArray),
348 avgWaitTime: 0,
349 medWaitTime: 0,
350 error: 0
351 })
352 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 353 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
a35560ba 354 }
a35560ba
S
355 // We need to clean up the resources after our test
356 await pool.destroy()
357 })
358
e4543b14 359 it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => {
ff5e76e1
JB
360 const pool = new DynamicThreadPool(
361 min,
362 max,
363 './tests/worker-files/thread/testWorker.js',
e4543b14 364 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
ff5e76e1 365 )
e4543b14 366 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
ee9f5295 367 const promises = new Set()
a20f0ba5
JB
368 const maxMultiplier = 2
369 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 370 promises.add(pool.execute())
e211bc18
JB
371 }
372 await Promise.all(promises)
373 for (const workerNode of pool.workerNodes) {
374 expect(workerNode.tasksUsage).toStrictEqual({
375 run: expect.any(Number),
376 running: 0,
377 runTime: 0,
378 runTimeHistory: expect.any(CircularArray),
379 avgRunTime: 0,
380 medRunTime: 0,
381 waitTime: 0,
382 waitTimeHistory: expect.any(CircularArray),
383 avgWaitTime: 0,
384 medWaitTime: 0,
385 error: 0
386 })
387 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 388 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
168c526f 389 }
168c526f
JB
390 // We need to clean up the resources after our test
391 await pool.destroy()
392 })
393
e4543b14
JB
394 it('Verify LEAST_BUSY strategy default tasks usage statistics requirements', async () => {
395 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
168c526f
JB
396 let pool = new FixedThreadPool(
397 max,
d710242d 398 './tests/worker-files/thread/testWorker.js',
594bfb84 399 { workerChoiceStrategy }
168c526f 400 )
168c526f 401 expect(
86bf340d
JB
402 pool.workerChoiceStrategyContext.getRequiredStatistics()
403 ).toStrictEqual({
404 runTime: true,
405 avgRunTime: false,
406 medRunTime: false,
407 waitTime: false,
408 avgWaitTime: false,
409 medWaitTime: false
410 })
168c526f
JB
411 await pool.destroy()
412 pool = new DynamicThreadPool(
413 min,
414 max,
d710242d 415 './tests/worker-files/thread/testWorker.js',
594bfb84 416 { workerChoiceStrategy }
168c526f 417 )
168c526f 418 expect(
86bf340d
JB
419 pool.workerChoiceStrategyContext.getRequiredStatistics()
420 ).toStrictEqual({
421 runTime: true,
422 avgRunTime: false,
423 medRunTime: false,
424 waitTime: false,
425 avgWaitTime: false,
426 medWaitTime: false
427 })
168c526f
JB
428 // We need to clean up the resources after our test
429 await pool.destroy()
430 })
431
e4543b14 432 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
168c526f
JB
433 const pool = new FixedThreadPool(
434 max,
435 './tests/worker-files/thread/testWorker.js',
e4543b14 436 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
168c526f 437 )
e4543b14 438 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
ee9f5295 439 const promises = new Set()
a20f0ba5
JB
440 const maxMultiplier = 2
441 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 442 promises.add(pool.execute())
e211bc18
JB
443 }
444 await Promise.all(promises)
445 for (const workerNode of pool.workerNodes) {
446 expect(workerNode.tasksUsage).toStrictEqual({
447 run: expect.any(Number),
448 running: 0,
449 runTime: expect.any(Number),
450 runTimeHistory: expect.any(CircularArray),
451 avgRunTime: 0,
452 medRunTime: 0,
453 waitTime: 0,
454 waitTimeHistory: expect.any(CircularArray),
455 avgWaitTime: 0,
456 medWaitTime: 0,
457 error: 0
458 })
459 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 460 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18 461 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
168c526f 462 }
168c526f
JB
463 // We need to clean up the resources after our test
464 await pool.destroy()
465 })
466
e4543b14 467 it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => {
168c526f
JB
468 const pool = new DynamicThreadPool(
469 min,
470 max,
471 './tests/worker-files/thread/testWorker.js',
e4543b14 472 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
168c526f 473 )
e4543b14 474 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
ee9f5295 475 const promises = new Set()
a20f0ba5
JB
476 const maxMultiplier = 2
477 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 478 promises.add(pool.execute())
e211bc18
JB
479 }
480 await Promise.all(promises)
481 for (const workerNode of pool.workerNodes) {
482 expect(workerNode.tasksUsage).toStrictEqual({
483 run: expect.any(Number),
484 running: 0,
485 runTime: expect.any(Number),
486 runTimeHistory: expect.any(CircularArray),
487 avgRunTime: 0,
488 medRunTime: 0,
489 waitTime: 0,
490 waitTimeHistory: expect.any(CircularArray),
491 avgWaitTime: 0,
492 medWaitTime: 0,
493 error: 0
494 })
495 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 496 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18 497 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
ff5e76e1 498 }
ff5e76e1
JB
499 // We need to clean up the resources after our test
500 await pool.destroy()
501 })
502
10fcfaf4 503 it('Verify FAIR_SHARE strategy default tasks usage statistics requirements', async () => {
594bfb84 504 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
10fcfaf4
JB
505 let pool = new FixedThreadPool(
506 max,
d710242d 507 './tests/worker-files/thread/testWorker.js',
594bfb84 508 { workerChoiceStrategy }
10fcfaf4 509 )
10fcfaf4 510 expect(
86bf340d
JB
511 pool.workerChoiceStrategyContext.getRequiredStatistics()
512 ).toStrictEqual({
513 runTime: true,
514 avgRunTime: true,
515 medRunTime: false,
516 waitTime: false,
517 avgWaitTime: false,
518 medWaitTime: false
519 })
fd7ebd49 520 await pool.destroy()
10fcfaf4
JB
521 pool = new DynamicThreadPool(
522 min,
523 max,
d710242d 524 './tests/worker-files/thread/testWorker.js',
594bfb84 525 { workerChoiceStrategy }
10fcfaf4 526 )
10fcfaf4 527 expect(
86bf340d
JB
528 pool.workerChoiceStrategyContext.getRequiredStatistics()
529 ).toStrictEqual({
530 runTime: true,
531 avgRunTime: true,
532 medRunTime: false,
533 waitTime: false,
534 avgWaitTime: false,
535 medWaitTime: false
536 })
10fcfaf4
JB
537 // We need to clean up the resources after our test
538 await pool.destroy()
539 })
540
23ff945a 541 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
23ff945a
JB
542 const pool = new FixedThreadPool(
543 max,
544 './tests/worker-files/thread/testWorker.js',
545 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
546 )
547 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 548 const promises = new Set()
a20f0ba5
JB
549 const maxMultiplier = 2
550 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 551 promises.add(pool.execute())
23ff945a 552 }
e211bc18 553 await Promise.all(promises)
138d29a8 554 for (const workerNode of pool.workerNodes) {
86bf340d 555 expect(workerNode.tasksUsage).toStrictEqual({
e211bc18 556 run: expect.any(Number),
86bf340d
JB
557 running: 0,
558 runTime: expect.any(Number),
559 runTimeHistory: expect.any(CircularArray),
560 avgRunTime: expect.any(Number),
561 medRunTime: 0,
562 waitTime: 0,
563 waitTimeHistory: expect.any(CircularArray),
564 avgWaitTime: 0,
565 medWaitTime: 0,
566 error: 0
567 })
e211bc18 568 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 569 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18
JB
570 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
571 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
138d29a8 572 }
97a2abc3 573 expect(
95c83464 574 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 575 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 576 ).workersVirtualTaskEndTimestamp.length
f06e48d8 577 ).toBe(pool.workerNodes.length)
23ff945a
JB
578 // We need to clean up the resources after our test
579 await pool.destroy()
580 })
581
582 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
23ff945a
JB
583 const pool = new DynamicThreadPool(
584 min,
585 max,
586 './tests/worker-files/thread/testWorker.js',
587 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
588 )
589 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 590 const promises = new Set()
f7070eee 591 const maxMultiplier = 2
804a889e 592 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 593 promises.add(pool.execute())
23ff945a 594 }
e211bc18 595 await Promise.all(promises)
138d29a8 596 for (const workerNode of pool.workerNodes) {
86bf340d 597 expect(workerNode.tasksUsage).toStrictEqual({
e211bc18 598 run: expect.any(Number),
86bf340d
JB
599 running: 0,
600 runTime: expect.any(Number),
601 runTimeHistory: expect.any(CircularArray),
602 avgRunTime: expect.any(Number),
603 medRunTime: 0,
604 waitTime: 0,
605 waitTimeHistory: expect.any(CircularArray),
606 avgWaitTime: 0,
607 medWaitTime: 0,
608 error: 0
609 })
e211bc18 610 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 611 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18
JB
612 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
613 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
138d29a8 614 }
2b4fddb8
JB
615 expect(
616 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
617 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 618 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 619 ).toBe(pool.workerNodes.length)
23ff945a
JB
620 // We need to clean up the resources after our test
621 await pool.destroy()
622 })
623
9e775f96 624 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
010d7020
JB
625 const pool = new DynamicThreadPool(
626 min,
627 max,
628 './tests/worker-files/thread/testWorker.js',
629 {
630 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
631 workerChoiceStrategyOptions: {
632 medRunTime: true
633 }
634 }
635 )
636 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 637 const promises = new Set()
010d7020
JB
638 const maxMultiplier = 2
639 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 640 promises.add(pool.execute())
010d7020 641 }
e211bc18 642 await Promise.all(promises)
010d7020 643 for (const workerNode of pool.workerNodes) {
86bf340d 644 expect(workerNode.tasksUsage).toStrictEqual({
e211bc18 645 run: expect.any(Number),
86bf340d
JB
646 running: 0,
647 runTime: expect.any(Number),
648 runTimeHistory: expect.any(CircularArray),
649 avgRunTime: 0,
650 medRunTime: expect.any(Number),
651 waitTime: 0,
652 waitTimeHistory: expect.any(CircularArray),
653 avgWaitTime: 0,
654 medWaitTime: 0,
655 error: 0
656 })
e211bc18 657 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 658 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18
JB
659 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
660 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
010d7020 661 }
2b4fddb8
JB
662 expect(
663 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
664 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 665 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 666 ).toBe(pool.workerNodes.length)
010d7020
JB
667 // We need to clean up the resources after our test
668 await pool.destroy()
669 })
670
a6f7f1b4 671 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
594bfb84 672 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
f0829c53 673 let pool = new FixedThreadPool(
caeb9817
JB
674 max,
675 './tests/worker-files/thread/testWorker.js'
676 )
677 expect(
95c83464 678 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 679 workerChoiceStrategy
b0d6ed8f 680 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
681 ).toBeInstanceOf(Array)
682 expect(
683 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
684 workerChoiceStrategy
b0d6ed8f 685 ).workersVirtualTaskEndTimestamp.length
08f3f44c 686 ).toBe(0)
2b4fddb8
JB
687 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
688 workerChoiceStrategy
b0d6ed8f 689 ).workersVirtualTaskEndTimestamp[0] = performance.now()
2b4fddb8
JB
690 expect(
691 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
692 workerChoiceStrategy
b0d6ed8f 693 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 694 ).toBe(1)
594bfb84 695 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
08f3f44c
JB
696 expect(
697 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
698 workerChoiceStrategy
b0d6ed8f 699 ).workersVirtualTaskEndTimestamp
08f3f44c 700 ).toBeInstanceOf(Array)
08f3f44c
JB
701 expect(
702 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
703 workerChoiceStrategy
b0d6ed8f 704 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 705 ).toBe(0)
f0829c53
JB
706 await pool.destroy()
707 pool = new DynamicThreadPool(
708 min,
709 max,
710 './tests/worker-files/thread/testWorker.js'
711 )
712 expect(
95c83464 713 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 714 workerChoiceStrategy
b0d6ed8f 715 ).workersVirtualTaskEndTimestamp
08f3f44c 716 ).toBeInstanceOf(Array)
2b4fddb8
JB
717 expect(
718 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
719 workerChoiceStrategy
b0d6ed8f 720 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 721 ).toBe(0)
08f3f44c
JB
722 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
723 workerChoiceStrategy
b0d6ed8f 724 ).workersVirtualTaskEndTimestamp[0] = performance.now()
08f3f44c
JB
725 expect(
726 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
727 workerChoiceStrategy
b0d6ed8f 728 ).workersVirtualTaskEndTimestamp.length
08f3f44c 729 ).toBe(1)
594bfb84 730 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
08f3f44c
JB
731 expect(
732 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
733 workerChoiceStrategy
b0d6ed8f 734 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
735 ).toBeInstanceOf(Array)
736 expect(
737 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
738 workerChoiceStrategy
b0d6ed8f 739 ).workersVirtualTaskEndTimestamp.length
08f3f44c 740 ).toBe(0)
caeb9817
JB
741 // We need to clean up the resources after our test
742 await pool.destroy()
743 })
744
10fcfaf4 745 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks usage statistics requirements', async () => {
594bfb84 746 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
10fcfaf4
JB
747 let pool = new FixedThreadPool(
748 max,
d710242d 749 './tests/worker-files/thread/testWorker.js',
594bfb84 750 { workerChoiceStrategy }
10fcfaf4 751 )
10fcfaf4 752 expect(
86bf340d
JB
753 pool.workerChoiceStrategyContext.getRequiredStatistics()
754 ).toStrictEqual({
755 runTime: true,
756 avgRunTime: true,
757 medRunTime: false,
758 waitTime: false,
759 avgWaitTime: false,
760 medWaitTime: false
761 })
fd7ebd49 762 await pool.destroy()
10fcfaf4
JB
763 pool = new DynamicThreadPool(
764 min,
765 max,
d710242d 766 './tests/worker-files/thread/testWorker.js',
594bfb84 767 { workerChoiceStrategy }
10fcfaf4 768 )
10fcfaf4 769 expect(
86bf340d
JB
770 pool.workerChoiceStrategyContext.getRequiredStatistics()
771 ).toStrictEqual({
772 runTime: true,
773 avgRunTime: true,
774 medRunTime: false,
775 waitTime: false,
776 avgWaitTime: false,
777 medWaitTime: false
778 })
10fcfaf4
JB
779 // We need to clean up the resources after our test
780 await pool.destroy()
781 })
782
b3432a63 783 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
b3432a63
JB
784 const pool = new FixedThreadPool(
785 max,
786 './tests/worker-files/thread/testWorker.js',
787 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
788 )
789 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 790 const promises = new Set()
a20f0ba5
JB
791 const maxMultiplier = 2
792 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 793 promises.add(pool.execute())
b3432a63 794 }
e211bc18 795 await Promise.all(promises)
138d29a8 796 for (const workerNode of pool.workerNodes) {
86bf340d 797 expect(workerNode.tasksUsage).toStrictEqual({
cb4a00ab 798 run: expect.any(Number),
86bf340d
JB
799 running: 0,
800 runTime: expect.any(Number),
801 runTimeHistory: expect.any(CircularArray),
802 avgRunTime: expect.any(Number),
803 medRunTime: 0,
804 waitTime: 0,
805 waitTimeHistory: expect.any(CircularArray),
806 avgWaitTime: 0,
807 medWaitTime: 0,
808 error: 0
809 })
ee9f5295
JB
810 expect(workerNode.tasksUsage.run).toBeGreaterThanOrEqual(0)
811 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
812 expect(workerNode.tasksUsage.runTime).toBeGreaterThanOrEqual(0)
813 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThanOrEqual(0)
138d29a8 814 }
97a2abc3 815 expect(
95c83464 816 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 817 pool.workerChoiceStrategyContext.workerChoiceStrategy
08f3f44c
JB
818 ).defaultWorkerWeight
819 ).toBeGreaterThan(0)
820 expect(
821 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
822 pool.workerChoiceStrategyContext.workerChoiceStrategy
823 ).workerVirtualTaskRunTime
824 ).toBeGreaterThanOrEqual(0)
b3432a63
JB
825 // We need to clean up the resources after our test
826 await pool.destroy()
827 })
828
829 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
b3432a63
JB
830 const pool = new DynamicThreadPool(
831 min,
832 max,
833 './tests/worker-files/thread/testWorker.js',
834 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
835 )
836 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 837 const promises = new Set()
138d29a8 838 const maxMultiplier = 2
5502c07c 839 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 840 promises.add(pool.execute())
b3432a63 841 }
e211bc18 842 await Promise.all(promises)
138d29a8 843 for (const workerNode of pool.workerNodes) {
86bf340d 844 expect(workerNode.tasksUsage).toStrictEqual({
e211bc18 845 run: expect.any(Number),
86bf340d
JB
846 running: 0,
847 runTime: expect.any(Number),
848 runTimeHistory: expect.any(CircularArray),
849 avgRunTime: expect.any(Number),
850 medRunTime: 0,
851 waitTime: 0,
852 waitTimeHistory: expect.any(CircularArray),
853 avgWaitTime: 0,
854 medWaitTime: 0,
855 error: 0
856 })
e211bc18 857 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 858 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18
JB
859 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
860 expect(workerNode.tasksUsage.avgRunTime).toBeGreaterThan(0)
138d29a8 861 }
2b4fddb8
JB
862 expect(
863 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
864 pool.workerChoiceStrategyContext.workerChoiceStrategy
865 ).defaultWorkerWeight
866 ).toBeGreaterThan(0)
867 expect(
868 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
869 pool.workerChoiceStrategyContext.workerChoiceStrategy
870 ).workerVirtualTaskRunTime
871 ).toBeGreaterThanOrEqual(0)
b3432a63
JB
872 // We need to clean up the resources after our test
873 await pool.destroy()
874 })
875
9e775f96 876 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
010d7020
JB
877 const pool = new DynamicThreadPool(
878 min,
879 max,
880 './tests/worker-files/thread/testWorker.js',
881 {
882 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
883 workerChoiceStrategyOptions: {
884 medRunTime: true
885 }
886 }
887 )
888 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 889 const promises = new Set()
010d7020
JB
890 const maxMultiplier = 2
891 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 892 promises.add(pool.execute())
010d7020 893 }
e211bc18 894 await Promise.all(promises)
010d7020 895 for (const workerNode of pool.workerNodes) {
86bf340d 896 expect(workerNode.tasksUsage).toStrictEqual({
e211bc18 897 run: expect.any(Number),
86bf340d
JB
898 running: 0,
899 runTime: expect.any(Number),
900 runTimeHistory: expect.any(CircularArray),
901 avgRunTime: 0,
902 medRunTime: expect.any(Number),
903 waitTime: 0,
904 waitTimeHistory: expect.any(CircularArray),
905 avgWaitTime: 0,
906 medWaitTime: 0,
907 error: 0
908 })
e211bc18 909 expect(workerNode.tasksUsage.run).toBeGreaterThan(0)
ee9f5295 910 expect(workerNode.tasksUsage.run).toBeLessThanOrEqual(max * maxMultiplier)
e211bc18
JB
911 expect(workerNode.tasksUsage.runTime).toBeGreaterThan(0)
912 expect(workerNode.tasksUsage.medRunTime).toBeGreaterThan(0)
010d7020 913 }
08f3f44c
JB
914 expect(
915 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
916 pool.workerChoiceStrategyContext.workerChoiceStrategy
917 ).defaultWorkerWeight
918 ).toBeGreaterThan(0)
919 expect(
920 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
921 pool.workerChoiceStrategyContext.workerChoiceStrategy
922 ).workerVirtualTaskRunTime
923 ).toBeGreaterThanOrEqual(0)
010d7020
JB
924 // We need to clean up the resources after our test
925 await pool.destroy()
926 })
927
a6f7f1b4 928 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
594bfb84 929 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
f0829c53 930 let pool = new FixedThreadPool(
caeb9817
JB
931 max,
932 './tests/worker-files/thread/testWorker.js'
933 )
38f6e859 934 expect(
95c83464 935 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 936 workerChoiceStrategy
f06e48d8 937 ).currentWorkerNodeId
b529c323 938 ).toBeDefined()
38f6e859 939 expect(
95c83464 940 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 941 workerChoiceStrategy
b529c323
JB
942 ).defaultWorkerWeight
943 ).toBeDefined()
caeb9817 944 expect(
95c83464 945 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 946 workerChoiceStrategy
08f3f44c 947 ).workerVirtualTaskRunTime
b529c323 948 ).toBeDefined()
594bfb84 949 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 950 expect(
95c83464 951 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 952 pool.workerChoiceStrategyContext.workerChoiceStrategy
f06e48d8 953 ).currentWorkerNodeId
a6f7f1b4
JB
954 ).toBe(0)
955 expect(
95c83464 956 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 957 pool.workerChoiceStrategyContext.workerChoiceStrategy
95c83464 958 ).defaultWorkerWeight
a6f7f1b4 959 ).toBeGreaterThan(0)
08f3f44c
JB
960 expect(
961 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
962 workerChoiceStrategy
963 ).workerVirtualTaskRunTime
964 ).toBe(0)
f0829c53
JB
965 await pool.destroy()
966 pool = new DynamicThreadPool(
967 min,
968 max,
969 './tests/worker-files/thread/testWorker.js'
970 )
38f6e859 971 expect(
95c83464 972 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 973 workerChoiceStrategy
f06e48d8 974 ).currentWorkerNodeId
b529c323 975 ).toBeDefined()
38f6e859 976 expect(
95c83464 977 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 978 workerChoiceStrategy
b529c323
JB
979 ).defaultWorkerWeight
980 ).toBeDefined()
f0829c53 981 expect(
95c83464 982 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 983 workerChoiceStrategy
08f3f44c 984 ).workerVirtualTaskRunTime
b529c323 985 ).toBeDefined()
594bfb84 986 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 987 expect(
95c83464 988 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 989 pool.workerChoiceStrategyContext.workerChoiceStrategy
f06e48d8 990 ).currentWorkerNodeId
a6f7f1b4
JB
991 ).toBe(0)
992 expect(
95c83464 993 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 994 pool.workerChoiceStrategyContext.workerChoiceStrategy
95c83464 995 ).defaultWorkerWeight
a6f7f1b4 996 ).toBeGreaterThan(0)
08f3f44c
JB
997 expect(
998 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
999 workerChoiceStrategy
1000 ).workerVirtualTaskRunTime
1001 ).toBe(0)
caeb9817
JB
1002 // We need to clean up the resources after our test
1003 await pool.destroy()
1004 })
1005
89b09b26 1006 it('Verify unknown strategy throw error', () => {
a35560ba
S
1007 expect(
1008 () =>
1009 new DynamicThreadPool(
1010 min,
1011 max,
1012 './tests/worker-files/thread/testWorker.js',
1927ee67 1013 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
a35560ba 1014 )
d4aeae5a 1015 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
a35560ba
S
1016 })
1017})