test: improve WCS coverage
[poolifier.git] / tests / pools / selection-strategies / selection-strategies.test.js
CommitLineData
a61a0724 1const { expect } = require('expect')
a35560ba 2const {
999ef664 3 DynamicClusterPool,
a35560ba 4 DynamicThreadPool,
3d6dd312 5 FixedClusterPool,
2ced693a 6 FixedThreadPool,
3d6dd312 7 WorkerChoiceStrategies
cdace0e5 8} = require('../../../lib')
86bf340d 9const { CircularArray } = require('../../../lib/circular-array')
a35560ba
S
10
11describe('Selection strategies test suite', () => {
e1ffb94f
JB
12 const min = 0
13 const max = 3
14
a35560ba
S
15 it('Verify that WorkerChoiceStrategies enumeration provides string values', () => {
16 expect(WorkerChoiceStrategies.ROUND_ROBIN).toBe('ROUND_ROBIN')
e4543b14
JB
17 expect(WorkerChoiceStrategies.LEAST_USED).toBe('LEAST_USED')
18 expect(WorkerChoiceStrategies.LEAST_BUSY).toBe('LEAST_BUSY')
a7bbf44a 19 expect(WorkerChoiceStrategies.LEAST_ELU).toBe('LEAST_ELU')
23ff945a 20 expect(WorkerChoiceStrategies.FAIR_SHARE).toBe('FAIR_SHARE')
b3432a63
JB
21 expect(WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN).toBe(
22 'WEIGHTED_ROUND_ROBIN'
23 )
feec6e8c
JB
24 expect(WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN).toBe(
25 'INTERLEAVED_WEIGHTED_ROUND_ROBIN'
26 )
a35560ba
S
27 })
28
e843b904 29 it('Verify ROUND_ROBIN strategy is the default at pool creation', async () => {
e843b904
JB
30 const pool = new DynamicThreadPool(
31 min,
32 max,
33 './tests/worker-files/thread/testWorker.js'
34 )
35 expect(pool.opts.workerChoiceStrategy).toBe(
36 WorkerChoiceStrategies.ROUND_ROBIN
37 )
38 // We need to clean up the resources after our test
39 await pool.destroy()
40 })
41
594bfb84
JB
42 it('Verify available strategies are taken at pool creation', async () => {
43 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
44 const pool = new FixedThreadPool(
45 max,
46 './tests/worker-files/thread/testWorker.js',
47 { workerChoiceStrategy }
48 )
49 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
50 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
51 workerChoiceStrategy
52 )
53 await pool.destroy()
54 }
d2f7b7a2
JB
55 })
56
594bfb84
JB
57 it('Verify available strategies can be set after pool creation', async () => {
58 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
59 const pool = new DynamicThreadPool(
60 min,
61 max,
ec82cfa1 62 './tests/worker-files/thread/testWorker.js'
594bfb84
JB
63 )
64 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
65 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
66 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
67 workerChoiceStrategy
68 )
999ef664
JB
69 expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({
70 retries: 6,
71 runTime: { median: false },
72 waitTime: { median: false },
73 elu: { median: false }
74 })
75 expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
76 retries: 6,
77 runTime: { median: false },
78 waitTime: { median: false },
79 elu: { median: false }
80 })
81 await pool.destroy()
82 }
83 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
84 const pool = new DynamicClusterPool(
85 min,
86 max,
87 './tests/worker-files/cluster/testWorker.js'
88 )
89 pool.setWorkerChoiceStrategy(workerChoiceStrategy, { retries: 3 })
90 expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
91 expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
92 workerChoiceStrategy
93 )
94 expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({
95 retries: 3,
96 runTime: { median: false },
97 waitTime: { median: false },
98 elu: { median: false }
99 })
100 expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
101 retries: 3,
102 runTime: { median: false },
103 waitTime: { median: false },
104 elu: { median: false }
105 })
594bfb84
JB
106 await pool.destroy()
107 }
108 })
109
110 it('Verify available strategies default internals at pool creation', async () => {
111 const pool = new FixedThreadPool(
e843b904
JB
112 max,
113 './tests/worker-files/thread/testWorker.js'
114 )
594bfb84 115 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
54f4e726
JB
116 expect(
117 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
118 workerChoiceStrategy
119 ).nextWorkerNodeKey
120 ).toBe(0)
121 expect(
122 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
123 workerChoiceStrategy
124 ).previousWorkerNodeKey
125 ).toBe(0)
126 if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
08f3f44c
JB
127 expect(
128 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
129 workerChoiceStrategy
b0d6ed8f 130 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
131 ).toBeInstanceOf(Array)
132 expect(
133 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
134 workerChoiceStrategy
b0d6ed8f 135 ).workersVirtualTaskEndTimestamp.length
08f3f44c 136 ).toBe(0)
594bfb84
JB
137 } else if (
138 workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
139 ) {
140 expect(
141 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
142 workerChoiceStrategy
54f4e726
JB
143 ).defaultWorkerWeight
144 ).toBeGreaterThan(0)
145 expect(
146 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
147 workerChoiceStrategy
148 ).workerVirtualTaskRunTime
594bfb84 149 ).toBe(0)
54f4e726
JB
150 } else if (
151 workerChoiceStrategy ===
152 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
153 ) {
594bfb84
JB
154 expect(
155 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
156 workerChoiceStrategy
157 ).defaultWorkerWeight
158 ).toBeGreaterThan(0)
08f3f44c
JB
159 expect(
160 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
161 workerChoiceStrategy
162 ).workerVirtualTaskRunTime
163 ).toBe(0)
54f4e726
JB
164 expect(
165 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
166 workerChoiceStrategy
167 ).roundId
168 ).toBe(0)
169 expect(
170 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
171 workerChoiceStrategy
172 ).workerNodeId
173 ).toBe(0)
174 expect(
175 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
176 workerChoiceStrategy
177 ).roundWeights
178 ).toStrictEqual([
179 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
180 workerChoiceStrategy
181 ).defaultWorkerWeight
182 ])
594bfb84
JB
183 }
184 }
e843b904
JB
185 await pool.destroy()
186 })
187
6c6afb84
JB
188 it('Verify ROUND_ROBIN strategy default policy', async () => {
189 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
190 let pool = new FixedThreadPool(
191 max,
192 './tests/worker-files/thread/testWorker.js',
193 { workerChoiceStrategy }
194 )
195 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
94407def 196 dynamicWorkerUsage: false,
b1aae695 197 dynamicWorkerReady: true
6c6afb84
JB
198 })
199 await pool.destroy()
200 pool = new DynamicThreadPool(
201 min,
202 max,
203 './tests/worker-files/thread/testWorker.js',
204 { workerChoiceStrategy }
205 )
206 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
94407def 207 dynamicWorkerUsage: false,
b1aae695 208 dynamicWorkerReady: true
6c6afb84
JB
209 })
210 // We need to clean up the resources after our test
211 await pool.destroy()
212 })
213
214 it('Verify ROUND_ROBIN strategy default tasks statistics requirements', async () => {
594bfb84 215 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
10fcfaf4
JB
216 let pool = new FixedThreadPool(
217 max,
d710242d 218 './tests/worker-files/thread/testWorker.js',
594bfb84 219 { workerChoiceStrategy }
10fcfaf4 220 )
87de9ff5
JB
221 expect(
222 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
223 ).toStrictEqual({
932fc8be
JB
224 runTime: {
225 aggregate: false,
226 average: false,
227 median: false
228 },
229 waitTime: {
230 aggregate: false,
231 average: false,
232 median: false
233 },
5df69fab
JB
234 elu: {
235 aggregate: false,
236 average: false,
237 median: false
238 }
86bf340d 239 })
fd7ebd49 240 await pool.destroy()
10fcfaf4
JB
241 pool = new DynamicThreadPool(
242 min,
243 max,
d710242d 244 './tests/worker-files/thread/testWorker.js',
594bfb84 245 { workerChoiceStrategy }
10fcfaf4 246 )
87de9ff5
JB
247 expect(
248 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
249 ).toStrictEqual({
932fc8be
JB
250 runTime: {
251 aggregate: false,
252 average: false,
253 median: false
254 },
255 waitTime: {
256 aggregate: false,
257 average: false,
258 median: false
259 },
5df69fab
JB
260 elu: {
261 aggregate: false,
262 average: false,
263 median: false
264 }
86bf340d 265 })
10fcfaf4
JB
266 // We need to clean up the resources after our test
267 await pool.destroy()
268 })
269
bdaf31cd 270 it('Verify ROUND_ROBIN strategy can be run in a fixed pool', async () => {
bdaf31cd
JB
271 const pool = new FixedThreadPool(
272 max,
273 './tests/worker-files/thread/testWorker.js',
274 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
275 )
bdaf31cd 276 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
ee9f5295 277 const promises = new Set()
a20f0ba5
JB
278 const maxMultiplier = 2
279 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 280 promises.add(pool.execute())
e211bc18
JB
281 }
282 await Promise.all(promises)
283 for (const workerNode of pool.workerNodes) {
465b2940 284 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
285 tasks: {
286 executed: maxMultiplier,
287 executing: 0,
288 queued: 0,
df593701 289 maxQueued: 0,
68cbdc84 290 stolen: 0,
a4e07f72
JB
291 failed: 0
292 },
293 runTime: {
4ba4c7f9 294 history: new CircularArray()
a4e07f72
JB
295 },
296 waitTime: {
4ba4c7f9 297 history: new CircularArray()
a4e07f72 298 },
5df69fab
JB
299 elu: {
300 idle: {
4ba4c7f9 301 history: new CircularArray()
5df69fab
JB
302 },
303 active: {
4ba4c7f9 304 history: new CircularArray()
71514351 305 }
5df69fab 306 }
e211bc18 307 })
bdaf31cd 308 }
9458090a
JB
309 expect(
310 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
311 WorkerChoiceStrategies.ROUND_ROBIN
9b106837 312 ).nextWorkerNodeKey
9458090a 313 ).toBe(0)
bdaf31cd
JB
314 // We need to clean up the resources after our test
315 await pool.destroy()
316 })
317
318 it('Verify ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
bdaf31cd
JB
319 const pool = new DynamicThreadPool(
320 min,
321 max,
322 './tests/worker-files/thread/testWorker.js',
323 { workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN }
324 )
bdaf31cd 325 // TODO: Create a better test to cover `RoundRobinWorkerChoiceStrategy#choose`
ee9f5295 326 const promises = new Set()
a20f0ba5
JB
327 const maxMultiplier = 2
328 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 329 promises.add(pool.execute())
e211bc18
JB
330 }
331 await Promise.all(promises)
332 for (const workerNode of pool.workerNodes) {
465b2940 333 expect(workerNode.usage).toStrictEqual({
a4e07f72 334 tasks: {
94407def 335 executed: expect.any(Number),
a4e07f72
JB
336 executing: 0,
337 queued: 0,
df593701 338 maxQueued: 0,
68cbdc84 339 stolen: 0,
a4e07f72
JB
340 failed: 0
341 },
342 runTime: {
4ba4c7f9 343 history: new CircularArray()
a4e07f72
JB
344 },
345 waitTime: {
4ba4c7f9 346 history: new CircularArray()
a4e07f72 347 },
5df69fab
JB
348 elu: {
349 idle: {
4ba4c7f9 350 history: new CircularArray()
5df69fab
JB
351 },
352 active: {
4ba4c7f9 353 history: new CircularArray()
71514351 354 }
5df69fab 355 }
e211bc18 356 })
94407def
JB
357 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
358 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
359 max * maxMultiplier
360 )
bdaf31cd 361 }
9458090a
JB
362 expect(
363 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
364 WorkerChoiceStrategies.ROUND_ROBIN
9b106837 365 ).nextWorkerNodeKey
9458090a 366 ).toBe(0)
bdaf31cd
JB
367 // We need to clean up the resources after our test
368 await pool.destroy()
369 })
370
2ced693a 371 it('Verify ROUND_ROBIN strategy runtime behavior', async () => {
594bfb84 372 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
2ced693a
JB
373 let pool = new FixedClusterPool(
374 max,
594bfb84
JB
375 './tests/worker-files/cluster/testWorker.js',
376 { workerChoiceStrategy }
2ced693a
JB
377 )
378 let results = new Set()
379 for (let i = 0; i < max; i++) {
20dcad1a 380 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.id)
2ced693a
JB
381 }
382 expect(results.size).toBe(max)
383 await pool.destroy()
594bfb84
JB
384 pool = new FixedThreadPool(
385 max,
386 './tests/worker-files/thread/testWorker.js',
387 { workerChoiceStrategy }
388 )
2ced693a
JB
389 results = new Set()
390 for (let i = 0; i < max; i++) {
20dcad1a 391 results.add(pool.workerNodes[pool.chooseWorkerNode()].worker.threadId)
2ced693a
JB
392 }
393 expect(results.size).toBe(max)
394 await pool.destroy()
395 })
396
a6f7f1b4 397 it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
594bfb84 398 const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
a6f7f1b4
JB
399 let pool = new FixedThreadPool(
400 max,
401 './tests/worker-files/thread/testWorker.js',
402 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
403 )
38f6e859 404 expect(
95c83464 405 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 406 workerChoiceStrategy
9b106837 407 ).nextWorkerNodeKey
b529c323 408 ).toBeDefined()
594bfb84 409 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 410 expect(
95c83464 411 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 412 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 413 ).nextWorkerNodeKey
a6f7f1b4
JB
414 ).toBe(0)
415 await pool.destroy()
416 pool = new DynamicThreadPool(
417 min,
418 max,
419 './tests/worker-files/thread/testWorker.js',
420 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
421 )
38f6e859 422 expect(
95c83464 423 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 424 workerChoiceStrategy
9b106837 425 ).nextWorkerNodeKey
b529c323 426 ).toBeDefined()
594bfb84 427 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 428 expect(
95c83464 429 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 430 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 431 ).nextWorkerNodeKey
a6f7f1b4
JB
432 ).toBe(0)
433 // We need to clean up the resources after our test
434 await pool.destroy()
435 })
436
6c6afb84
JB
437 it('Verify LEAST_USED strategy default policy', async () => {
438 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
439 let pool = new FixedThreadPool(
440 max,
441 './tests/worker-files/thread/testWorker.js',
442 { workerChoiceStrategy }
443 )
444 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
445 dynamicWorkerUsage: false,
446 dynamicWorkerReady: true
6c6afb84
JB
447 })
448 await pool.destroy()
449 pool = new DynamicThreadPool(
450 min,
451 max,
452 './tests/worker-files/thread/testWorker.js',
453 { workerChoiceStrategy }
454 )
455 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
456 dynamicWorkerUsage: false,
457 dynamicWorkerReady: true
6c6afb84
JB
458 })
459 // We need to clean up the resources after our test
460 await pool.destroy()
461 })
462
463 it('Verify LEAST_USED strategy default tasks statistics requirements', async () => {
e4543b14 464 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
10fcfaf4
JB
465 let pool = new FixedThreadPool(
466 max,
d710242d 467 './tests/worker-files/thread/testWorker.js',
594bfb84 468 { workerChoiceStrategy }
10fcfaf4 469 )
87de9ff5
JB
470 expect(
471 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
472 ).toStrictEqual({
932fc8be
JB
473 runTime: {
474 aggregate: false,
475 average: false,
476 median: false
477 },
478 waitTime: {
479 aggregate: false,
480 average: false,
481 median: false
482 },
5df69fab
JB
483 elu: {
484 aggregate: false,
485 average: false,
486 median: false
487 }
86bf340d 488 })
fd7ebd49 489 await pool.destroy()
10fcfaf4
JB
490 pool = new DynamicThreadPool(
491 min,
492 max,
d710242d 493 './tests/worker-files/thread/testWorker.js',
594bfb84 494 { workerChoiceStrategy }
10fcfaf4 495 )
87de9ff5
JB
496 expect(
497 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
498 ).toStrictEqual({
932fc8be
JB
499 runTime: {
500 aggregate: false,
501 average: false,
502 median: false
503 },
504 waitTime: {
505 aggregate: false,
506 average: false,
507 median: false
508 },
5df69fab
JB
509 elu: {
510 aggregate: false,
511 average: false,
512 median: false
513 }
86bf340d 514 })
10fcfaf4
JB
515 // We need to clean up the resources after our test
516 await pool.destroy()
517 })
518
e4543b14 519 it('Verify LEAST_USED strategy can be run in a fixed pool', async () => {
b98ec2e6
JB
520 const pool = new FixedThreadPool(
521 max,
522 './tests/worker-files/thread/testWorker.js',
e4543b14 523 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
b98ec2e6 524 )
e4543b14 525 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
ee9f5295 526 const promises = new Set()
a20f0ba5
JB
527 const maxMultiplier = 2
528 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 529 promises.add(pool.execute())
e211bc18
JB
530 }
531 await Promise.all(promises)
532 for (const workerNode of pool.workerNodes) {
465b2940 533 expect(workerNode.usage).toStrictEqual({
a4e07f72 534 tasks: {
76407b8e 535 executed: expect.any(Number),
a4e07f72
JB
536 executing: 0,
537 queued: 0,
df593701 538 maxQueued: 0,
68cbdc84 539 stolen: 0,
a4e07f72
JB
540 failed: 0
541 },
542 runTime: {
4ba4c7f9 543 history: new CircularArray()
a4e07f72
JB
544 },
545 waitTime: {
4ba4c7f9 546 history: new CircularArray()
a4e07f72 547 },
5df69fab
JB
548 elu: {
549 idle: {
4ba4c7f9 550 history: new CircularArray()
5df69fab
JB
551 },
552 active: {
4ba4c7f9 553 history: new CircularArray()
71514351 554 }
5df69fab 555 }
e211bc18 556 })
465b2940
JB
557 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
558 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
76407b8e
JB
559 max * maxMultiplier
560 )
a35560ba 561 }
a35560ba
S
562 // We need to clean up the resources after our test
563 await pool.destroy()
564 })
565
e4543b14 566 it('Verify LEAST_USED strategy can be run in a dynamic pool', async () => {
ff5e76e1
JB
567 const pool = new DynamicThreadPool(
568 min,
569 max,
570 './tests/worker-files/thread/testWorker.js',
e4543b14 571 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_USED }
ff5e76e1 572 )
e4543b14 573 // TODO: Create a better test to cover `LeastUsedWorkerChoiceStrategy#choose`
ee9f5295 574 const promises = new Set()
a20f0ba5
JB
575 const maxMultiplier = 2
576 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 577 promises.add(pool.execute())
e211bc18
JB
578 }
579 await Promise.all(promises)
580 for (const workerNode of pool.workerNodes) {
465b2940 581 expect(workerNode.usage).toStrictEqual({
a4e07f72 582 tasks: {
76407b8e 583 executed: expect.any(Number),
a4e07f72
JB
584 executing: 0,
585 queued: 0,
df593701 586 maxQueued: 0,
68cbdc84 587 stolen: 0,
a4e07f72
JB
588 failed: 0
589 },
590 runTime: {
4ba4c7f9 591 history: new CircularArray()
a4e07f72
JB
592 },
593 waitTime: {
4ba4c7f9 594 history: new CircularArray()
a4e07f72 595 },
5df69fab
JB
596 elu: {
597 idle: {
4ba4c7f9 598 history: new CircularArray()
5df69fab
JB
599 },
600 active: {
4ba4c7f9 601 history: new CircularArray()
71514351 602 }
5df69fab 603 }
e211bc18 604 })
465b2940
JB
605 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
606 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
76407b8e
JB
607 max * maxMultiplier
608 )
168c526f 609 }
168c526f
JB
610 // We need to clean up the resources after our test
611 await pool.destroy()
612 })
613
6c6afb84
JB
614 it('Verify LEAST_BUSY strategy default policy', async () => {
615 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
616 let pool = new FixedThreadPool(
617 max,
618 './tests/worker-files/thread/testWorker.js',
619 { workerChoiceStrategy }
620 )
621 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
622 dynamicWorkerUsage: false,
623 dynamicWorkerReady: true
6c6afb84
JB
624 })
625 await pool.destroy()
626 pool = new DynamicThreadPool(
627 min,
628 max,
629 './tests/worker-files/thread/testWorker.js',
630 { workerChoiceStrategy }
631 )
632 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
633 dynamicWorkerUsage: false,
634 dynamicWorkerReady: true
6c6afb84
JB
635 })
636 // We need to clean up the resources after our test
637 await pool.destroy()
638 })
639
640 it('Verify LEAST_BUSY strategy default tasks statistics requirements', async () => {
e4543b14 641 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
168c526f
JB
642 let pool = new FixedThreadPool(
643 max,
d710242d 644 './tests/worker-files/thread/testWorker.js',
594bfb84 645 { workerChoiceStrategy }
168c526f 646 )
87de9ff5
JB
647 expect(
648 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
649 ).toStrictEqual({
932fc8be
JB
650 runTime: {
651 aggregate: true,
652 average: false,
653 median: false
654 },
655 waitTime: {
656 aggregate: true,
657 average: false,
658 median: false
659 },
5df69fab
JB
660 elu: {
661 aggregate: false,
662 average: false,
663 median: false
664 }
86bf340d 665 })
168c526f
JB
666 await pool.destroy()
667 pool = new DynamicThreadPool(
668 min,
669 max,
d710242d 670 './tests/worker-files/thread/testWorker.js',
594bfb84 671 { workerChoiceStrategy }
168c526f 672 )
87de9ff5
JB
673 expect(
674 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
675 ).toStrictEqual({
932fc8be
JB
676 runTime: {
677 aggregate: true,
678 average: false,
679 median: false
680 },
681 waitTime: {
682 aggregate: true,
683 average: false,
684 median: false
685 },
5df69fab
JB
686 elu: {
687 aggregate: false,
688 average: false,
689 median: false
690 }
86bf340d 691 })
168c526f
JB
692 // We need to clean up the resources after our test
693 await pool.destroy()
694 })
695
e4543b14 696 it('Verify LEAST_BUSY strategy can be run in a fixed pool', async () => {
168c526f
JB
697 const pool = new FixedThreadPool(
698 max,
699 './tests/worker-files/thread/testWorker.js',
e4543b14 700 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
168c526f 701 )
e4543b14 702 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
ee9f5295 703 const promises = new Set()
a20f0ba5
JB
704 const maxMultiplier = 2
705 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 706 promises.add(pool.execute())
e211bc18
JB
707 }
708 await Promise.all(promises)
709 for (const workerNode of pool.workerNodes) {
619f403b 710 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
711 tasks: {
712 executed: expect.any(Number),
713 executing: 0,
714 queued: 0,
df593701 715 maxQueued: 0,
68cbdc84 716 stolen: 0,
a4e07f72
JB
717 failed: 0
718 },
619f403b 719 runTime: expect.objectContaining({
a4e07f72 720 history: expect.any(CircularArray)
619f403b
JB
721 }),
722 waitTime: expect.objectContaining({
a4e07f72 723 history: expect.any(CircularArray)
619f403b 724 }),
5df69fab
JB
725 elu: {
726 idle: {
619f403b 727 history: new CircularArray()
5df69fab
JB
728 },
729 active: {
619f403b 730 history: new CircularArray()
71514351 731 }
5df69fab 732 }
e211bc18 733 })
465b2940
JB
734 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
735 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
736 max * maxMultiplier
737 )
19dbc45b
JB
738 if (workerNode.usage.runTime.aggregate == null) {
739 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
740 } else {
741 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
742 }
743 if (workerNode.usage.waitTime.aggregate == null) {
744 expect(workerNode.usage.waitTime.aggregate).toBeUndefined()
745 } else {
746 expect(workerNode.usage.waitTime.aggregate).toBeGreaterThan(0)
747 }
168c526f 748 }
168c526f
JB
749 // We need to clean up the resources after our test
750 await pool.destroy()
751 })
752
e4543b14 753 it('Verify LEAST_BUSY strategy can be run in a dynamic pool', async () => {
168c526f
JB
754 const pool = new DynamicThreadPool(
755 min,
756 max,
757 './tests/worker-files/thread/testWorker.js',
e4543b14 758 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_BUSY }
168c526f 759 )
e4543b14 760 // TODO: Create a better test to cover `LeastBusyWorkerChoiceStrategy#choose`
ee9f5295 761 const promises = new Set()
a20f0ba5
JB
762 const maxMultiplier = 2
763 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 764 promises.add(pool.execute())
e211bc18
JB
765 }
766 await Promise.all(promises)
767 for (const workerNode of pool.workerNodes) {
619f403b 768 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
769 tasks: {
770 executed: expect.any(Number),
771 executing: 0,
772 queued: 0,
df593701 773 maxQueued: 0,
68cbdc84 774 stolen: 0,
a4e07f72
JB
775 failed: 0
776 },
619f403b 777 runTime: expect.objectContaining({
a4e07f72 778 history: expect.any(CircularArray)
619f403b
JB
779 }),
780 waitTime: expect.objectContaining({
a4e07f72 781 history: expect.any(CircularArray)
619f403b 782 }),
5df69fab
JB
783 elu: {
784 idle: {
619f403b 785 history: new CircularArray()
5df69fab
JB
786 },
787 active: {
619f403b 788 history: new CircularArray()
71514351 789 }
5df69fab 790 }
e211bc18 791 })
465b2940
JB
792 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
793 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
794 max * maxMultiplier
795 )
19dbc45b
JB
796 if (workerNode.usage.runTime.aggregate == null) {
797 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
798 } else {
799 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
800 }
801 if (workerNode.usage.waitTime.aggregate == null) {
802 expect(workerNode.usage.waitTime.aggregate).toBeUndefined()
803 } else {
804 expect(workerNode.usage.waitTime.aggregate).toBeGreaterThan(0)
805 }
ff5e76e1 806 }
ff5e76e1
JB
807 // We need to clean up the resources after our test
808 await pool.destroy()
809 })
810
6c6afb84
JB
811 it('Verify LEAST_ELU strategy default policy', async () => {
812 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
813 let pool = new FixedThreadPool(
814 max,
815 './tests/worker-files/thread/testWorker.js',
816 { workerChoiceStrategy }
817 )
818 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
819 dynamicWorkerUsage: false,
820 dynamicWorkerReady: true
6c6afb84
JB
821 })
822 await pool.destroy()
823 pool = new DynamicThreadPool(
824 min,
825 max,
826 './tests/worker-files/thread/testWorker.js',
827 { workerChoiceStrategy }
828 )
829 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
830 dynamicWorkerUsage: false,
831 dynamicWorkerReady: true
6c6afb84
JB
832 })
833 // We need to clean up the resources after our test
834 await pool.destroy()
835 })
836
837 it('Verify LEAST_ELU strategy default tasks statistics requirements', async () => {
a7bbf44a
JB
838 const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
839 let pool = new FixedThreadPool(
840 max,
841 './tests/worker-files/thread/testWorker.js',
842 { workerChoiceStrategy }
843 )
05302647
JB
844 expect(
845 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
846 ).toStrictEqual({
e460940e
JB
847 runTime: {
848 aggregate: false,
849 average: false,
850 median: false
851 },
852 waitTime: {
853 aggregate: false,
854 average: false,
855 median: false
856 },
5df69fab
JB
857 elu: {
858 aggregate: true,
859 average: false,
860 median: false
861 }
a7bbf44a
JB
862 })
863 await pool.destroy()
864 pool = new DynamicThreadPool(
865 min,
866 max,
867 './tests/worker-files/thread/testWorker.js',
868 { workerChoiceStrategy }
869 )
05302647
JB
870 expect(
871 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
872 ).toStrictEqual({
e460940e
JB
873 runTime: {
874 aggregate: false,
875 average: false,
876 median: false
877 },
878 waitTime: {
879 aggregate: false,
880 average: false,
881 median: false
882 },
5df69fab
JB
883 elu: {
884 aggregate: true,
885 average: false,
886 median: false
887 }
a7bbf44a
JB
888 })
889 // We need to clean up the resources after our test
890 await pool.destroy()
891 })
892
ae9cf3c8 893 it('Verify LEAST_ELU strategy can be run in a fixed pool', async () => {
c5ad42cd
JB
894 const pool = new FixedThreadPool(
895 max,
896 './tests/worker-files/thread/testWorker.js',
897 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU }
898 )
899 // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#choose`
dd38581f 900 const promises = new Set()
c5ad42cd
JB
901 const maxMultiplier = 2
902 for (let i = 0; i < max * maxMultiplier; i++) {
dd38581f 903 promises.add(pool.execute())
c5ad42cd 904 }
dd38581f 905 await Promise.all(promises)
c5ad42cd 906 for (const workerNode of pool.workerNodes) {
619f403b 907 expect(workerNode.usage).toStrictEqual({
c5ad42cd
JB
908 tasks: {
909 executed: expect.any(Number),
910 executing: 0,
911 queued: 0,
df593701 912 maxQueued: 0,
68cbdc84 913 stolen: 0,
c5ad42cd
JB
914 failed: 0
915 },
916 runTime: {
619f403b 917 history: new CircularArray()
a1347286
JB
918 },
919 waitTime: {
619f403b 920 history: new CircularArray()
5df69fab 921 },
619f403b
JB
922 elu: expect.objectContaining({
923 idle: expect.objectContaining({
5df69fab 924 history: expect.any(CircularArray)
619f403b
JB
925 }),
926 active: expect.objectContaining({
5df69fab 927 history: expect.any(CircularArray)
619f403b
JB
928 })
929 })
5df69fab 930 })
465b2940
JB
931 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
932 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a1347286
JB
933 max * maxMultiplier
934 )
7db63069
JB
935 if (workerNode.usage.elu.active.aggregate == null) {
936 expect(workerNode.usage.elu.active.aggregate).toBeUndefined()
937 } else {
938 expect(workerNode.usage.elu.active.aggregate).toBeGreaterThan(0)
939 }
940 if (workerNode.usage.elu.idle.aggregate == null) {
941 expect(workerNode.usage.elu.idle.aggregate).toBeUndefined()
942 } else {
943 expect(workerNode.usage.elu.idle.aggregate).toBeGreaterThanOrEqual(0)
944 }
71514351
JB
945 if (workerNode.usage.elu.utilization == null) {
946 expect(workerNode.usage.elu.utilization).toBeUndefined()
947 } else {
948 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
949 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
950 }
a1347286
JB
951 }
952 // We need to clean up the resources after our test
953 await pool.destroy()
954 })
955
956 it('Verify LEAST_ELU strategy can be run in a dynamic pool', async () => {
957 const pool = new DynamicThreadPool(
958 min,
959 max,
960 './tests/worker-files/thread/testWorker.js',
961 { workerChoiceStrategy: WorkerChoiceStrategies.LEAST_ELU }
962 )
963 // TODO: Create a better test to cover `LeastEluWorkerChoiceStrategy#choose`
dd38581f 964 const promises = new Set()
a1347286
JB
965 const maxMultiplier = 2
966 for (let i = 0; i < max * maxMultiplier; i++) {
dd38581f 967 promises.add(pool.execute())
a1347286 968 }
dd38581f 969 await Promise.all(promises)
a1347286 970 for (const workerNode of pool.workerNodes) {
619f403b 971 expect(workerNode.usage).toStrictEqual({
a1347286
JB
972 tasks: {
973 executed: expect.any(Number),
974 executing: 0,
975 queued: 0,
df593701 976 maxQueued: 0,
68cbdc84 977 stolen: 0,
a1347286
JB
978 failed: 0
979 },
980 runTime: {
619f403b 981 history: new CircularArray()
c5ad42cd
JB
982 },
983 waitTime: {
619f403b 984 history: new CircularArray()
5df69fab 985 },
619f403b
JB
986 elu: expect.objectContaining({
987 idle: expect.objectContaining({
5df69fab 988 history: expect.any(CircularArray)
619f403b
JB
989 }),
990 active: expect.objectContaining({
5df69fab 991 history: expect.any(CircularArray)
619f403b
JB
992 })
993 })
5df69fab 994 })
465b2940
JB
995 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
996 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
c5ad42cd
JB
997 max * maxMultiplier
998 )
7db63069
JB
999 if (workerNode.usage.elu.active.aggregate == null) {
1000 expect(workerNode.usage.elu.active.aggregate).toBeUndefined()
1001 } else {
1002 expect(workerNode.usage.elu.active.aggregate).toBeGreaterThan(0)
1003 }
1004 if (workerNode.usage.elu.idle.aggregate == null) {
1005 expect(workerNode.usage.elu.idle.aggregate).toBeUndefined()
1006 } else {
1007 expect(workerNode.usage.elu.idle.aggregate).toBeGreaterThanOrEqual(0)
1008 }
71514351
JB
1009 if (workerNode.usage.elu.utilization == null) {
1010 expect(workerNode.usage.elu.utilization).toBeUndefined()
1011 } else {
1012 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
1013 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
1014 }
c5ad42cd
JB
1015 }
1016 // We need to clean up the resources after our test
1017 await pool.destroy()
1018 })
1019
6c6afb84
JB
1020 it('Verify FAIR_SHARE strategy default policy', async () => {
1021 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
1022 let pool = new FixedThreadPool(
1023 max,
1024 './tests/worker-files/thread/testWorker.js',
1025 { workerChoiceStrategy }
1026 )
1027 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1028 dynamicWorkerUsage: false,
1029 dynamicWorkerReady: true
6c6afb84
JB
1030 })
1031 await pool.destroy()
1032 pool = new DynamicThreadPool(
1033 min,
1034 max,
1035 './tests/worker-files/thread/testWorker.js',
1036 { workerChoiceStrategy }
1037 )
1038 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1039 dynamicWorkerUsage: false,
1040 dynamicWorkerReady: true
6c6afb84
JB
1041 })
1042 // We need to clean up the resources after our test
1043 await pool.destroy()
1044 })
1045
1046 it('Verify FAIR_SHARE strategy default tasks statistics requirements', async () => {
594bfb84 1047 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
10fcfaf4
JB
1048 let pool = new FixedThreadPool(
1049 max,
d710242d 1050 './tests/worker-files/thread/testWorker.js',
594bfb84 1051 { workerChoiceStrategy }
10fcfaf4 1052 )
87de9ff5
JB
1053 expect(
1054 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1055 ).toStrictEqual({
932fc8be
JB
1056 runTime: {
1057 aggregate: true,
1058 average: true,
1059 median: false
1060 },
1061 waitTime: {
1062 aggregate: false,
1063 average: false,
1064 median: false
1065 },
5df69fab 1066 elu: {
9adcefab
JB
1067 aggregate: true,
1068 average: true,
5df69fab
JB
1069 median: false
1070 }
86bf340d 1071 })
fd7ebd49 1072 await pool.destroy()
10fcfaf4
JB
1073 pool = new DynamicThreadPool(
1074 min,
1075 max,
d710242d 1076 './tests/worker-files/thread/testWorker.js',
594bfb84 1077 { workerChoiceStrategy }
10fcfaf4 1078 )
87de9ff5
JB
1079 expect(
1080 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1081 ).toStrictEqual({
932fc8be
JB
1082 runTime: {
1083 aggregate: true,
1084 average: true,
1085 median: false
1086 },
1087 waitTime: {
1088 aggregate: false,
1089 average: false,
1090 median: false
1091 },
5df69fab 1092 elu: {
9adcefab
JB
1093 aggregate: true,
1094 average: true,
5df69fab
JB
1095 median: false
1096 }
86bf340d 1097 })
10fcfaf4
JB
1098 // We need to clean up the resources after our test
1099 await pool.destroy()
1100 })
1101
23ff945a 1102 it('Verify FAIR_SHARE strategy can be run in a fixed pool', async () => {
23ff945a
JB
1103 const pool = new FixedThreadPool(
1104 max,
1105 './tests/worker-files/thread/testWorker.js',
1106 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
1107 )
1108 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 1109 const promises = new Set()
a20f0ba5
JB
1110 const maxMultiplier = 2
1111 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1112 promises.add(pool.execute())
23ff945a 1113 }
e211bc18 1114 await Promise.all(promises)
138d29a8 1115 for (const workerNode of pool.workerNodes) {
619f403b 1116 expect(workerNode.usage).toStrictEqual({
a4e07f72 1117 tasks: {
d33be430 1118 executed: expect.any(Number),
a4e07f72
JB
1119 executing: 0,
1120 queued: 0,
df593701 1121 maxQueued: 0,
68cbdc84 1122 stolen: 0,
a4e07f72
JB
1123 failed: 0
1124 },
619f403b 1125 runTime: expect.objectContaining({
a4e07f72 1126 history: expect.any(CircularArray)
619f403b 1127 }),
a4e07f72 1128 waitTime: {
619f403b 1129 history: new CircularArray()
a4e07f72 1130 },
619f403b
JB
1131 elu: expect.objectContaining({
1132 idle: expect.objectContaining({
5df69fab 1133 history: expect.any(CircularArray)
619f403b
JB
1134 }),
1135 active: expect.objectContaining({
5df69fab 1136 history: expect.any(CircularArray)
619f403b
JB
1137 })
1138 })
86bf340d 1139 })
465b2940
JB
1140 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1141 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
d33be430
JB
1142 max * maxMultiplier
1143 )
71514351
JB
1144 if (workerNode.usage.runTime.aggregate == null) {
1145 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1146 } else {
1147 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1148 }
1149 if (workerNode.usage.runTime.average == null) {
1150 expect(workerNode.usage.runTime.average).toBeUndefined()
1151 } else {
1152 expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
1153 }
7db63069
JB
1154 if (workerNode.usage.elu.active.aggregate == null) {
1155 expect(workerNode.usage.elu.active.aggregate).toBeUndefined()
1156 } else {
1157 expect(workerNode.usage.elu.active.aggregate).toBeGreaterThan(0)
1158 }
1159 if (workerNode.usage.elu.idle.aggregate == null) {
1160 expect(workerNode.usage.elu.idle.aggregate).toBeUndefined()
1161 } else {
1162 expect(workerNode.usage.elu.idle.aggregate).toBeGreaterThanOrEqual(0)
1163 }
71514351
JB
1164 if (workerNode.usage.elu.utilization == null) {
1165 expect(workerNode.usage.elu.utilization).toBeUndefined()
1166 } else {
1167 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
1168 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
1169 }
138d29a8 1170 }
97a2abc3 1171 expect(
95c83464 1172 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1173 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 1174 ).workersVirtualTaskEndTimestamp.length
f06e48d8 1175 ).toBe(pool.workerNodes.length)
23ff945a
JB
1176 // We need to clean up the resources after our test
1177 await pool.destroy()
1178 })
1179
1180 it('Verify FAIR_SHARE strategy can be run in a dynamic pool', async () => {
23ff945a
JB
1181 const pool = new DynamicThreadPool(
1182 min,
1183 max,
1184 './tests/worker-files/thread/testWorker.js',
1185 { workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
1186 )
1187 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 1188 const promises = new Set()
f7070eee 1189 const maxMultiplier = 2
804a889e 1190 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1191 promises.add(pool.execute())
23ff945a 1192 }
e211bc18 1193 await Promise.all(promises)
138d29a8 1194 for (const workerNode of pool.workerNodes) {
619f403b 1195 expect(workerNode.usage).toStrictEqual({
a4e07f72 1196 tasks: {
6c6afb84 1197 executed: expect.any(Number),
a4e07f72
JB
1198 executing: 0,
1199 queued: 0,
df593701 1200 maxQueued: 0,
68cbdc84 1201 stolen: 0,
a4e07f72
JB
1202 failed: 0
1203 },
619f403b 1204 runTime: expect.objectContaining({
a4e07f72 1205 history: expect.any(CircularArray)
619f403b 1206 }),
a4e07f72 1207 waitTime: {
619f403b 1208 history: new CircularArray()
a4e07f72 1209 },
619f403b
JB
1210 elu: expect.objectContaining({
1211 idle: expect.objectContaining({
5df69fab 1212 history: expect.any(CircularArray)
619f403b
JB
1213 }),
1214 active: expect.objectContaining({
5df69fab 1215 history: expect.any(CircularArray)
619f403b
JB
1216 })
1217 })
86bf340d 1218 })
465b2940
JB
1219 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1220 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
6c6afb84
JB
1221 max * maxMultiplier
1222 )
71514351
JB
1223 if (workerNode.usage.runTime.aggregate == null) {
1224 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1225 } else {
1226 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1227 }
1228 if (workerNode.usage.runTime.average == null) {
1229 expect(workerNode.usage.runTime.average).toBeUndefined()
1230 } else {
1231 expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
1232 }
7db63069
JB
1233 if (workerNode.usage.elu.active.aggregate == null) {
1234 expect(workerNode.usage.elu.active.aggregate).toBeUndefined()
1235 } else {
1236 expect(workerNode.usage.elu.active.aggregate).toBeGreaterThan(0)
1237 }
1238 if (workerNode.usage.elu.idle.aggregate == null) {
1239 expect(workerNode.usage.elu.idle.aggregate).toBeUndefined()
1240 } else {
1241 expect(workerNode.usage.elu.idle.aggregate).toBeGreaterThanOrEqual(0)
1242 }
71514351
JB
1243 if (workerNode.usage.elu.utilization == null) {
1244 expect(workerNode.usage.elu.utilization).toBeUndefined()
1245 } else {
1246 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
1247 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
1248 }
138d29a8 1249 }
2b4fddb8
JB
1250 expect(
1251 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1252 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 1253 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1254 ).toBe(pool.workerNodes.length)
23ff945a
JB
1255 // We need to clean up the resources after our test
1256 await pool.destroy()
1257 })
1258
9e775f96 1259 it('Verify FAIR_SHARE strategy can be run in a dynamic pool with median runtime statistic', async () => {
010d7020
JB
1260 const pool = new DynamicThreadPool(
1261 min,
1262 max,
1263 './tests/worker-files/thread/testWorker.js',
1264 {
1265 workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE,
1266 workerChoiceStrategyOptions: {
932fc8be 1267 runTime: { median: true }
010d7020
JB
1268 }
1269 }
1270 )
1271 // TODO: Create a better test to cover `FairShareChoiceStrategy#choose`
ee9f5295 1272 const promises = new Set()
010d7020
JB
1273 const maxMultiplier = 2
1274 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1275 promises.add(pool.execute())
010d7020 1276 }
e211bc18 1277 await Promise.all(promises)
010d7020 1278 for (const workerNode of pool.workerNodes) {
619f403b 1279 expect(workerNode.usage).toStrictEqual({
a4e07f72 1280 tasks: {
6c6afb84 1281 executed: expect.any(Number),
a4e07f72
JB
1282 executing: 0,
1283 queued: 0,
df593701 1284 maxQueued: 0,
68cbdc84 1285 stolen: 0,
a4e07f72
JB
1286 failed: 0
1287 },
619f403b 1288 runTime: expect.objectContaining({
a4e07f72 1289 history: expect.any(CircularArray)
619f403b 1290 }),
a4e07f72 1291 waitTime: {
619f403b 1292 history: new CircularArray()
a4e07f72 1293 },
619f403b
JB
1294 elu: expect.objectContaining({
1295 idle: expect.objectContaining({
5df69fab 1296 history: expect.any(CircularArray)
619f403b
JB
1297 }),
1298 active: expect.objectContaining({
5df69fab 1299 history: expect.any(CircularArray)
619f403b
JB
1300 })
1301 })
86bf340d 1302 })
465b2940
JB
1303 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1304 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
6c6afb84
JB
1305 max * maxMultiplier
1306 )
71514351
JB
1307 if (workerNode.usage.runTime.aggregate == null) {
1308 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1309 } else {
1310 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1311 }
1312 if (workerNode.usage.runTime.median == null) {
1313 expect(workerNode.usage.runTime.median).toBeUndefined()
1314 } else {
1315 expect(workerNode.usage.runTime.median).toBeGreaterThan(0)
1316 }
7db63069
JB
1317 if (workerNode.usage.elu.active.aggregate == null) {
1318 expect(workerNode.usage.elu.active.aggregate).toBeUndefined()
1319 } else {
1320 expect(workerNode.usage.elu.active.aggregate).toBeGreaterThan(0)
1321 }
1322 if (workerNode.usage.elu.idle.aggregate == null) {
1323 expect(workerNode.usage.elu.idle.aggregate).toBeUndefined()
1324 } else {
1325 expect(workerNode.usage.elu.idle.aggregate).toBeGreaterThanOrEqual(0)
1326 }
71514351
JB
1327 if (workerNode.usage.elu.utilization == null) {
1328 expect(workerNode.usage.elu.utilization).toBeUndefined()
1329 } else {
1330 expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
1331 expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
1332 }
010d7020 1333 }
2b4fddb8
JB
1334 expect(
1335 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1336 pool.workerChoiceStrategyContext.workerChoiceStrategy
b0d6ed8f 1337 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1338 ).toBe(pool.workerNodes.length)
010d7020
JB
1339 // We need to clean up the resources after our test
1340 await pool.destroy()
1341 })
1342
a6f7f1b4 1343 it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
594bfb84 1344 const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
f0829c53 1345 let pool = new FixedThreadPool(
caeb9817
JB
1346 max,
1347 './tests/worker-files/thread/testWorker.js'
1348 )
1349 expect(
95c83464 1350 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1351 workerChoiceStrategy
b0d6ed8f 1352 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
1353 ).toBeInstanceOf(Array)
1354 expect(
1355 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1356 workerChoiceStrategy
b0d6ed8f 1357 ).workersVirtualTaskEndTimestamp.length
08f3f44c 1358 ).toBe(0)
2b4fddb8
JB
1359 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1360 workerChoiceStrategy
b0d6ed8f 1361 ).workersVirtualTaskEndTimestamp[0] = performance.now()
2b4fddb8
JB
1362 expect(
1363 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1364 workerChoiceStrategy
b0d6ed8f 1365 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1366 ).toBe(1)
594bfb84 1367 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
08f3f44c
JB
1368 expect(
1369 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1370 workerChoiceStrategy
b0d6ed8f 1371 ).workersVirtualTaskEndTimestamp
08f3f44c 1372 ).toBeInstanceOf(Array)
08f3f44c
JB
1373 expect(
1374 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1375 workerChoiceStrategy
b0d6ed8f 1376 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1377 ).toBe(0)
f0829c53
JB
1378 await pool.destroy()
1379 pool = new DynamicThreadPool(
1380 min,
1381 max,
1382 './tests/worker-files/thread/testWorker.js'
1383 )
1384 expect(
95c83464 1385 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1386 workerChoiceStrategy
b0d6ed8f 1387 ).workersVirtualTaskEndTimestamp
08f3f44c 1388 ).toBeInstanceOf(Array)
2b4fddb8
JB
1389 expect(
1390 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1391 workerChoiceStrategy
b0d6ed8f 1392 ).workersVirtualTaskEndTimestamp.length
2b4fddb8 1393 ).toBe(0)
08f3f44c
JB
1394 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1395 workerChoiceStrategy
b0d6ed8f 1396 ).workersVirtualTaskEndTimestamp[0] = performance.now()
08f3f44c
JB
1397 expect(
1398 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1399 workerChoiceStrategy
b0d6ed8f 1400 ).workersVirtualTaskEndTimestamp.length
08f3f44c 1401 ).toBe(1)
594bfb84 1402 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
08f3f44c
JB
1403 expect(
1404 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1405 workerChoiceStrategy
b0d6ed8f 1406 ).workersVirtualTaskEndTimestamp
08f3f44c
JB
1407 ).toBeInstanceOf(Array)
1408 expect(
1409 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1410 workerChoiceStrategy
b0d6ed8f 1411 ).workersVirtualTaskEndTimestamp.length
08f3f44c 1412 ).toBe(0)
caeb9817
JB
1413 // We need to clean up the resources after our test
1414 await pool.destroy()
1415 })
1416
6c6afb84
JB
1417 it('Verify WEIGHTED_ROUND_ROBIN strategy default policy', async () => {
1418 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
1419 let pool = new FixedThreadPool(
1420 max,
1421 './tests/worker-files/thread/testWorker.js',
1422 { workerChoiceStrategy }
1423 )
1424 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1425 dynamicWorkerUsage: false,
1426 dynamicWorkerReady: true
6c6afb84
JB
1427 })
1428 await pool.destroy()
1429 pool = new DynamicThreadPool(
1430 min,
1431 max,
1432 './tests/worker-files/thread/testWorker.js',
1433 { workerChoiceStrategy }
1434 )
1435 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1436 dynamicWorkerUsage: false,
1437 dynamicWorkerReady: true
6c6afb84
JB
1438 })
1439 // We need to clean up the resources after our test
1440 await pool.destroy()
1441 })
1442
1443 it('Verify WEIGHTED_ROUND_ROBIN strategy default tasks statistics requirements', async () => {
594bfb84 1444 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
10fcfaf4
JB
1445 let pool = new FixedThreadPool(
1446 max,
d710242d 1447 './tests/worker-files/thread/testWorker.js',
594bfb84 1448 { workerChoiceStrategy }
10fcfaf4 1449 )
87de9ff5
JB
1450 expect(
1451 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1452 ).toStrictEqual({
932fc8be
JB
1453 runTime: {
1454 aggregate: true,
1455 average: true,
1456 median: false
1457 },
1458 waitTime: {
1459 aggregate: false,
1460 average: false,
1461 median: false
1462 },
5df69fab
JB
1463 elu: {
1464 aggregate: false,
1465 average: false,
1466 median: false
1467 }
86bf340d 1468 })
fd7ebd49 1469 await pool.destroy()
10fcfaf4
JB
1470 pool = new DynamicThreadPool(
1471 min,
1472 max,
d710242d 1473 './tests/worker-files/thread/testWorker.js',
594bfb84 1474 { workerChoiceStrategy }
10fcfaf4 1475 )
87de9ff5
JB
1476 expect(
1477 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1478 ).toStrictEqual({
932fc8be
JB
1479 runTime: {
1480 aggregate: true,
1481 average: true,
1482 median: false
1483 },
1484 waitTime: {
1485 aggregate: false,
1486 average: false,
1487 median: false
1488 },
5df69fab
JB
1489 elu: {
1490 aggregate: false,
1491 average: false,
1492 median: false
1493 }
86bf340d 1494 })
10fcfaf4
JB
1495 // We need to clean up the resources after our test
1496 await pool.destroy()
1497 })
1498
b3432a63 1499 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
b3432a63
JB
1500 const pool = new FixedThreadPool(
1501 max,
1502 './tests/worker-files/thread/testWorker.js',
1503 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1504 )
1505 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 1506 const promises = new Set()
a20f0ba5
JB
1507 const maxMultiplier = 2
1508 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1509 promises.add(pool.execute())
b3432a63 1510 }
e211bc18 1511 await Promise.all(promises)
138d29a8 1512 for (const workerNode of pool.workerNodes) {
465b2940 1513 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
1514 tasks: {
1515 executed: expect.any(Number),
1516 executing: 0,
1517 queued: 0,
df593701 1518 maxQueued: 0,
68cbdc84 1519 stolen: 0,
a4e07f72
JB
1520 failed: 0
1521 },
71514351 1522 runTime: expect.objectContaining({
a4e07f72 1523 history: expect.any(CircularArray)
71514351 1524 }),
a4e07f72 1525 waitTime: {
619f403b 1526 history: new CircularArray()
a4e07f72 1527 },
5df69fab
JB
1528 elu: {
1529 idle: {
619f403b 1530 history: new CircularArray()
5df69fab
JB
1531 },
1532 active: {
619f403b 1533 history: new CircularArray()
71514351 1534 }
5df69fab 1535 }
86bf340d 1536 })
465b2940
JB
1537 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1538 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
1539 max * maxMultiplier
1540 )
71514351
JB
1541 if (workerNode.usage.runTime.aggregate == null) {
1542 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1543 } else {
1544 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1545 }
1546 if (workerNode.usage.runTime.average == null) {
1547 expect(workerNode.usage.runTime.average).toBeUndefined()
1548 } else {
1549 expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
1550 }
138d29a8 1551 }
97a2abc3 1552 expect(
95c83464 1553 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1554 pool.workerChoiceStrategyContext.workerChoiceStrategy
08f3f44c
JB
1555 ).defaultWorkerWeight
1556 ).toBeGreaterThan(0)
1557 expect(
1558 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1559 pool.workerChoiceStrategyContext.workerChoiceStrategy
1560 ).workerVirtualTaskRunTime
1561 ).toBeGreaterThanOrEqual(0)
b3432a63
JB
1562 // We need to clean up the resources after our test
1563 await pool.destroy()
1564 })
1565
1566 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
b3432a63
JB
1567 const pool = new DynamicThreadPool(
1568 min,
1569 max,
1570 './tests/worker-files/thread/testWorker.js',
1571 { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
1572 )
1573 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 1574 const promises = new Set()
138d29a8 1575 const maxMultiplier = 2
5502c07c 1576 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1577 promises.add(pool.execute())
b3432a63 1578 }
e211bc18 1579 await Promise.all(promises)
138d29a8 1580 for (const workerNode of pool.workerNodes) {
465b2940 1581 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
1582 tasks: {
1583 executed: expect.any(Number),
1584 executing: 0,
1585 queued: 0,
df593701 1586 maxQueued: 0,
68cbdc84 1587 stolen: 0,
a4e07f72
JB
1588 failed: 0
1589 },
b1aae695 1590 runTime: expect.objectContaining({
a4e07f72 1591 history: expect.any(CircularArray)
b1aae695 1592 }),
a4e07f72 1593 waitTime: {
619f403b 1594 history: new CircularArray()
a4e07f72 1595 },
5df69fab
JB
1596 elu: {
1597 idle: {
619f403b 1598 history: new CircularArray()
5df69fab
JB
1599 },
1600 active: {
619f403b 1601 history: new CircularArray()
71514351 1602 }
5df69fab 1603 }
86bf340d 1604 })
465b2940
JB
1605 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1606 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
1607 max * maxMultiplier
1608 )
b1aae695
JB
1609 if (workerNode.usage.runTime.aggregate == null) {
1610 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1611 } else {
1612 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1613 }
1614 if (workerNode.usage.runTime.average == null) {
1615 expect(workerNode.usage.runTime.average).toBeUndefined()
1616 } else {
1617 expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
1618 }
138d29a8 1619 }
2b4fddb8
JB
1620 expect(
1621 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1622 pool.workerChoiceStrategyContext.workerChoiceStrategy
1623 ).defaultWorkerWeight
1624 ).toBeGreaterThan(0)
1625 expect(
1626 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1627 pool.workerChoiceStrategyContext.workerChoiceStrategy
1628 ).workerVirtualTaskRunTime
1629 ).toBeGreaterThanOrEqual(0)
b3432a63
JB
1630 // We need to clean up the resources after our test
1631 await pool.destroy()
1632 })
1633
9e775f96 1634 it('Verify WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool with median runtime statistic', async () => {
010d7020
JB
1635 const pool = new DynamicThreadPool(
1636 min,
1637 max,
1638 './tests/worker-files/thread/testWorker.js',
1639 {
1640 workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
1641 workerChoiceStrategyOptions: {
932fc8be 1642 runTime: { median: true }
010d7020
JB
1643 }
1644 }
1645 )
1646 // TODO: Create a better test to cover `WeightedRoundRobinWorkerChoiceStrategy#choose`
ee9f5295 1647 const promises = new Set()
010d7020
JB
1648 const maxMultiplier = 2
1649 for (let i = 0; i < max * maxMultiplier; i++) {
ee9f5295 1650 promises.add(pool.execute())
010d7020 1651 }
e211bc18 1652 await Promise.all(promises)
010d7020 1653 for (const workerNode of pool.workerNodes) {
465b2940 1654 expect(workerNode.usage).toStrictEqual({
a4e07f72
JB
1655 tasks: {
1656 executed: expect.any(Number),
1657 executing: 0,
1658 queued: 0,
df593701 1659 maxQueued: 0,
68cbdc84 1660 stolen: 0,
a4e07f72
JB
1661 failed: 0
1662 },
b1aae695 1663 runTime: expect.objectContaining({
a4e07f72 1664 history: expect.any(CircularArray)
b1aae695 1665 }),
a4e07f72 1666 waitTime: {
619f403b 1667 history: new CircularArray()
a4e07f72 1668 },
5df69fab
JB
1669 elu: {
1670 idle: {
619f403b 1671 history: new CircularArray()
5df69fab
JB
1672 },
1673 active: {
619f403b 1674 history: new CircularArray()
71514351 1675 }
5df69fab 1676 }
86bf340d 1677 })
465b2940
JB
1678 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1679 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
a4e07f72
JB
1680 max * maxMultiplier
1681 )
b1aae695
JB
1682 if (workerNode.usage.runTime.aggregate == null) {
1683 expect(workerNode.usage.runTime.aggregate).toBeUndefined()
1684 } else {
1685 expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
1686 }
1687 if (workerNode.usage.runTime.median == null) {
1688 expect(workerNode.usage.runTime.median).toBeUndefined()
1689 } else {
1690 expect(workerNode.usage.runTime.median).toBeGreaterThan(0)
1691 }
010d7020 1692 }
08f3f44c
JB
1693 expect(
1694 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1695 pool.workerChoiceStrategyContext.workerChoiceStrategy
1696 ).defaultWorkerWeight
1697 ).toBeGreaterThan(0)
1698 expect(
1699 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1700 pool.workerChoiceStrategyContext.workerChoiceStrategy
1701 ).workerVirtualTaskRunTime
1702 ).toBeGreaterThanOrEqual(0)
010d7020
JB
1703 // We need to clean up the resources after our test
1704 await pool.destroy()
1705 })
1706
a6f7f1b4 1707 it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
594bfb84 1708 const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
f0829c53 1709 let pool = new FixedThreadPool(
caeb9817
JB
1710 max,
1711 './tests/worker-files/thread/testWorker.js'
1712 )
38f6e859 1713 expect(
95c83464 1714 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1715 workerChoiceStrategy
9b106837 1716 ).nextWorkerNodeKey
b529c323 1717 ).toBeDefined()
38f6e859 1718 expect(
95c83464 1719 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1720 workerChoiceStrategy
b529c323
JB
1721 ).defaultWorkerWeight
1722 ).toBeDefined()
caeb9817 1723 expect(
95c83464 1724 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1725 workerChoiceStrategy
08f3f44c 1726 ).workerVirtualTaskRunTime
b529c323 1727 ).toBeDefined()
594bfb84 1728 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 1729 expect(
95c83464 1730 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1731 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1732 ).nextWorkerNodeKey
a6f7f1b4
JB
1733 ).toBe(0)
1734 expect(
95c83464 1735 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1736 pool.workerChoiceStrategyContext.workerChoiceStrategy
95c83464 1737 ).defaultWorkerWeight
a6f7f1b4 1738 ).toBeGreaterThan(0)
08f3f44c
JB
1739 expect(
1740 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1741 workerChoiceStrategy
1742 ).workerVirtualTaskRunTime
1743 ).toBe(0)
f0829c53
JB
1744 await pool.destroy()
1745 pool = new DynamicThreadPool(
1746 min,
1747 max,
1748 './tests/worker-files/thread/testWorker.js'
1749 )
38f6e859 1750 expect(
95c83464 1751 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1752 workerChoiceStrategy
9b106837 1753 ).nextWorkerNodeKey
b529c323 1754 ).toBeDefined()
38f6e859 1755 expect(
95c83464 1756 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1757 workerChoiceStrategy
b529c323
JB
1758 ).defaultWorkerWeight
1759 ).toBeDefined()
f0829c53 1760 expect(
95c83464 1761 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
594bfb84 1762 workerChoiceStrategy
08f3f44c 1763 ).workerVirtualTaskRunTime
b529c323 1764 ).toBeDefined()
594bfb84 1765 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
a6f7f1b4 1766 expect(
95c83464 1767 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1768 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1769 ).nextWorkerNodeKey
a6f7f1b4
JB
1770 ).toBe(0)
1771 expect(
95c83464 1772 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
d710242d 1773 pool.workerChoiceStrategyContext.workerChoiceStrategy
95c83464 1774 ).defaultWorkerWeight
a6f7f1b4 1775 ).toBeGreaterThan(0)
08f3f44c
JB
1776 expect(
1777 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1778 workerChoiceStrategy
1779 ).workerVirtualTaskRunTime
1780 ).toBe(0)
caeb9817
JB
1781 // We need to clean up the resources after our test
1782 await pool.destroy()
1783 })
1784
6c6afb84
JB
1785 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default policy', async () => {
1786 const workerChoiceStrategy =
1787 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1788 let pool = new FixedThreadPool(
1789 max,
1790 './tests/worker-files/thread/testWorker.js',
1791 { workerChoiceStrategy }
1792 )
1793 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1794 dynamicWorkerUsage: false,
1795 dynamicWorkerReady: true
6c6afb84
JB
1796 })
1797 await pool.destroy()
1798 pool = new DynamicThreadPool(
1799 min,
1800 max,
1801 './tests/worker-files/thread/testWorker.js',
1802 { workerChoiceStrategy }
1803 )
1804 expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
b1aae695
JB
1805 dynamicWorkerUsage: false,
1806 dynamicWorkerReady: true
6c6afb84
JB
1807 })
1808 // We need to clean up the resources after our test
1809 await pool.destroy()
1810 })
1811
1812 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy default tasks statistics requirements', async () => {
e52fb978
JB
1813 const workerChoiceStrategy =
1814 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1815 let pool = new FixedThreadPool(
1816 max,
1817 './tests/worker-files/thread/testWorker.js',
1818 { workerChoiceStrategy }
1819 )
87de9ff5
JB
1820 expect(
1821 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1822 ).toStrictEqual({
932fc8be 1823 runTime: {
619f403b
JB
1824 aggregate: true,
1825 average: true,
932fc8be
JB
1826 median: false
1827 },
1828 waitTime: {
1829 aggregate: false,
1830 average: false,
1831 median: false
1832 },
5df69fab
JB
1833 elu: {
1834 aggregate: false,
1835 average: false,
1836 median: false
1837 }
e52fb978
JB
1838 })
1839 await pool.destroy()
1840 pool = new DynamicThreadPool(
1841 min,
1842 max,
1843 './tests/worker-files/thread/testWorker.js',
1844 { workerChoiceStrategy }
1845 )
87de9ff5
JB
1846 expect(
1847 pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
1848 ).toStrictEqual({
932fc8be 1849 runTime: {
619f403b
JB
1850 aggregate: true,
1851 average: true,
932fc8be
JB
1852 median: false
1853 },
1854 waitTime: {
1855 aggregate: false,
1856 average: false,
1857 median: false
1858 },
5df69fab
JB
1859 elu: {
1860 aggregate: false,
1861 average: false,
1862 median: false
1863 }
e52fb978
JB
1864 })
1865 // We need to clean up the resources after our test
1866 await pool.destroy()
1867 })
1868
e62e7646
JB
1869 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a fixed pool', async () => {
1870 const pool = new FixedThreadPool(
1871 max,
1872 './tests/worker-files/thread/testWorker.js',
1873 {
1874 workerChoiceStrategy:
1875 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1876 }
1877 )
1878 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1879 const promises = new Set()
1880 const maxMultiplier = 2
1881 for (let i = 0; i < max * maxMultiplier; i++) {
1882 promises.add(pool.execute())
1883 }
1884 await Promise.all(promises)
1885 for (const workerNode of pool.workerNodes) {
465b2940 1886 expect(workerNode.usage).toStrictEqual({
a4e07f72 1887 tasks: {
619f403b 1888 executed: expect.any(Number),
a4e07f72
JB
1889 executing: 0,
1890 queued: 0,
df593701 1891 maxQueued: 0,
68cbdc84 1892 stolen: 0,
a4e07f72
JB
1893 failed: 0
1894 },
619f403b
JB
1895 runTime: expect.objectContaining({
1896 history: expect.any(CircularArray)
1897 }),
a4e07f72 1898 waitTime: {
4ba4c7f9 1899 history: new CircularArray()
a4e07f72 1900 },
5df69fab
JB
1901 elu: {
1902 idle: {
4ba4c7f9 1903 history: new CircularArray()
5df69fab
JB
1904 },
1905 active: {
4ba4c7f9 1906 history: new CircularArray()
71514351 1907 }
5df69fab 1908 }
e62e7646 1909 })
619f403b
JB
1910 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1911 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
1912 max * maxMultiplier
1913 )
e62e7646
JB
1914 }
1915 expect(
1916 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1917 pool.workerChoiceStrategyContext.workerChoiceStrategy
1918 ).defaultWorkerWeight
1919 ).toBeGreaterThan(0)
1920 expect(
1921 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1922 pool.workerChoiceStrategyContext.workerChoiceStrategy
d33be430 1923 ).roundId
e62e7646 1924 ).toBe(0)
619f403b
JB
1925 expect(
1926 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1927 pool.workerChoiceStrategyContext.workerChoiceStrategy
1928 ).workerNodeId
1929 ).toBe(0)
e62e7646
JB
1930 expect(
1931 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1932 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 1933 ).nextWorkerNodeKey
e62e7646
JB
1934 ).toBe(0)
1935 expect(
1936 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1937 pool.workerChoiceStrategyContext.workerChoiceStrategy
1938 ).roundWeights
1939 ).toStrictEqual([
1940 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1941 pool.workerChoiceStrategyContext.workerChoiceStrategy
1942 ).defaultWorkerWeight
1943 ])
1944 // We need to clean up the resources after our test
1945 await pool.destroy()
1946 })
1947
1948 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy can be run in a dynamic pool', async () => {
1949 const pool = new DynamicThreadPool(
1950 min,
1951 max,
1952 './tests/worker-files/thread/testWorker.js',
1953 {
1954 workerChoiceStrategy:
1955 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
1956 }
1957 )
1958 // TODO: Create a better test to cover `InterleavedWeightedRoundRobinWorkerChoiceStrategy#choose`
1959 const promises = new Set()
1960 const maxMultiplier = 2
1961 for (let i = 0; i < max * maxMultiplier; i++) {
1962 promises.add(pool.execute())
1963 }
1964 await Promise.all(promises)
1965 for (const workerNode of pool.workerNodes) {
465b2940 1966 expect(workerNode.usage).toStrictEqual({
a4e07f72 1967 tasks: {
b1aae695 1968 executed: expect.any(Number),
a4e07f72
JB
1969 executing: 0,
1970 queued: 0,
df593701 1971 maxQueued: 0,
68cbdc84 1972 stolen: 0,
a4e07f72
JB
1973 failed: 0
1974 },
619f403b
JB
1975 runTime: expect.objectContaining({
1976 history: expect.any(CircularArray)
1977 }),
a4e07f72 1978 waitTime: {
4ba4c7f9 1979 history: new CircularArray()
a4e07f72 1980 },
5df69fab
JB
1981 elu: {
1982 idle: {
4ba4c7f9 1983 history: new CircularArray()
5df69fab
JB
1984 },
1985 active: {
4ba4c7f9 1986 history: new CircularArray()
71514351 1987 }
5df69fab 1988 }
e62e7646 1989 })
e43b8c2d
JB
1990 expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
1991 expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
1992 max * maxMultiplier
1993 )
e62e7646
JB
1994 }
1995 expect(
1996 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
1997 pool.workerChoiceStrategyContext.workerChoiceStrategy
1998 ).defaultWorkerWeight
1999 ).toBeGreaterThan(0)
2000 expect(
2001 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2002 pool.workerChoiceStrategyContext.workerChoiceStrategy
d33be430 2003 ).roundId
e62e7646 2004 ).toBe(0)
619f403b
JB
2005 expect(
2006 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2007 pool.workerChoiceStrategyContext.workerChoiceStrategy
2008 ).workerNodeId
2009 ).toBe(0)
e62e7646
JB
2010 expect(
2011 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2012 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 2013 ).nextWorkerNodeKey
d2c73f82 2014 ).toBe(0)
e62e7646
JB
2015 expect(
2016 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2017 pool.workerChoiceStrategyContext.workerChoiceStrategy
2018 ).roundWeights
2019 ).toStrictEqual([
2020 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2021 pool.workerChoiceStrategyContext.workerChoiceStrategy
2022 ).defaultWorkerWeight
2023 ])
2024 // We need to clean up the resources after our test
2025 await pool.destroy()
2026 })
2027
8c3ec188
JB
2028 it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
2029 const workerChoiceStrategy =
2030 WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
2031 let pool = new FixedThreadPool(
2032 max,
2033 './tests/worker-files/thread/testWorker.js'
2034 )
2035 expect(
2036 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2037 workerChoiceStrategy
d33be430 2038 ).roundId
8c3ec188
JB
2039 ).toBeDefined()
2040 expect(
2041 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2042 workerChoiceStrategy
9b106837 2043 ).nextWorkerNodeKey
8c3ec188
JB
2044 ).toBeDefined()
2045 expect(
2046 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2047 workerChoiceStrategy
2048 ).defaultWorkerWeight
2049 ).toBeDefined()
2050 expect(
2051 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2052 workerChoiceStrategy
2053 ).roundWeights
2054 ).toBeDefined()
2055 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
2056 expect(
2057 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2058 workerChoiceStrategy
d33be430 2059 ).roundId
8c3ec188
JB
2060 ).toBe(0)
2061 expect(
2062 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2063 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 2064 ).nextWorkerNodeKey
8c3ec188
JB
2065 ).toBe(0)
2066 expect(
2067 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2068 pool.workerChoiceStrategyContext.workerChoiceStrategy
2069 ).defaultWorkerWeight
2070 ).toBeGreaterThan(0)
2071 expect(
2072 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2073 workerChoiceStrategy
2074 ).roundWeights
2075 ).toStrictEqual([
2076 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2077 pool.workerChoiceStrategyContext.workerChoiceStrategy
2078 ).defaultWorkerWeight
2079 ])
2080 await pool.destroy()
2081 pool = new DynamicThreadPool(
2082 min,
2083 max,
2084 './tests/worker-files/thread/testWorker.js'
2085 )
2086 expect(
2087 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2088 workerChoiceStrategy
d33be430 2089 ).roundId
8c3ec188
JB
2090 ).toBeDefined()
2091 expect(
2092 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2093 workerChoiceStrategy
9b106837 2094 ).nextWorkerNodeKey
8c3ec188
JB
2095 ).toBeDefined()
2096 expect(
2097 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2098 workerChoiceStrategy
2099 ).defaultWorkerWeight
2100 ).toBeDefined()
2101 expect(
2102 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2103 workerChoiceStrategy
2104 ).roundWeights
2105 ).toBeDefined()
2106 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
2107 expect(
2108 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2109 pool.workerChoiceStrategyContext.workerChoiceStrategy
9b106837 2110 ).nextWorkerNodeKey
8c3ec188
JB
2111 ).toBe(0)
2112 expect(
2113 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2114 pool.workerChoiceStrategyContext.workerChoiceStrategy
2115 ).defaultWorkerWeight
2116 ).toBeGreaterThan(0)
2117 expect(
2118 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2119 workerChoiceStrategy
2120 ).roundWeights
2121 ).toStrictEqual([
2122 pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
2123 pool.workerChoiceStrategyContext.workerChoiceStrategy
2124 ).defaultWorkerWeight
2125 ])
2126 // We need to clean up the resources after our test
2127 await pool.destroy()
2128 })
2129
89b09b26 2130 it('Verify unknown strategy throw error', () => {
a35560ba
S
2131 expect(
2132 () =>
2133 new DynamicThreadPool(
2134 min,
2135 max,
2136 './tests/worker-files/thread/testWorker.js',
1927ee67 2137 { workerChoiceStrategy: 'UNKNOWN_STRATEGY' }
a35560ba 2138 )
d4aeae5a 2139 ).toThrowError("Invalid worker choice strategy 'UNKNOWN_STRATEGY'")
a35560ba
S
2140 })
2141})