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