@nsshunt/stsappframework 3.1.160 → 3.1.162
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/testertesting/app.js +198 -0
- package/dist/testertesting/app.js.map +1 -0
- package/dist/testertesting/commonTypes.js +39 -0
- package/dist/testertesting/commonTypes.js.map +1 -0
- package/dist/testertesting/requestResponseHelper.js +83 -0
- package/dist/testertesting/requestResponseHelper.js.map +1 -0
- package/dist/testertesting/telemetryProcessor.js +114 -0
- package/dist/testertesting/telemetryProcessor.js.map +1 -0
- package/dist/testertesting/workerInstance.js +141 -0
- package/dist/testertesting/workerInstance.js.map +1 -0
- package/dist/testertesting/workerManager.js +339 -0
- package/dist/testertesting/workerManager.js.map +1 -0
- package/dist/testertesting/workerPrimaryTestRunner01.js +62 -0
- package/dist/testertesting/workerPrimaryTestRunner01.js.map +1 -0
- package/dist/testertesting/workerWorkerTestRunner01.js +146 -0
- package/dist/testertesting/workerWorkerTestRunner01.js.map +1 -0
- package/package.json +1 -1
- package/src/testertesting/app.ts +243 -0
- package/src/testertesting/commonTypes.ts +197 -0
- package/src/testertesting/requestResponseHelper.ts +95 -0
- package/src/testertesting/telemetryProcessor.ts +128 -0
- package/src/testertesting/workerInstance.ts +165 -0
- package/src/testertesting/workerManager.ts +386 -0
- package/src/testertesting/workerPrimaryTestRunner01.ts +81 -0
- package/src/testertesting/workerWorkerTestRunner01.ts +184 -0
- package/types/testertesting/app.d.ts +2 -0
- package/types/testertesting/app.d.ts.map +1 -0
- package/types/testertesting/commonTypes.d.ts +167 -0
- package/types/testertesting/commonTypes.d.ts.map +1 -0
- package/types/testertesting/requestResponseHelper.d.ts +8 -0
- package/types/testertesting/requestResponseHelper.d.ts.map +1 -0
- package/types/testertesting/telemetryProcessor.d.ts +6 -0
- package/types/testertesting/telemetryProcessor.d.ts.map +1 -0
- package/types/testertesting/workerInstance.d.ts +18 -0
- package/types/testertesting/workerInstance.d.ts.map +1 -0
- package/types/testertesting/workerManager.d.ts +14 -0
- package/types/testertesting/workerManager.d.ts.map +1 -0
- package/types/testertesting/workerPrimaryTestRunner01.d.ts +8 -0
- package/types/testertesting/workerPrimaryTestRunner01.d.ts.map +1 -0
- package/types/testertesting/workerWorkerTestRunner01.d.ts +16 -0
- package/types/testertesting/workerWorkerTestRunner01.d.ts.map +1 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/no-explicit-any: 0, @typescript-eslint/no-unused-vars: 0 */ // --> OFF
|
|
2
|
+
import { MessagePort } from 'worker_threads';
|
|
3
|
+
|
|
4
|
+
import { IIWMessagePayload, eIWMessageCommands, IIWMessagePayloadContentBase, IRunnerState,
|
|
5
|
+
ISTSAgentWorkerMessagePort, IRunner, ITestRunnerTelemetryPayload
|
|
6
|
+
} from './commonTypes'
|
|
7
|
+
|
|
8
|
+
import { RequestResponseHelper } from './requestResponseHelper'
|
|
9
|
+
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
// Force chalk level
|
|
12
|
+
chalk.level = 3;
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
15
|
+
export interface IWorkerInstanceOptions {
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export abstract class WorkerInstance {
|
|
20
|
+
#collectorCollectorPort: MessagePort | null = null;
|
|
21
|
+
#requestResponseHelper: RequestResponseHelper | null = null;
|
|
22
|
+
#runners: Record<string, IRunner> = { };
|
|
23
|
+
#options: IWorkerInstanceOptions | null = null;
|
|
24
|
+
|
|
25
|
+
#debug = (message: string) => {
|
|
26
|
+
console.log(chalk.green(`pid: [${process.pid}] WorkerInstance::${message}`));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
constructor() {
|
|
31
|
+
this.#debug(`constructor`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
GetRandomInt = (max: number) => {
|
|
35
|
+
this.#debug(`GetRandomInt`)
|
|
36
|
+
return Math.floor(Math.random() * Math.floor(max));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
StartWork = async (runner: IRunner): Promise<void> => {
|
|
41
|
+
this.#debug(`StartWork`)
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
PostTelemetry = (runner: IRunner) => {
|
|
46
|
+
//debug(`WorkerInstance::PostTelemetry`)
|
|
47
|
+
if (this.#collectorCollectorPort) {
|
|
48
|
+
const message: IIWMessagePayload = {
|
|
49
|
+
command: eIWMessageCommands.InstrumentTelemetry,
|
|
50
|
+
payload: {
|
|
51
|
+
runner
|
|
52
|
+
} as ITestRunnerTelemetryPayload
|
|
53
|
+
}
|
|
54
|
+
this.#collectorCollectorPort.postMessage(message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get RequestResponseHelper(): RequestResponseHelper | null {
|
|
59
|
+
return this.#requestResponseHelper;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get CollectorCollectorPort(): MessagePort | null {
|
|
63
|
+
return this.#collectorCollectorPort;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get Options(): IWorkerInstanceOptions | null {
|
|
67
|
+
return this.#options;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#SetMessagePort = (workerMessagePort: ISTSAgentWorkerMessagePort) => {
|
|
71
|
+
this.#debug(`SetMessagePort`)
|
|
72
|
+
this.#collectorCollectorPort = workerMessagePort.port as MessagePort;
|
|
73
|
+
|
|
74
|
+
// NodeJS version
|
|
75
|
+
this.#collectorCollectorPort.on('message', (data: any) => {
|
|
76
|
+
this.#debug(`collectorCollectorPort onmessage: ${data.data}`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Browser Version
|
|
80
|
+
/*
|
|
81
|
+
this.#collectorCollectorPort.onmessage = function(data: MessageEvent) {
|
|
82
|
+
console.log(`collectorCollectorPort onmessage: ${data.data}`);
|
|
83
|
+
}
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
this.#requestResponseHelper = new RequestResponseHelper(this.#collectorCollectorPort);
|
|
87
|
+
|
|
88
|
+
const response: IIWMessagePayload = {
|
|
89
|
+
command: eIWMessageCommands.MessagePortResponse,
|
|
90
|
+
payload: { } as IIWMessagePayloadContentBase
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.#collectorCollectorPort.postMessage(response);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
StartRunner = async (runner: IRunner) => {
|
|
97
|
+
this.#debug(`StartRunner`)
|
|
98
|
+
this.#debug(`StartTests: [${JSON.stringify(runner)}]`);
|
|
99
|
+
runner.state = IRunnerState.running;
|
|
100
|
+
this.StartWork(runner);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#AddAsyncRunner = (testRunnerTelemetryPayload: ITestRunnerTelemetryPayload): IRunner => {
|
|
104
|
+
this.#debug(`AddAsyncRunner`)
|
|
105
|
+
const { runner } = testRunnerTelemetryPayload;
|
|
106
|
+
this.#runners[runner.id] = runner;
|
|
107
|
+
return runner;
|
|
108
|
+
//this.StartRunner(runner);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
#StartRunner = (testRunnerTelemetryPayload: ITestRunnerTelemetryPayload): IRunner => {
|
|
112
|
+
this.#debug(`StartRunner`)
|
|
113
|
+
const { runner } = testRunnerTelemetryPayload;
|
|
114
|
+
this.StartRunner(runner);
|
|
115
|
+
return runner;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#StopRunners = (testRunnerTelemetryPayload: ITestRunnerTelemetryPayload) => {
|
|
119
|
+
this.#debug(`StopRunners`)
|
|
120
|
+
if (testRunnerTelemetryPayload === null) {
|
|
121
|
+
for (const [, testRunner] of Object.entries(this.#runners)) {
|
|
122
|
+
testRunner.state = IRunnerState.stopped;
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
const runner: IRunner = this.#runners[testRunnerTelemetryPayload.runner.id];
|
|
126
|
+
if (runner) {
|
|
127
|
+
runner.state = IRunnerState.stopped;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ProcessMessage = async(data: MessageEvent) => { // Browser version
|
|
133
|
+
ProcessMessage = async(data: any) => {
|
|
134
|
+
this.#debug(`ProcessMessage: data: [${JSON.stringify(data)}]`)
|
|
135
|
+
try {
|
|
136
|
+
// const payloadMessage: IIWMessagePayload = data.data as IIWMessagePayload; // browser version
|
|
137
|
+
const payloadMessage: IIWMessagePayload = data as IIWMessagePayload;
|
|
138
|
+
switch (payloadMessage.command) {
|
|
139
|
+
case eIWMessageCommands.MessagePort :
|
|
140
|
+
this.#debug(`ProcessMessage::MessagePort`)
|
|
141
|
+
this.#SetMessagePort(payloadMessage.payload as ISTSAgentWorkerMessagePort);
|
|
142
|
+
this.#options = (payloadMessage.payload as ISTSAgentWorkerMessagePort).options;
|
|
143
|
+
this.#debug(`ProcessMessage::#options: [${JSON.stringify(this.#options)}]`)
|
|
144
|
+
break;
|
|
145
|
+
case eIWMessageCommands.AddAsyncRunner :
|
|
146
|
+
this.#debug(`ProcessMessage::AddAsyncRunner`)
|
|
147
|
+
this.#AddAsyncRunner(payloadMessage.payload as ITestRunnerTelemetryPayload);
|
|
148
|
+
break;
|
|
149
|
+
case eIWMessageCommands.StopAllAsyncRunners :
|
|
150
|
+
this.#debug(`ProcessMessage::StopAllAsyncRunners`)
|
|
151
|
+
this.#StopRunners(payloadMessage.payload as ITestRunnerTelemetryPayload);
|
|
152
|
+
break;
|
|
153
|
+
case eIWMessageCommands.StartRunner :
|
|
154
|
+
this.#debug(`ProcessMessage::StartRunner`)
|
|
155
|
+
this.#StartRunner(payloadMessage.payload as ITestRunnerTelemetryPayload);
|
|
156
|
+
break;
|
|
157
|
+
default :
|
|
158
|
+
this.#debug(`ProcessMessage::default`)
|
|
159
|
+
this.#debug(`Invalid payloadMessage.command: [${payloadMessage.command}] - Ignoring`);
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.log(error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/no-explicit-any: 0, @typescript-eslint/no-unused-vars: 0 */ // --> OFF
|
|
2
|
+
//import MyWorker from './sts-worker?worker' // https://vitejs.dev/guide/features.html#web-workers
|
|
3
|
+
import { IAsyncRunnerContext, IIWMessagePayload, IIWMessagePayloadContentBase,
|
|
4
|
+
IIWMessageCommand, eIWMessageCommands, IWorkerState, IRunnerState,
|
|
5
|
+
ISTSAgentWorkerMessagePort, IWorkerEx, IRunner, IRunnerEx,
|
|
6
|
+
ITestRunnerTelemetryPayload, IRunnerOptions, IRunnerTelemetry,
|
|
7
|
+
IWorkerManagerOptions, IWorkerFactory
|
|
8
|
+
} from './commonTypes'
|
|
9
|
+
|
|
10
|
+
import { ModelDelimeter } from '@nsshunt/stsutils'
|
|
11
|
+
|
|
12
|
+
import { Sleep } from '@nsshunt/stsutils';
|
|
13
|
+
|
|
14
|
+
import { AgentInstrumentController, PublishInstrumentController } from '@nsshunt/stsobservability'
|
|
15
|
+
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
import { TelemetryProcessor } from './telemetryProcessor'
|
|
18
|
+
chalk.level = 3;
|
|
19
|
+
|
|
20
|
+
export class STSWorkerManager {
|
|
21
|
+
//#agentSession: string = null;
|
|
22
|
+
#workersEx: Record<string, IWorkerEx> = { };
|
|
23
|
+
#runner = 0;
|
|
24
|
+
#workerId = 0;
|
|
25
|
+
#options: IWorkerManagerOptions;
|
|
26
|
+
#STSInstrumentController: PublishInstrumentController
|
|
27
|
+
#telemetryProcessor: TelemetryProcessor
|
|
28
|
+
#app: any
|
|
29
|
+
|
|
30
|
+
constructor(app: any, options?: IWorkerManagerOptions) {
|
|
31
|
+
this.#app = app;
|
|
32
|
+
if (options) {
|
|
33
|
+
this.#options = options;
|
|
34
|
+
} else {
|
|
35
|
+
this.#options = { } as IWorkerManagerOptions;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.#STSInstrumentController = this.#options.publishInstrumentController;
|
|
39
|
+
this.#telemetryProcessor = new TelemetryProcessor();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#debug = (message: string) => {
|
|
43
|
+
console.log(chalk.cyan(`pid: [${process.pid}] STSWorkerManager::${message}`));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get WorkersEx(): Record<string, IWorkerEx> {
|
|
47
|
+
return this.#workersEx;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
AddWorker = async (useWorkerFactory?: IWorkerFactory): Promise<IWorkerEx> => {
|
|
51
|
+
let workerFactory: IWorkerFactory;
|
|
52
|
+
if (useWorkerFactory) {
|
|
53
|
+
// Use the supplied workFactory
|
|
54
|
+
workerFactory = useWorkerFactory;
|
|
55
|
+
} else {
|
|
56
|
+
// Use the default workFactory
|
|
57
|
+
workerFactory = this.#options.workerFactory
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const workerId = this.#workerId++;
|
|
61
|
+
const stsWorkerEx: IWorkerEx = {
|
|
62
|
+
id: workerId,
|
|
63
|
+
worker: workerFactory.createWorkerThreadWorker(),
|
|
64
|
+
primaryWorker: workerFactory.createPrimaryThreadWorker(this.#app, workerFactory.primaryThreadWorkerOptions),
|
|
65
|
+
state: IWorkerState.starting,
|
|
66
|
+
workerThreadWorkerOptions: workerFactory.workerThreadWorkerOptions,
|
|
67
|
+
primaryThreadWorkerOptions: workerFactory.primaryThreadWorkerOptions,
|
|
68
|
+
runnersEx: { } as Record<string, IRunnerEx>,
|
|
69
|
+
AddRunner: (runnerOptions: IRunnerOptions): IRunnerEx => this.AddRunnerToWorker(stsWorkerEx, runnerOptions),
|
|
70
|
+
StartRunner: (runner: IRunnerEx): Promise<boolean> => this.#StartRunner(stsWorkerEx, runner),
|
|
71
|
+
StopRunner: (runner: IRunnerEx): Promise<boolean> => this.#StopRunner(stsWorkerEx, runner),
|
|
72
|
+
Stop: async (): Promise<boolean> => this.#StopWorker(stsWorkerEx),
|
|
73
|
+
GetRunner: (id: string): IRunnerEx | null => this.#workersEx[workerId].GetRunner(id)
|
|
74
|
+
}
|
|
75
|
+
this.#STSInstrumentController.LogEx(chalk.yellow(`pid: [${process.pid}] Creating new worker: [${stsWorkerEx.id}]`));
|
|
76
|
+
this.#debug(`Adding worker: [${stsWorkerEx.id}]`);
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
stsWorkerEx.worker.onmessage = function(data: MessageEvent) {
|
|
80
|
+
console.log(data.data);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
stsWorkerEx.worker.onerror = function(error) {
|
|
84
|
+
console.log(error);
|
|
85
|
+
};
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
const {
|
|
89
|
+
port1, // process message port
|
|
90
|
+
port2 // collector message port
|
|
91
|
+
} = new MessageChannel();
|
|
92
|
+
|
|
93
|
+
const workerPort = port1;
|
|
94
|
+
|
|
95
|
+
this.#debug(`AddWorker::workerThreadWorkerOptions: [${JSON.stringify(stsWorkerEx.workerThreadWorkerOptions)}]`);
|
|
96
|
+
|
|
97
|
+
this.#PostMessageToWorker(stsWorkerEx, eIWMessageCommands.MessagePort, {
|
|
98
|
+
port: port2,
|
|
99
|
+
//applicationStoreState: stateCopy,
|
|
100
|
+
options: { ...stsWorkerEx.workerThreadWorkerOptions }
|
|
101
|
+
} as ISTSAgentWorkerMessagePort, port2);
|
|
102
|
+
|
|
103
|
+
// Process messages received back from the worker
|
|
104
|
+
//workerPort.onmessage = async (data: MessageEvent) => {
|
|
105
|
+
workerPort.on('message', (data: any) => {
|
|
106
|
+
// const publishMessagePayload: IIWMessagePayload = data.data as IIWMessagePayload; browser version
|
|
107
|
+
const publishMessagePayload: IIWMessagePayload = data as IIWMessagePayload;
|
|
108
|
+
switch (publishMessagePayload.command) {
|
|
109
|
+
case eIWMessageCommands.MessagePortResponse :
|
|
110
|
+
this.#debug(`AddWorker::eIWMessageCommands.MessagePortResponse`);
|
|
111
|
+
stsWorkerEx.state = IWorkerState.started;
|
|
112
|
+
break;
|
|
113
|
+
case eIWMessageCommands.InstrumentTelemetry :
|
|
114
|
+
this.#debug(`AddWorker::eIWMessageCommands.InstrumentTelemetry`);
|
|
115
|
+
this.#ProcessTelemetry(stsWorkerEx, publishMessagePayload.payload as ITestRunnerTelemetryPayload);
|
|
116
|
+
break;
|
|
117
|
+
default :
|
|
118
|
+
this.#debug(`AddWorker::default`);
|
|
119
|
+
stsWorkerEx.primaryWorker.ProcessMessageFromWorker(workerPort, publishMessagePayload);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
this.#workersEx[stsWorkerEx.id] = stsWorkerEx;
|
|
124
|
+
|
|
125
|
+
this.#debug(`Added worker: [${stsWorkerEx.id}]`);
|
|
126
|
+
|
|
127
|
+
return stsWorkerEx;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
AddRunnerToWorker = (stsWorkerEx: IWorkerEx, runnerOptions: IRunnerOptions): IRunnerEx => {
|
|
131
|
+
const runnerEx: IRunnerEx = this.#CreateAsyncRunner(stsWorkerEx, runnerOptions);
|
|
132
|
+
stsWorkerEx.runnersEx[runnerEx.id] = runnerEx;
|
|
133
|
+
this.#SetRunnerIntoWorker(stsWorkerEx, runnerEx);
|
|
134
|
+
runnerEx.publishInstrumentController.LogEx(chalk.green(`Added runner: [${runnerEx.id}] into worker: [${stsWorkerEx.id}]`));
|
|
135
|
+
return runnerEx;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#CreateRunnerCopy(runnerEx: IRunnerEx): IRunner {
|
|
139
|
+
return {
|
|
140
|
+
id: runnerEx.id,
|
|
141
|
+
asyncRunnerContext: { ...runnerEx.asyncRunnerContext },
|
|
142
|
+
options: { ...runnerEx.options },
|
|
143
|
+
state: runnerEx.state,
|
|
144
|
+
instrumentData: { ...runnerEx.instrumentData }
|
|
145
|
+
} as IRunner
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
#SetRunnerIntoWorker = (workerEx: IWorkerEx, runnerEx: IRunnerEx): void => {
|
|
149
|
+
// Now that the worker is setup, send the options
|
|
150
|
+
//@@ wait until worker in running state
|
|
151
|
+
const payload: ITestRunnerTelemetryPayload = {
|
|
152
|
+
runner: this.#CreateRunnerCopy(runnerEx)
|
|
153
|
+
}
|
|
154
|
+
this.#PostMessageToWorker(workerEx, eIWMessageCommands.AddAsyncRunner, payload);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#ProcessTelemetry = (workerEx: IWorkerEx, payloadContents: ITestRunnerTelemetryPayload): void => {
|
|
158
|
+
//const store = TelemetryStore();
|
|
159
|
+
|
|
160
|
+
const { runner } = payloadContents;
|
|
161
|
+
|
|
162
|
+
if (workerEx.runnersEx[runner.id]) {
|
|
163
|
+
const runnerEx: IRunnerEx = workerEx.runnersEx[runner.id];
|
|
164
|
+
|
|
165
|
+
// Copy telemetry
|
|
166
|
+
runnerEx.instrumentData = { ...runner.instrumentData };
|
|
167
|
+
|
|
168
|
+
this.#debug(JSON.stringify(runnerEx.instrumentData));
|
|
169
|
+
|
|
170
|
+
if (runner.instrumentData.message) {
|
|
171
|
+
runnerEx.instrumentData.message = [...runner.instrumentData.message];
|
|
172
|
+
} else {
|
|
173
|
+
runnerEx.instrumentData.message = [ ];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const update = this.#telemetryProcessor.ProcessTelemetry(runnerEx.publishInstrumentController, runnerEx.instrumentData);
|
|
177
|
+
|
|
178
|
+
if (update) {
|
|
179
|
+
//store.Update(workerEx, runnerEx);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#CreateAsyncRunner = (workerEx: IWorkerEx, runnerOptions: IRunnerOptions): IRunnerEx => {
|
|
185
|
+
//const applicationStore = ApplicationStore();
|
|
186
|
+
this.#runner++; // The runner number always increases
|
|
187
|
+
this.#STSInstrumentController.LogEx(chalk.yellow(`Creating new async runner: [${this.#runner}]`));
|
|
188
|
+
const asyncRunnerContext: IAsyncRunnerContext = {
|
|
189
|
+
nid: `\
|
|
190
|
+
${workerEx.workerThreadWorkerOptions.hostName}${ModelDelimeter.COMPONENT_SEPERATOR}${workerEx.workerThreadWorkerOptions.agentId}-${workerEx.workerThreadWorkerOptions.userAgent}\
|
|
191
|
+
${ModelDelimeter.NID_SEPERATOR}\
|
|
192
|
+
worker${workerEx.id}\
|
|
193
|
+
${ModelDelimeter.SEPERATOR}\
|
|
194
|
+
${this.#runner}`,
|
|
195
|
+
id: this.#runner.toString(),
|
|
196
|
+
hostName: (workerEx.workerThreadWorkerOptions.hostName ? workerEx.workerThreadWorkerOptions.hostName : 'host'),
|
|
197
|
+
agentName: `${workerEx.workerThreadWorkerOptions.agentId}-${workerEx.workerThreadWorkerOptions.userAgent}`,
|
|
198
|
+
threadId: `worker${workerEx.id}`,
|
|
199
|
+
asyncRunnerId: this.#runner
|
|
200
|
+
}
|
|
201
|
+
const runnerEx: IRunnerEx = {
|
|
202
|
+
id: this.#runner,
|
|
203
|
+
publishInstrumentController: (this.#STSInstrumentController as AgentInstrumentController).AddPublishInstrumentController(asyncRunnerContext),
|
|
204
|
+
asyncRunnerContext: asyncRunnerContext,
|
|
205
|
+
state: IRunnerState.created,
|
|
206
|
+
options: runnerOptions,
|
|
207
|
+
instrumentData: {
|
|
208
|
+
requestCount: 0,
|
|
209
|
+
errorCount: 0,
|
|
210
|
+
retryCount: 0,
|
|
211
|
+
authenticationCount: 0,
|
|
212
|
+
authenticationErrorCount: 0,
|
|
213
|
+
authenticationRetryCount: 0,
|
|
214
|
+
velocity: 0,
|
|
215
|
+
coreCount: 0,
|
|
216
|
+
timer: 0,
|
|
217
|
+
duration: 0,
|
|
218
|
+
latency: 0,
|
|
219
|
+
activeRequestCount: 0,
|
|
220
|
+
message: [ ],
|
|
221
|
+
childCount: 0,
|
|
222
|
+
rx: 0,
|
|
223
|
+
tx: 0
|
|
224
|
+
} as IRunnerTelemetry,
|
|
225
|
+
Stop: async (): Promise<boolean> => this.#StopRunner(workerEx, runnerEx),
|
|
226
|
+
Start: async (): Promise<boolean> => this.#StartRunner(workerEx, runnerEx),
|
|
227
|
+
Pause: async (): Promise<boolean> => this.#PauseRunner(workerEx, runnerEx),
|
|
228
|
+
Resume: async (): Promise<boolean> => this.#ResumeRunner(workerEx, runnerEx),
|
|
229
|
+
ApplyConfig: async (config: IRunnerOptions): Promise<boolean> => {
|
|
230
|
+
runnerEx.options = config;
|
|
231
|
+
return true;
|
|
232
|
+
},
|
|
233
|
+
GetConfig: (): IRunnerOptions => runnerEx.options
|
|
234
|
+
}
|
|
235
|
+
return runnerEx;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
#PostMessageToWorker = (workerEx: IWorkerEx, command: IIWMessageCommand, payload: IIWMessagePayloadContentBase | null, transferObject?: any) => {
|
|
240
|
+
if (transferObject) {
|
|
241
|
+
this.#debug(`#PostMessageToWorker with transfer object`);
|
|
242
|
+
workerEx.worker.postMessage({ command, payload }, [transferObject]);
|
|
243
|
+
this.#debug(`#PostMessageToWorker with transfer object - done...`);
|
|
244
|
+
} else {
|
|
245
|
+
this.#debug(`#PostMessageToWorker`);
|
|
246
|
+
workerEx.worker.postMessage({ command, payload });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
#TerminateWorker = (workerEx: IWorkerEx) => {
|
|
251
|
+
if (workerEx.worker) {
|
|
252
|
+
//const store = TelemetryStore();
|
|
253
|
+
workerEx.worker.terminate();
|
|
254
|
+
this.#debug(`Terminated worker: [${workerEx.id}]`);
|
|
255
|
+
//store.RemoveWorker(workerEx);
|
|
256
|
+
delete this.#workersEx[workerEx.id];
|
|
257
|
+
} else {
|
|
258
|
+
// Some other runner has already removed the parent worker, do nothing
|
|
259
|
+
// console.log(`WORKER ALREADY NULL`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#StartRunner = async (workerEx: IWorkerEx, runnerEx: IRunnerEx): Promise<boolean> => {
|
|
264
|
+
this.#PostMessageToWorker(workerEx, eIWMessageCommands.StartRunner, {
|
|
265
|
+
runner: this.#CreateRunnerCopy(runnerEx)
|
|
266
|
+
} as ITestRunnerTelemetryPayload);
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
#StopRunner = async (workerEx: IWorkerEx, runnerEx: IRunnerEx | null = null): Promise<boolean> => {
|
|
271
|
+
// If runnerEx not provided, Remove the first runner in the collection
|
|
272
|
+
if (runnerEx === null) {
|
|
273
|
+
const ids: string[] = Object.keys(workerEx.runnersEx);
|
|
274
|
+
if (ids.length > 0) {
|
|
275
|
+
const id = ids[0];
|
|
276
|
+
runnerEx = workerEx.runnersEx[id];
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (runnerEx !== null) {
|
|
280
|
+
this.#PostMessageToWorker(workerEx, eIWMessageCommands.StopAllAsyncRunners, {
|
|
281
|
+
runner: this.#CreateRunnerCopy(runnerEx)
|
|
282
|
+
} as ITestRunnerTelemetryPayload);
|
|
283
|
+
|
|
284
|
+
runnerEx.publishInstrumentController.LogEx(`Terminating runner: [${runnerEx.id}]`);
|
|
285
|
+
|
|
286
|
+
const promArray: Promise<boolean>[] = [ ];
|
|
287
|
+
|
|
288
|
+
promArray.push((async (): Promise<boolean> => {
|
|
289
|
+
await Sleep(100);
|
|
290
|
+
return runnerEx.publishInstrumentController.EndPublish() as Promise<boolean>
|
|
291
|
+
})());
|
|
292
|
+
|
|
293
|
+
//const store = TelemetryStore();
|
|
294
|
+
//store.RemoveRunner(workerEx, runnerEx);
|
|
295
|
+
|
|
296
|
+
delete workerEx.runnersEx[runnerEx.id];
|
|
297
|
+
|
|
298
|
+
const retVal = await Promise.all(promArray);
|
|
299
|
+
console.log(`Removed instrument workers: [${retVal}]`);
|
|
300
|
+
}
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
#PauseRunner = async (workerEx: IWorkerEx, runnerEx: IRunnerEx | null = null): Promise<boolean> => {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
#ResumeRunner = async (workerEx: IWorkerEx, runnerEx: IRunnerEx | null = null): Promise<boolean> => {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
#StopWorker = async (workerEx: IWorkerEx): Promise<boolean> => {
|
|
313
|
+
try {
|
|
314
|
+
if (workerEx.state !== IWorkerState.stopped) {
|
|
315
|
+
this.#PostMessageToWorker(workerEx, eIWMessageCommands.StopAllAsyncRunners, null);
|
|
316
|
+
|
|
317
|
+
//@@ Now wait until we get an ack back from the worker
|
|
318
|
+
// This is because we may be trying to stop BEFORE the worker has had a chance to startup ...
|
|
319
|
+
|
|
320
|
+
console.log(`Terminating worker: [${workerEx.id}]`);
|
|
321
|
+
const promArray: Promise<boolean>[] = [ ];
|
|
322
|
+
|
|
323
|
+
// Terminate only those that are currently running...
|
|
324
|
+
const ids: string[] = Object.keys(workerEx.runnersEx);
|
|
325
|
+
|
|
326
|
+
ids.forEach((id) => {
|
|
327
|
+
const runnerEx: IRunnerEx = workerEx.runnersEx[id];
|
|
328
|
+
promArray.push(this.#StopRunner(workerEx, runnerEx));
|
|
329
|
+
});
|
|
330
|
+
await Promise.all(promArray);
|
|
331
|
+
|
|
332
|
+
this.#TerminateWorker(workerEx);
|
|
333
|
+
}
|
|
334
|
+
return true;
|
|
335
|
+
} catch (error) {
|
|
336
|
+
console.log(`Error in STSTestWorker:StopWorker: [${error}]`);
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
GetNextAvailableWorker = (): IWorkerEx | null => {
|
|
342
|
+
// Calculate the worker with the least runners
|
|
343
|
+
let leastRunnerWorker: IWorkerEx | null = null;
|
|
344
|
+
for (const [, stsWorker] of Object.entries(this.WorkersEx)) {
|
|
345
|
+
if (leastRunnerWorker) {
|
|
346
|
+
if (Object.keys(stsWorker.runnersEx).length < Object.keys(leastRunnerWorker.runnersEx).length) {
|
|
347
|
+
leastRunnerWorker = stsWorker;
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
leastRunnerWorker = stsWorker;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return leastRunnerWorker;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
GetBusyWorker = (): IWorkerEx | null => {
|
|
357
|
+
// Calculate the worker with the least runners
|
|
358
|
+
let busyWorker: IWorkerEx | null = null;
|
|
359
|
+
for (const [, stsWorker] of Object.entries(this.WorkersEx)) {
|
|
360
|
+
if (busyWorker) {
|
|
361
|
+
if (Object.keys(stsWorker.runnersEx).length > Object.keys(busyWorker.runnersEx).length) {
|
|
362
|
+
busyWorker = stsWorker;
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
busyWorker = stsWorker;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return busyWorker;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
get Options(): IWorkerManagerOptions {
|
|
372
|
+
return this.#options;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
set Options(options: IWorkerManagerOptions) {
|
|
376
|
+
this.#options = options;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
StopAllWorkers = async () => {
|
|
380
|
+
const promArray = [ ];
|
|
381
|
+
for (const [, stsWorker] of Object.entries(this.WorkersEx)) {
|
|
382
|
+
promArray.push(stsWorker.Stop());
|
|
383
|
+
}
|
|
384
|
+
await Promise.all(promArray);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { MessagePort } from 'worker_threads';
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
// Force chalk level
|
|
5
|
+
chalk.level = 3;
|
|
6
|
+
|
|
7
|
+
import { IPrimaryWorker, IWorkerOptions, PublishMessageCommandsTestRunner,
|
|
8
|
+
IIWMessagePayload, IIWMessagePayloadContentBase } from './commonTypes'
|
|
9
|
+
|
|
10
|
+
//import { STSOAuth2Manager, STSOAuth2ManagerPluginKey } from '@nsshunt/stsoauth2plugin'
|
|
11
|
+
|
|
12
|
+
// This will execute within the primary thread context and have access to plugins and other Vue application features
|
|
13
|
+
export class WorkerPrimaryTestRunner01 implements IPrimaryWorker {
|
|
14
|
+
// #STSOAuth2Manager: STSOAuth2Manager
|
|
15
|
+
#options: IWorkerOptions
|
|
16
|
+
|
|
17
|
+
#debug = (message: string) => {
|
|
18
|
+
console.log(chalk.magenta(`pid: [${process.pid}] WorkerPrimaryTestRunner01::${message}`));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
constructor(app: any, options: IWorkerOptions) {
|
|
23
|
+
this.#debug(`constructor`)
|
|
24
|
+
this.#options = options;
|
|
25
|
+
this.#debug(`constructor::options: [${JSON.stringify(this.#options)}]`);
|
|
26
|
+
// this.#STSOAuth2Manager = app.config.globalProperties.$sts[STSOAuth2ManagerPluginKey]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async ProcessMessageFromWorker(workerPort: MessagePort, publishMessagePayload: IIWMessagePayload) {
|
|
30
|
+
this.#debug(`ProcessMessageFromWorker`)
|
|
31
|
+
// Process messages received back from the worker
|
|
32
|
+
switch (publishMessagePayload.command as PublishMessageCommandsTestRunner) {
|
|
33
|
+
case PublishMessageCommandsTestRunner.GetAccessToken : {
|
|
34
|
+
//const access_token = await this.#GetAccessToken();
|
|
35
|
+
const access_token = '1234';
|
|
36
|
+
workerPort.postMessage({
|
|
37
|
+
command: PublishMessageCommandsTestRunner.GetAccessTokenResponse,
|
|
38
|
+
payload: {
|
|
39
|
+
messageId: publishMessagePayload.payload.messageId,
|
|
40
|
+
access_token
|
|
41
|
+
} as IIWMessagePayloadContentBase
|
|
42
|
+
})}
|
|
43
|
+
break;
|
|
44
|
+
case PublishMessageCommandsTestRunner.ExecuteRefreshToken : {
|
|
45
|
+
//const access_token = await this.#ExecuteRefreshToken();
|
|
46
|
+
const access_token = '5678';
|
|
47
|
+
workerPort.postMessage({
|
|
48
|
+
command: PublishMessageCommandsTestRunner.ExecuteRefreshTokenResponse,
|
|
49
|
+
payload: {
|
|
50
|
+
messageId: publishMessagePayload.payload.messageId,
|
|
51
|
+
access_token
|
|
52
|
+
} as IIWMessagePayloadContentBase
|
|
53
|
+
})}
|
|
54
|
+
break;
|
|
55
|
+
default :
|
|
56
|
+
throw new Error(`publishMessagePayload.command: [${publishMessagePayload.command}] not allowed`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
#GetAccessToken = async() : Promise<string | null> => {
|
|
62
|
+
debug(`WorkerPrimaryTestRunner01::GetAccessToken`)
|
|
63
|
+
return await this.#STSOAuth2Manager.GetAccessToken();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#ExecuteRefreshToken = async (): Promise<string | null> => {
|
|
67
|
+
debug(`WorkerPrimaryTestRunner01::ExecuteRefreshToken`)
|
|
68
|
+
const retVal = await this.#STSOAuth2Manager.ExecuteRefreshToken();
|
|
69
|
+
if (retVal) {
|
|
70
|
+
const access_token = await this.#STSOAuth2Manager.GetAccessToken();
|
|
71
|
+
if (access_token) {
|
|
72
|
+
return access_token;
|
|
73
|
+
} else {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
*/
|
|
81
|
+
}
|