testeranto 0.47.11 → 0.47.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/Features.js +85 -0
- package/dist/common/Node.js +82 -0
- package/dist/common/NodeWriter.js +56 -0
- package/dist/common/Project.js +648 -0
- package/dist/common/Types.js +2 -0
- package/dist/common/Web.js +70 -0
- package/dist/common/core.js +392 -0
- package/dist/common/electron.js +40 -0
- package/dist/common/preload.js +8 -0
- package/dist/common/subPackages/react/component.js +2 -0
- package/dist/common/subPackages/react/node.js +57 -0
- package/dist/common/subPackages/react/web.js +57 -0
- package/dist/common/subPackages/react-test-render/component.js +2 -0
- package/dist/common/subPackages/react-test-render/node.js +46 -0
- package/dist/common/subPackages/react-test-render/web.js +46 -0
- package/dist/common/tsconfig.common.tsbuildinfo +1 -0
- package/dist/module/Features.js +74 -0
- package/dist/module/Node.js +77 -0
- package/dist/module/NodeWriter.js +50 -0
- package/dist/module/Project.js +618 -0
- package/dist/module/Report.js +186 -0
- package/dist/module/Types.js +1 -0
- package/dist/module/Web.js +65 -0
- package/dist/module/core.js +383 -0
- package/dist/module/electron.js +35 -0
- package/dist/module/preload.js +6 -0
- package/dist/module/subPackages/react/component.js +1 -0
- package/dist/module/subPackages/react/node.js +52 -0
- package/dist/module/subPackages/react/web.js +52 -0
- package/dist/module/subPackages/react-test-render/component.js +1 -0
- package/dist/module/subPackages/react-test-render/node.js +16 -0
- package/dist/module/subPackages/react-test-render/web.js +16 -0
- package/dist/module/tsconfig.module.tsbuildinfo +1 -0
- package/dist/types/Features.d.ts +68 -0
- package/dist/types/Node.d.ts +12 -0
- package/dist/types/NodeWriter.d.ts +2 -0
- package/dist/types/Project.d.ts +32 -0
- package/dist/types/Types.d.ts +17 -0
- package/dist/types/Web.d.ts +12 -0
- package/dist/types/core.d.ts +219 -0
- package/dist/types/electron.d.ts +1 -0
- package/dist/types/preload.d.ts +1 -0
- package/dist/types/subPackages/react/component.d.ts +17 -0
- package/dist/types/subPackages/react/node.d.ts +4 -0
- package/dist/types/subPackages/react/web.d.ts +4 -0
- package/dist/types/subPackages/react-test-render/component.d.ts +17 -0
- package/dist/types/subPackages/react-test-render/node.d.ts +4 -0
- package/dist/types/subPackages/react-test-render/web.d.ts +4 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -0
- package/package.json +9 -5
- package/src/subPackages/react/component.ts +1 -0
- package/src/subPackages/react-test-render/component.ts +21 -0
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
import { WebSocketServer } from 'ws';
|
|
2
|
+
import esbuild from "esbuild";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fsExists from "fs.promises.exists";
|
|
6
|
+
import pm2 from "pm2";
|
|
7
|
+
import readline from 'readline';
|
|
8
|
+
readline.emitKeypressEvents(process.stdin);
|
|
9
|
+
if (process.stdin.isTTY)
|
|
10
|
+
process.stdin.setRawMode(true);
|
|
11
|
+
const TIMEOUT = 2000;
|
|
12
|
+
const OPEN_PORT = "";
|
|
13
|
+
let webSocketServer;
|
|
14
|
+
const getRunnables = (tests, payload = [new Set(), new Set()]) => {
|
|
15
|
+
return tests.reduce((pt, cv, cndx, cry) => {
|
|
16
|
+
if (cv[1] === "node") {
|
|
17
|
+
pt[0].add(cv[0]);
|
|
18
|
+
}
|
|
19
|
+
else if (cv[1] === "web") {
|
|
20
|
+
pt[1].add(cv[0]);
|
|
21
|
+
}
|
|
22
|
+
if (cv[2].length) {
|
|
23
|
+
getRunnables(cv[2], payload);
|
|
24
|
+
}
|
|
25
|
+
return pt;
|
|
26
|
+
}, payload);
|
|
27
|
+
};
|
|
28
|
+
export class ITProject {
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.exitCodes = {};
|
|
31
|
+
this.mode = `up`;
|
|
32
|
+
this.ports = {};
|
|
33
|
+
this.websockets = {};
|
|
34
|
+
this.resourceQueue = [];
|
|
35
|
+
this.spinCycle = 0;
|
|
36
|
+
this.spinAnimation = "←↖↑↗→↘↓↙";
|
|
37
|
+
this.mainLoop = async () => {
|
|
38
|
+
if (this.clearScreen) {
|
|
39
|
+
console.clear();
|
|
40
|
+
}
|
|
41
|
+
const procsTable = [];
|
|
42
|
+
pm2.list((err, procs) => {
|
|
43
|
+
procs.forEach((proc) => {
|
|
44
|
+
var _a, _b;
|
|
45
|
+
procsTable.push({
|
|
46
|
+
name: proc.name,
|
|
47
|
+
pid: proc.pid,
|
|
48
|
+
pm_id: proc.pm_id,
|
|
49
|
+
mem: (_a = proc.monit) === null || _a === void 0 ? void 0 : _a.memory,
|
|
50
|
+
cpu: (_b = proc.monit) === null || _b === void 0 ? void 0 : _b.cpu,
|
|
51
|
+
"exit code": this.exitCodes[proc.name]
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
console.table(procsTable);
|
|
55
|
+
console.table(this.resourceQueue);
|
|
56
|
+
// console.log("webSocketServer.clients", webSocketServer.clients.size);
|
|
57
|
+
// console.log("resourceQueue", this.resourceQueue);
|
|
58
|
+
const resourceRequest = this.resourceQueue.pop();
|
|
59
|
+
if (!resourceRequest) {
|
|
60
|
+
if (!this.devMode && this.mode === "up") {
|
|
61
|
+
this.initiateShutdown("resource request queue is empty");
|
|
62
|
+
}
|
|
63
|
+
if (this.mode === "down" && procsTable.every((p) => p.pid === 0 || p.pid === undefined)) {
|
|
64
|
+
this.shutdown();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.log("handling", resourceRequest);
|
|
69
|
+
if (resourceRequest.protocol === "ipc") {
|
|
70
|
+
this.allocateViaIpc(resourceRequest);
|
|
71
|
+
}
|
|
72
|
+
else if (resourceRequest.protocol === "ws") {
|
|
73
|
+
this.allocateViaWs(resourceRequest);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (this.devMode) {
|
|
77
|
+
if (this.mode === "up") {
|
|
78
|
+
console.log(this.spinner(), "Running tests while watching for changes. Use 'q' to initiate shutdown");
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.log(this.spinner(), "Shutdown is in progress. Please wait.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
if (this.mode === "up") {
|
|
86
|
+
console.log(this.spinner(), "Running tests without watching for changes. Use 'q' to initiate shutdown");
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(this.spinner(), "Shutdown is in progress. Please wait.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// console.log(this.spinner());
|
|
93
|
+
// console.log(
|
|
94
|
+
// this.spinner(),
|
|
95
|
+
// this.mode === `up`
|
|
96
|
+
// ? `press "q" to initiate graceful shutdown`
|
|
97
|
+
// : `please wait while testeranto shuts down gracefully...`
|
|
98
|
+
// );
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
this.clearScreen = config.clearScreen;
|
|
102
|
+
this.devMode = config.devMode;
|
|
103
|
+
Object.values(config.ports).forEach((port) => {
|
|
104
|
+
this.ports[port] = OPEN_PORT;
|
|
105
|
+
});
|
|
106
|
+
const testPath = `${process.cwd()}/${config.tests}`;
|
|
107
|
+
const featurePath = `${process.cwd()}/${config.features}`;
|
|
108
|
+
process.on('SIGINT', () => this.initiateShutdown("CTRL+C"));
|
|
109
|
+
process.on('SIGQUIT', () => this.initiateShutdown("Keyboard quit"));
|
|
110
|
+
process.on('SIGTERM', () => this.initiateShutdown("'kill' command"));
|
|
111
|
+
process.stdin.on('keypress', (str, key) => {
|
|
112
|
+
if (key.name === 'q') {
|
|
113
|
+
this.initiateShutdown("'q' command");
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
import(testPath).then((tests) => {
|
|
117
|
+
this.tests = tests.default;
|
|
118
|
+
import(featurePath).then((features) => {
|
|
119
|
+
this.features = features.default;
|
|
120
|
+
Promise.resolve(Promise.all([
|
|
121
|
+
...this.getSecondaryEndpointsPoints("web")
|
|
122
|
+
]
|
|
123
|
+
.map(async (sourceFilePath) => {
|
|
124
|
+
const sourceFileSplit = sourceFilePath.split("/");
|
|
125
|
+
const sourceDir = sourceFileSplit.slice(0, -1);
|
|
126
|
+
const sourceFileName = sourceFileSplit[sourceFileSplit.length - 1];
|
|
127
|
+
const sourceFileNameMinusJs = sourceFileName.split(".").slice(0, -1).join(".");
|
|
128
|
+
const htmlFilePath = path.normalize(`${process.cwd()}/${config.outdir}/${sourceDir.join("/")}/${sourceFileNameMinusJs}.html`);
|
|
129
|
+
const jsfilePath = `./${sourceFileNameMinusJs}.mjs`;
|
|
130
|
+
return fs.promises.mkdir(path.dirname(htmlFilePath), { recursive: true }).then(x => fs.writeFileSync(htmlFilePath, `
|
|
131
|
+
<!DOCTYPE html>
|
|
132
|
+
<html lang="en">
|
|
133
|
+
<head>
|
|
134
|
+
<script type="module" src="${jsfilePath}"></script>
|
|
135
|
+
</head>
|
|
136
|
+
|
|
137
|
+
<body>
|
|
138
|
+
<h1>${htmlFilePath}</h1>
|
|
139
|
+
<div id="root">
|
|
140
|
+
|
|
141
|
+
</div>
|
|
142
|
+
</body>
|
|
143
|
+
|
|
144
|
+
<footer></footer>
|
|
145
|
+
|
|
146
|
+
</html>
|
|
147
|
+
`));
|
|
148
|
+
})));
|
|
149
|
+
const [nodeEntryPoints, webEntryPoints] = getRunnables(this.tests);
|
|
150
|
+
const esbuildConfigNode = {
|
|
151
|
+
define: {
|
|
152
|
+
"process.env.FLUENTFFMPEG_COV": "0"
|
|
153
|
+
},
|
|
154
|
+
absWorkingDir: process.cwd(),
|
|
155
|
+
banner: {
|
|
156
|
+
js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`
|
|
157
|
+
},
|
|
158
|
+
target: "esnext",
|
|
159
|
+
// packages: "external",
|
|
160
|
+
format: "esm",
|
|
161
|
+
splitting: true,
|
|
162
|
+
outExtension: { '.js': '.mjs' },
|
|
163
|
+
platform: "node",
|
|
164
|
+
external: ["tests.test.js", "features.test.js", "react"],
|
|
165
|
+
outbase: config.outbase,
|
|
166
|
+
outdir: config.outdir,
|
|
167
|
+
jsx: 'transform',
|
|
168
|
+
entryPoints: [...nodeEntryPoints],
|
|
169
|
+
bundle: true,
|
|
170
|
+
minify: config.minify === true,
|
|
171
|
+
write: true,
|
|
172
|
+
loader: {
|
|
173
|
+
'.js': 'jsx',
|
|
174
|
+
'.png': 'binary',
|
|
175
|
+
'.jpg': 'binary',
|
|
176
|
+
},
|
|
177
|
+
plugins: [
|
|
178
|
+
...(config.plugins || []),
|
|
179
|
+
{
|
|
180
|
+
name: 'rebuild-notify',
|
|
181
|
+
setup(build) {
|
|
182
|
+
build.onEnd(result => {
|
|
183
|
+
console.log(`node build ended with ${result.errors.length} errors`);
|
|
184
|
+
result.errors.length !== 0 && process.exit(-1);
|
|
185
|
+
// HERE: somehow restart the server from here, e.g., by sending a signal that you trap and react to inside the server.
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
};
|
|
191
|
+
const esbuildConfigWeb = {
|
|
192
|
+
// packages: "external",
|
|
193
|
+
target: "esnext",
|
|
194
|
+
format: "esm",
|
|
195
|
+
splitting: true,
|
|
196
|
+
outExtension: { '.js': '.mjs' },
|
|
197
|
+
alias: {
|
|
198
|
+
react: path.resolve("./node_modules/react")
|
|
199
|
+
},
|
|
200
|
+
external: [
|
|
201
|
+
// "url",
|
|
202
|
+
"electron",
|
|
203
|
+
"path",
|
|
204
|
+
"fs",
|
|
205
|
+
// "react",
|
|
206
|
+
"stream",
|
|
207
|
+
"tests.test.js", "features.test.js"
|
|
208
|
+
],
|
|
209
|
+
platform: "browser",
|
|
210
|
+
outbase: config.outbase,
|
|
211
|
+
outdir: config.outdir,
|
|
212
|
+
jsx: 'transform',
|
|
213
|
+
entryPoints: [
|
|
214
|
+
...webEntryPoints,
|
|
215
|
+
testPath,
|
|
216
|
+
featurePath,
|
|
217
|
+
],
|
|
218
|
+
bundle: true,
|
|
219
|
+
minify: config.minify === true,
|
|
220
|
+
write: true,
|
|
221
|
+
loader: {
|
|
222
|
+
'.js': 'jsx',
|
|
223
|
+
'.png': 'binary',
|
|
224
|
+
'.jpg': 'binary',
|
|
225
|
+
},
|
|
226
|
+
plugins: [
|
|
227
|
+
...(config.plugins || []),
|
|
228
|
+
{
|
|
229
|
+
name: 'rebuild-notify',
|
|
230
|
+
setup(build) {
|
|
231
|
+
build.onEnd(result => {
|
|
232
|
+
console.log(`web build ended with ${result.errors.length} errors`);
|
|
233
|
+
result.errors.length !== 0 && process.exit(-1);
|
|
234
|
+
// HERE: somehow restart the server from here, e.g., by sending a signal that you trap and react to inside the server.
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
};
|
|
240
|
+
esbuild.build({
|
|
241
|
+
bundle: true,
|
|
242
|
+
entryPoints: ["./node_modules/testeranto/dist/module/Report.js"],
|
|
243
|
+
minify: config.minify === true,
|
|
244
|
+
outbase: config.outbase,
|
|
245
|
+
write: true,
|
|
246
|
+
outfile: `${config.outdir}/Report.js`,
|
|
247
|
+
external: ["tests.test.js", "features.test.js"]
|
|
248
|
+
});
|
|
249
|
+
fs.writeFileSync(`${config.outdir}/report.html`, `
|
|
250
|
+
<!DOCTYPE html>
|
|
251
|
+
<html lang="en">
|
|
252
|
+
|
|
253
|
+
<head>
|
|
254
|
+
<meta name="description" content="Webpage description goes here" />
|
|
255
|
+
<meta charset="utf-8" />
|
|
256
|
+
<title>kokomoBay - testeranto</title>
|
|
257
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
258
|
+
<meta name="author" content="" />
|
|
259
|
+
<link rel="stylesheet" href="./Report.css" />
|
|
260
|
+
|
|
261
|
+
<script type="importmap">
|
|
262
|
+
{
|
|
263
|
+
"imports": {
|
|
264
|
+
"tests.test.js": "./tests.test.js",
|
|
265
|
+
"features.test.js": "./features.test.js"
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
</script>
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
<script src="./Report.js"></script>
|
|
272
|
+
</head>
|
|
273
|
+
|
|
274
|
+
<body>
|
|
275
|
+
<div id="root">
|
|
276
|
+
react is loading
|
|
277
|
+
</div>
|
|
278
|
+
</body>
|
|
279
|
+
|
|
280
|
+
</html>
|
|
281
|
+
`);
|
|
282
|
+
Promise.all([
|
|
283
|
+
esbuild.context(esbuildConfigNode).then(async (nodeContext) => {
|
|
284
|
+
await nodeContext.watch();
|
|
285
|
+
}),
|
|
286
|
+
esbuild.context(esbuildConfigWeb).then(async (esbuildWeb) => {
|
|
287
|
+
await esbuildWeb.watch();
|
|
288
|
+
})
|
|
289
|
+
]);
|
|
290
|
+
pm2.connect(async (err) => {
|
|
291
|
+
if (err) {
|
|
292
|
+
console.error(err);
|
|
293
|
+
process.exit(-1);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
console.log(`pm2 is connected`);
|
|
297
|
+
}
|
|
298
|
+
// run a websocket as an alternative to node IPC
|
|
299
|
+
webSocketServer = new WebSocketServer({
|
|
300
|
+
port: 8080,
|
|
301
|
+
host: "localhost",
|
|
302
|
+
});
|
|
303
|
+
webSocketServer.on('open', () => {
|
|
304
|
+
console.log('open');
|
|
305
|
+
// process.exit()
|
|
306
|
+
});
|
|
307
|
+
webSocketServer.on('close', (data) => {
|
|
308
|
+
console.log('webSocketServer close: %s', data);
|
|
309
|
+
// process.exit()
|
|
310
|
+
});
|
|
311
|
+
webSocketServer.on('listening', () => {
|
|
312
|
+
console.log("webSocketServer listening", webSocketServer.address());
|
|
313
|
+
// process.exit()
|
|
314
|
+
});
|
|
315
|
+
webSocketServer.on('connection', (webSocket) => {
|
|
316
|
+
console.log('webSocketServer connection');
|
|
317
|
+
webSocket.on('message', (webSocketData) => {
|
|
318
|
+
console.log('webSocket message: %s', webSocketData);
|
|
319
|
+
const payload = webSocketData.valueOf();
|
|
320
|
+
const name = payload.data.name;
|
|
321
|
+
const messageType = payload.type;
|
|
322
|
+
const requestedResources = payload.data;
|
|
323
|
+
this.websockets[name] = webSocket;
|
|
324
|
+
console.log('connected: ' + name + ' in ' + Object.getOwnPropertyNames(this.websockets));
|
|
325
|
+
if (messageType === "testeranto:hola") {
|
|
326
|
+
console.log("hola WS", requestedResources);
|
|
327
|
+
this.requestResource(requestedResources, 'ws');
|
|
328
|
+
}
|
|
329
|
+
else if (messageType === "testeranto:adios") {
|
|
330
|
+
console.log("adios WS", name);
|
|
331
|
+
this.releaseTestResources(payload);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
const makePath = (fPath, rt) => {
|
|
336
|
+
return path.resolve("./" + config.outdir + "/" + fPath.replace(path.extname(fPath), "") + ".mjs");
|
|
337
|
+
};
|
|
338
|
+
const bootInterval = setInterval(async () => {
|
|
339
|
+
const filesToLookup = this.tests
|
|
340
|
+
.map(([p, rt]) => {
|
|
341
|
+
const filepath = makePath(p, rt);
|
|
342
|
+
return {
|
|
343
|
+
filepath,
|
|
344
|
+
exists: fsExists(filepath),
|
|
345
|
+
};
|
|
346
|
+
});
|
|
347
|
+
const allFilesExist = (await Promise.all(filesToLookup.map((f) => f.exists))).every((b) => b);
|
|
348
|
+
if (!allFilesExist) {
|
|
349
|
+
console.log(this.spinner(), "waiting for files to build...");
|
|
350
|
+
filesToLookup.forEach((f) => {
|
|
351
|
+
console.log(f.exists, "\t", f.filepath);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
clearInterval(bootInterval);
|
|
356
|
+
pm2.launchBus((err, pm2_bus) => {
|
|
357
|
+
pm2_bus.on("testeranto:hola", (packet) => {
|
|
358
|
+
console.log("hola IPC", packet);
|
|
359
|
+
this.requestResource(packet.data.requirement, 'ipc');
|
|
360
|
+
});
|
|
361
|
+
pm2_bus.on("testeranto:adios", (payload) => {
|
|
362
|
+
console.log("adios IPC", payload);
|
|
363
|
+
this.releaseTestResources(payload.data);
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
this
|
|
367
|
+
.tests
|
|
368
|
+
.reduce((m, [inputFilePath, runtime]) => {
|
|
369
|
+
const script = makePath(inputFilePath, runtime);
|
|
370
|
+
const partialTestResourceByCommandLineArg = `${script} '${JSON.stringify({
|
|
371
|
+
name: inputFilePath,
|
|
372
|
+
ports: [],
|
|
373
|
+
fs: path.resolve(process.cwd(), config.outdir, inputFilePath),
|
|
374
|
+
})}'`;
|
|
375
|
+
if (runtime === "web") {
|
|
376
|
+
const fileAsList = inputFilePath.split("/");
|
|
377
|
+
const fileListHead = fileAsList.slice(0, -1);
|
|
378
|
+
const fname = fileAsList[fileAsList.length - 1];
|
|
379
|
+
const fnameOnly = fname.split(".").slice(0, -1).join(".");
|
|
380
|
+
const htmlFile = [config.outdir, ...fileListHead, `${fnameOnly}.html`].join("/");
|
|
381
|
+
const jsFile = htmlFile.split(".html")[0] + ".mjs";
|
|
382
|
+
console.log("watching", jsFile);
|
|
383
|
+
pm2.start({
|
|
384
|
+
script: `yarn electron node_modules/testeranto/dist/common/electron.js ${htmlFile} '${JSON.stringify({
|
|
385
|
+
name: inputFilePath,
|
|
386
|
+
ports: [],
|
|
387
|
+
fs: path.resolve(process.cwd(), config.outdir, inputFilePath),
|
|
388
|
+
})}'`,
|
|
389
|
+
name: inputFilePath,
|
|
390
|
+
autorestart: false,
|
|
391
|
+
args: partialTestResourceByCommandLineArg,
|
|
392
|
+
watch: [jsFile],
|
|
393
|
+
}, (err, proc) => {
|
|
394
|
+
if (err) {
|
|
395
|
+
console.error(err);
|
|
396
|
+
return pm2.disconnect();
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
else if (runtime === "node") {
|
|
401
|
+
pm2.start({
|
|
402
|
+
name: inputFilePath,
|
|
403
|
+
script: `node ${script} '${JSON.stringify({
|
|
404
|
+
name: inputFilePath,
|
|
405
|
+
ports: [],
|
|
406
|
+
fs: path.resolve(process.cwd(), config.outdir, inputFilePath),
|
|
407
|
+
})}'`,
|
|
408
|
+
autorestart: false,
|
|
409
|
+
watch: [script],
|
|
410
|
+
args: partialTestResourceByCommandLineArg
|
|
411
|
+
}, (err, proc) => {
|
|
412
|
+
if (err) {
|
|
413
|
+
console.error(err);
|
|
414
|
+
return pm2.disconnect();
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
this.exitCodes[inputFilePath] = null;
|
|
419
|
+
return [inputFilePath, ...m];
|
|
420
|
+
}, []);
|
|
421
|
+
setInterval(this.mainLoop, TIMEOUT).unref();
|
|
422
|
+
}
|
|
423
|
+
}, TIMEOUT).unref();
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
requestResource(requirement, protocol) {
|
|
429
|
+
this.resourceQueue.push({ requirement, protocol });
|
|
430
|
+
}
|
|
431
|
+
getSecondaryEndpointsPoints(runtime) {
|
|
432
|
+
if (runtime) {
|
|
433
|
+
return this.tests
|
|
434
|
+
.filter((t) => {
|
|
435
|
+
return (t[1] === runtime);
|
|
436
|
+
})
|
|
437
|
+
.map((tc) => tc[0]);
|
|
438
|
+
}
|
|
439
|
+
return this.tests
|
|
440
|
+
.map((tc) => tc[0]);
|
|
441
|
+
}
|
|
442
|
+
initiateShutdown(reason) {
|
|
443
|
+
console.log("Shutdown initiated because", reason);
|
|
444
|
+
this.mode = "down";
|
|
445
|
+
}
|
|
446
|
+
shutdown() {
|
|
447
|
+
console.log("Stopping PM2");
|
|
448
|
+
pm2.stop("all", (e) => console.error(e));
|
|
449
|
+
pm2.killDaemon((e) => console.error(e));
|
|
450
|
+
pm2.disconnect();
|
|
451
|
+
process.exit();
|
|
452
|
+
}
|
|
453
|
+
spinner() {
|
|
454
|
+
this.spinCycle = (this.spinCycle + 1) % this.spinAnimation.length;
|
|
455
|
+
return this.spinAnimation[this.spinCycle];
|
|
456
|
+
}
|
|
457
|
+
async releaseTestResources(payload) {
|
|
458
|
+
const name = payload.testResourceConfiguration.name;
|
|
459
|
+
const failed = payload.failed;
|
|
460
|
+
console.log("releaseTestResources", name, failed);
|
|
461
|
+
// reset ports
|
|
462
|
+
pm2.list((err, processes) => {
|
|
463
|
+
processes.forEach((proc) => {
|
|
464
|
+
if (proc.name === name) {
|
|
465
|
+
Object.keys(this.ports).forEach((port) => {
|
|
466
|
+
if (this.ports[port] === name) {
|
|
467
|
+
this.ports[port] = OPEN_PORT;
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
this.exitCodes[name] = failed;
|
|
474
|
+
}
|
|
475
|
+
allocateViaWs(resourceRequest) {
|
|
476
|
+
const pName = resourceRequest.requirement.name;
|
|
477
|
+
const testResourceRequirement = resourceRequest.requirement;
|
|
478
|
+
pm2.list((err, processes) => {
|
|
479
|
+
console.error(err);
|
|
480
|
+
processes.forEach((p) => {
|
|
481
|
+
if (p.name === pName && p.pid) {
|
|
482
|
+
const message = {
|
|
483
|
+
// these fields must be present
|
|
484
|
+
id: p.pid,
|
|
485
|
+
topic: "some topic",
|
|
486
|
+
type: "process:msg",
|
|
487
|
+
// Data to be sent
|
|
488
|
+
data: {
|
|
489
|
+
testResourceConfiguration: {
|
|
490
|
+
ports: [],
|
|
491
|
+
// fs: fPath,
|
|
492
|
+
},
|
|
493
|
+
id: p.pm_id,
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
if ((testResourceRequirement === null || testResourceRequirement === void 0 ? void 0 : testResourceRequirement.ports) === 0) {
|
|
497
|
+
pm2.sendDataToProcessId(p.pid, message, function (err, res) {
|
|
498
|
+
// console.log("sendDataToProcessId", err, res, message);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
if (((testResourceRequirement === null || testResourceRequirement === void 0 ? void 0 : testResourceRequirement.ports) || 0) > 0) {
|
|
502
|
+
// clear any port-slots associated with this job
|
|
503
|
+
Object.values(this.ports).forEach((jobMaybe, portNumber) => {
|
|
504
|
+
if (jobMaybe && jobMaybe === pName) {
|
|
505
|
+
this.ports[portNumber] = OPEN_PORT;
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
// find a list of open ports
|
|
509
|
+
const foundOpenPorts = Object.keys(this.ports).filter((p) => this.ports[p] === OPEN_PORT);
|
|
510
|
+
// if there are enough open port-slots...
|
|
511
|
+
if (foundOpenPorts.length >= testResourceRequirement.ports) {
|
|
512
|
+
const selectionOfPorts = foundOpenPorts.slice(0, testResourceRequirement.ports);
|
|
513
|
+
const message = {
|
|
514
|
+
// these fields must be present
|
|
515
|
+
id: p.pid,
|
|
516
|
+
topic: "some topic",
|
|
517
|
+
// process:msg will be send as 'message' on target process
|
|
518
|
+
type: "process:msg",
|
|
519
|
+
// Data to be sent
|
|
520
|
+
data: {
|
|
521
|
+
testResourceConfiguration: {
|
|
522
|
+
// fs: fPath,
|
|
523
|
+
ports: selectionOfPorts,
|
|
524
|
+
},
|
|
525
|
+
id: p.pid,
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
pm2.sendDataToProcessId(p.pid, message, function (err, res) {
|
|
529
|
+
// no-op
|
|
530
|
+
});
|
|
531
|
+
// mark the selected ports as occupied
|
|
532
|
+
for (const foundOpenPort of selectionOfPorts) {
|
|
533
|
+
this.ports[foundOpenPort] = p.pid.toString();
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
console.log(`no port was open so send the ${p.pid} job to the back of the resourceQueue`);
|
|
538
|
+
this.resourceQueue.push(resourceRequest);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
allocateViaIpc(resourceRequest) {
|
|
546
|
+
console.log("allocateViaIpc", resourceRequest);
|
|
547
|
+
const pName = resourceRequest.requirement.name;
|
|
548
|
+
const testResourceRequirement = resourceRequest.requirement;
|
|
549
|
+
pm2.list((err, processes) => {
|
|
550
|
+
console.error(err);
|
|
551
|
+
processes.forEach((p) => {
|
|
552
|
+
console.log("p.pid, p.name, p.pm_id", p.pid, p.name, p.pm_id);
|
|
553
|
+
if (p.name === pName && p.pid) {
|
|
554
|
+
const message = {
|
|
555
|
+
// these fields must be present
|
|
556
|
+
id: p.pid,
|
|
557
|
+
topic: "some topic",
|
|
558
|
+
type: "process:msg",
|
|
559
|
+
// Data to be sent
|
|
560
|
+
data: {
|
|
561
|
+
testResourceConfiguration: {
|
|
562
|
+
ports: [],
|
|
563
|
+
// fs: fPath,
|
|
564
|
+
},
|
|
565
|
+
id: p.pm_id,
|
|
566
|
+
},
|
|
567
|
+
};
|
|
568
|
+
console.log("message", message);
|
|
569
|
+
if ((testResourceRequirement === null || testResourceRequirement === void 0 ? void 0 : testResourceRequirement.ports) === 0) {
|
|
570
|
+
pm2.sendDataToProcessId(p.pm_id, message, function (err, res) {
|
|
571
|
+
// console.log("sendDataToProcessId", err, res, message);
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
if (((testResourceRequirement === null || testResourceRequirement === void 0 ? void 0 : testResourceRequirement.ports) || 0) > 0) {
|
|
575
|
+
// clear any port-slots associated with this job
|
|
576
|
+
Object.values(this.ports).forEach((jobMaybe, portNumber) => {
|
|
577
|
+
if (jobMaybe && jobMaybe === pName) {
|
|
578
|
+
this.ports[portNumber] = OPEN_PORT;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
// find a list of open ports
|
|
582
|
+
const foundOpenPorts = Object.keys(this.ports).filter((p) => this.ports[p] === OPEN_PORT);
|
|
583
|
+
// if there are enough open port-slots...
|
|
584
|
+
if (foundOpenPorts.length >= testResourceRequirement.ports) {
|
|
585
|
+
const selectionOfPorts = foundOpenPorts.slice(0, testResourceRequirement.ports);
|
|
586
|
+
const message = {
|
|
587
|
+
// these fields must be present
|
|
588
|
+
id: p.pid,
|
|
589
|
+
topic: "some topic",
|
|
590
|
+
// process:msg will be send as 'message' on target process
|
|
591
|
+
type: "process:msg",
|
|
592
|
+
// Data to be sent
|
|
593
|
+
data: {
|
|
594
|
+
testResourceConfiguration: {
|
|
595
|
+
// fs: fPath,
|
|
596
|
+
ports: selectionOfPorts,
|
|
597
|
+
},
|
|
598
|
+
id: p.pid,
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
pm2.sendDataToProcessId(p.pm_id, message, function (err, res) {
|
|
602
|
+
// no-op
|
|
603
|
+
});
|
|
604
|
+
// mark the selected ports as occupied
|
|
605
|
+
for (const foundOpenPort of selectionOfPorts) {
|
|
606
|
+
this.ports[foundOpenPort] = p.pid.toString();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
console.log(`no port was open so send the ${p.pid} job to the back of the resourceQueue`);
|
|
611
|
+
this.resourceQueue.push(resourceRequest);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|