Commit | Line | Data |
---|---|---|
c3719753 | 1 | import { MessageChannel } from 'node:worker_threads' |
a074ffee JB |
2 | import { expect } from 'expect' |
3 | import { WorkerNode } from '../../lib/pools/worker-node.js' | |
4 | import { WorkerTypes } from '../../lib/index.js' | |
5 | import { CircularArray } from '../../lib/circular-array.js' | |
6 | import { Deque } from '../../lib/deque.js' | |
7 | import { DEFAULT_TASK_NAME } from '../../lib/utils.js' | |
26fb3c18 JB |
8 | |
9 | describe('Worker node test suite', () => { | |
c3719753 JB |
10 | const threadWorkerNode = new WorkerNode( |
11 | WorkerTypes.thread, | |
12 | './tests/worker-files/thread/testWorker.mjs', | |
13 | { tasksQueueBackPressureSize: 12 } | |
14 | ) | |
15 | const clusterWorkerNode = new WorkerNode( | |
16 | WorkerTypes.cluster, | |
17 | './tests/worker-files/cluster/testWorker.js', | |
18 | { tasksQueueBackPressureSize: 12 } | |
19 | ) | |
26fb3c18 JB |
20 | |
21 | it('Worker node instantiation', () => { | |
948faff7 | 22 | expect(() => new WorkerNode()).toThrow( |
c3719753 | 23 | new TypeError('Cannot construct a worker node without a worker type') |
26fb3c18 | 24 | ) |
c3719753 JB |
25 | expect( |
26 | () => | |
27 | new WorkerNode( | |
28 | 'invalidWorkerType', | |
29 | './tests/worker-files/thread/testWorker.mjs', | |
30 | { tasksQueueBackPressureSize: 12 } | |
31 | ) | |
32 | ).toThrow( | |
33 | new TypeError( | |
34 | "Cannot construct a worker node with an invalid worker type 'invalidWorkerType'" | |
35 | ) | |
36 | ) | |
37 | expect( | |
38 | () => | |
39 | new WorkerNode( | |
40 | WorkerTypes.thread, | |
41 | './tests/worker-files/thread/testWorker.mjs' | |
42 | ) | |
43 | ).toThrow( | |
26fb3c18 | 44 | new TypeError( |
c3719753 | 45 | 'Cannot construct a worker node without worker node options' |
26fb3c18 JB |
46 | ) |
47 | ) | |
48 | expect( | |
c3719753 JB |
49 | () => |
50 | new WorkerNode( | |
51 | WorkerTypes.thread, | |
52 | './tests/worker-files/thread/testWorker.mjs', | |
53 | '' | |
54 | ) | |
948faff7 | 55 | ).toThrow( |
26fb3c18 | 56 | new TypeError( |
c3719753 | 57 | 'Cannot construct a worker node with invalid options: must be a plain object' |
26fb3c18 JB |
58 | ) |
59 | ) | |
c3719753 JB |
60 | expect( |
61 | () => | |
62 | new WorkerNode( | |
63 | WorkerTypes.thread, | |
64 | './tests/worker-files/thread/testWorker.mjs', | |
65 | {} | |
66 | ) | |
67 | ).toThrow( | |
5b49e864 | 68 | new TypeError( |
c3719753 | 69 | 'Cannot construct a worker node without a tasks queue back pressure size option' |
5b49e864 JB |
70 | ) |
71 | ) | |
c3719753 JB |
72 | expect( |
73 | () => | |
74 | new WorkerNode( | |
75 | WorkerTypes.thread, | |
76 | './tests/worker-files/thread/testWorker.mjs', | |
77 | { tasksQueueBackPressureSize: 'invalidTasksQueueBackPressureSize' } | |
78 | ) | |
79 | ).toThrow( | |
80 | new TypeError( | |
81 | 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer' | |
82 | ) | |
83 | ) | |
84 | expect( | |
85 | () => | |
86 | new WorkerNode( | |
87 | WorkerTypes.thread, | |
88 | './tests/worker-files/thread/testWorker.mjs', | |
89 | { tasksQueueBackPressureSize: 0.2 } | |
90 | ) | |
91 | ).toThrow( | |
92 | new TypeError( | |
93 | 'Cannot construct a worker node with a tasks queue back pressure size option that is not an integer' | |
94 | ) | |
95 | ) | |
96 | expect( | |
97 | () => | |
98 | new WorkerNode( | |
99 | WorkerTypes.thread, | |
100 | './tests/worker-files/thread/testWorker.mjs', | |
101 | { tasksQueueBackPressureSize: 0 } | |
102 | ) | |
103 | ).toThrow( | |
5b49e864 | 104 | new RangeError( |
c3719753 | 105 | 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer' |
5b49e864 JB |
106 | ) |
107 | ) | |
c3719753 JB |
108 | expect( |
109 | () => | |
110 | new WorkerNode( | |
111 | WorkerTypes.thread, | |
112 | './tests/worker-files/thread/testWorker.mjs', | |
113 | { tasksQueueBackPressureSize: -1 } | |
114 | ) | |
115 | ).toThrow( | |
5b49e864 | 116 | new RangeError( |
c3719753 | 117 | 'Cannot construct a worker node with a tasks queue back pressure size option that is not a positive integer' |
5b49e864 JB |
118 | ) |
119 | ) | |
75de9f41 | 120 | expect(threadWorkerNode).toBeInstanceOf(WorkerNode) |
75de9f41 | 121 | expect(threadWorkerNode.info).toStrictEqual({ |
c3719753 | 122 | id: threadWorkerNode.worker.threadId, |
26fb3c18 JB |
123 | type: WorkerTypes.thread, |
124 | dynamic: false, | |
125 | ready: false | |
126 | }) | |
75de9f41 JB |
127 | expect(threadWorkerNode.usage).toStrictEqual({ |
128 | tasks: { | |
129 | executed: 0, | |
130 | executing: 0, | |
131 | queued: 0, | |
132 | maxQueued: 0, | |
463226a4 | 133 | sequentiallyStolen: 0, |
75de9f41 JB |
134 | stolen: 0, |
135 | failed: 0 | |
136 | }, | |
137 | runTime: { | |
4ba4c7f9 | 138 | history: new CircularArray() |
75de9f41 JB |
139 | }, |
140 | waitTime: { | |
4ba4c7f9 | 141 | history: new CircularArray() |
75de9f41 JB |
142 | }, |
143 | elu: { | |
144 | idle: { | |
4ba4c7f9 | 145 | history: new CircularArray() |
75de9f41 JB |
146 | }, |
147 | active: { | |
4ba4c7f9 | 148 | history: new CircularArray() |
75de9f41 JB |
149 | } |
150 | } | |
151 | }) | |
152 | expect(threadWorkerNode.messageChannel).toBeInstanceOf(MessageChannel) | |
153 | expect(threadWorkerNode.tasksQueueBackPressureSize).toBe(12) | |
154 | expect(threadWorkerNode.tasksQueue).toBeInstanceOf(Deque) | |
155 | expect(threadWorkerNode.tasksQueue.size).toBe(0) | |
68f1f531 JB |
156 | expect(threadWorkerNode.tasksQueueSize()).toBe( |
157 | threadWorkerNode.tasksQueue.size | |
158 | ) | |
159 | expect(threadWorkerNode.onBackPressureStarted).toBe(false) | |
75de9f41 JB |
160 | expect(threadWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map) |
161 | ||
162 | expect(clusterWorkerNode).toBeInstanceOf(WorkerNode) | |
75de9f41 | 163 | expect(clusterWorkerNode.info).toStrictEqual({ |
c3719753 | 164 | id: clusterWorkerNode.worker.id, |
75de9f41 JB |
165 | type: WorkerTypes.cluster, |
166 | dynamic: false, | |
167 | ready: false | |
168 | }) | |
169 | expect(clusterWorkerNode.usage).toStrictEqual({ | |
26fb3c18 JB |
170 | tasks: { |
171 | executed: 0, | |
172 | executing: 0, | |
173 | queued: 0, | |
174 | maxQueued: 0, | |
463226a4 | 175 | sequentiallyStolen: 0, |
26fb3c18 JB |
176 | stolen: 0, |
177 | failed: 0 | |
178 | }, | |
179 | runTime: { | |
4ba4c7f9 | 180 | history: new CircularArray() |
26fb3c18 JB |
181 | }, |
182 | waitTime: { | |
4ba4c7f9 | 183 | history: new CircularArray() |
26fb3c18 JB |
184 | }, |
185 | elu: { | |
186 | idle: { | |
4ba4c7f9 | 187 | history: new CircularArray() |
26fb3c18 JB |
188 | }, |
189 | active: { | |
4ba4c7f9 | 190 | history: new CircularArray() |
26fb3c18 JB |
191 | } |
192 | } | |
193 | }) | |
75de9f41 JB |
194 | expect(clusterWorkerNode.messageChannel).toBeUndefined() |
195 | expect(clusterWorkerNode.tasksQueueBackPressureSize).toBe(12) | |
196 | expect(clusterWorkerNode.tasksQueue).toBeInstanceOf(Deque) | |
197 | expect(clusterWorkerNode.tasksQueue.size).toBe(0) | |
68f1f531 JB |
198 | expect(clusterWorkerNode.tasksQueueSize()).toBe( |
199 | clusterWorkerNode.tasksQueue.size | |
200 | ) | |
201 | expect(clusterWorkerNode.onBackPressureStarted).toBe(false) | |
75de9f41 | 202 | expect(clusterWorkerNode.taskFunctionsUsage).toBeInstanceOf(Map) |
26fb3c18 JB |
203 | }) |
204 | ||
205 | it('Worker node getTaskFunctionWorkerUsage()', () => { | |
206 | expect(() => | |
75de9f41 | 207 | threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction') |
948faff7 | 208 | ).toThrow( |
26fb3c18 JB |
209 | new TypeError( |
210 | "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list is not yet defined" | |
211 | ) | |
212 | ) | |
66979634 | 213 | threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1'] |
26fb3c18 | 214 | expect(() => |
75de9f41 | 215 | threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction') |
948faff7 | 216 | ).toThrow( |
26fb3c18 JB |
217 | new TypeError( |
218 | "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list has less than 3 elements" | |
219 | ) | |
220 | ) | |
66979634 | 221 | threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1', 'fn2'] |
6cd5248f | 222 | expect( |
75de9f41 | 223 | threadWorkerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME) |
6cd5248f | 224 | ).toStrictEqual({ |
26fb3c18 JB |
225 | tasks: { |
226 | executed: 0, | |
227 | executing: 0, | |
228 | queued: 0, | |
463226a4 | 229 | sequentiallyStolen: 0, |
5ad42e34 | 230 | stolen: 0, |
26fb3c18 JB |
231 | failed: 0 |
232 | }, | |
233 | runTime: { | |
4ba4c7f9 | 234 | history: new CircularArray() |
26fb3c18 JB |
235 | }, |
236 | waitTime: { | |
4ba4c7f9 | 237 | history: new CircularArray() |
26fb3c18 JB |
238 | }, |
239 | elu: { | |
240 | idle: { | |
4ba4c7f9 | 241 | history: new CircularArray() |
26fb3c18 JB |
242 | }, |
243 | active: { | |
4ba4c7f9 | 244 | history: new CircularArray() |
26fb3c18 JB |
245 | } |
246 | } | |
247 | }) | |
75de9f41 | 248 | expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn1')).toStrictEqual({ |
26fb3c18 JB |
249 | tasks: { |
250 | executed: 0, | |
251 | executing: 0, | |
252 | queued: 0, | |
463226a4 | 253 | sequentiallyStolen: 0, |
5ad42e34 | 254 | stolen: 0, |
26fb3c18 JB |
255 | failed: 0 |
256 | }, | |
257 | runTime: { | |
4ba4c7f9 | 258 | history: new CircularArray() |
26fb3c18 JB |
259 | }, |
260 | waitTime: { | |
4ba4c7f9 | 261 | history: new CircularArray() |
26fb3c18 JB |
262 | }, |
263 | elu: { | |
264 | idle: { | |
4ba4c7f9 | 265 | history: new CircularArray() |
26fb3c18 JB |
266 | }, |
267 | active: { | |
4ba4c7f9 | 268 | history: new CircularArray() |
26fb3c18 JB |
269 | } |
270 | } | |
271 | }) | |
75de9f41 | 272 | expect(threadWorkerNode.getTaskFunctionWorkerUsage('fn2')).toStrictEqual({ |
26fb3c18 JB |
273 | tasks: { |
274 | executed: 0, | |
275 | executing: 0, | |
276 | queued: 0, | |
463226a4 | 277 | sequentiallyStolen: 0, |
5ad42e34 | 278 | stolen: 0, |
26fb3c18 JB |
279 | failed: 0 |
280 | }, | |
281 | runTime: { | |
4ba4c7f9 | 282 | history: new CircularArray() |
26fb3c18 JB |
283 | }, |
284 | waitTime: { | |
4ba4c7f9 | 285 | history: new CircularArray() |
26fb3c18 JB |
286 | }, |
287 | elu: { | |
288 | idle: { | |
4ba4c7f9 | 289 | history: new CircularArray() |
26fb3c18 JB |
290 | }, |
291 | active: { | |
4ba4c7f9 | 292 | history: new CircularArray() |
26fb3c18 JB |
293 | } |
294 | } | |
295 | }) | |
75de9f41 | 296 | expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2) |
26fb3c18 | 297 | }) |
adee6053 JB |
298 | |
299 | it('Worker node deleteTaskFunctionWorkerUsage()', () => { | |
300 | expect(threadWorkerNode.info.taskFunctionNames).toStrictEqual([ | |
301 | DEFAULT_TASK_NAME, | |
302 | 'fn1', | |
303 | 'fn2' | |
304 | ]) | |
305 | expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2) | |
306 | expect( | |
307 | threadWorkerNode.deleteTaskFunctionWorkerUsage('invalidTaskFunction') | |
308 | ).toBe(false) | |
309 | expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2) | |
310 | expect(threadWorkerNode.deleteTaskFunctionWorkerUsage('fn1')).toBe(true) | |
311 | expect(threadWorkerNode.taskFunctionsUsage.size).toBe(1) | |
312 | }) | |
26fb3c18 | 313 | }) |