@loadmill/executer 0.1.52 → 0.1.55

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.
Files changed (44) hide show
  1. package/dist/parameter-pools.d.ts +3 -0
  2. package/dist/parameter-pools.js +69 -0
  3. package/dist/parameter-pools.js.map +1 -0
  4. package/dist/post-script/post-script-executor.d.ts +12 -2
  5. package/dist/post-script/post-script-executor.js +28 -6
  6. package/dist/post-script/post-script-executor.js.map +1 -1
  7. package/dist/sequence.d.ts +6 -1
  8. package/dist/sequence.js +51 -18
  9. package/dist/sequence.js.map +1 -1
  10. package/dist/single-runner.d.ts +1 -0
  11. package/dist/single-runner.js +4 -1
  12. package/dist/single-runner.js.map +1 -1
  13. package/package.json +3 -3
  14. package/src/asserter.ts +0 -137
  15. package/src/errors.ts +0 -10
  16. package/src/extraction-combiner.ts +0 -110
  17. package/src/failures.ts +0 -79
  18. package/src/message-creators.ts +0 -44
  19. package/src/mill-info.ts +0 -81
  20. package/src/mill-version.ts +0 -7
  21. package/src/post-script/ast-walker/index.ts +0 -160
  22. package/src/post-script/ast-walker/type-guard.ts +0 -73
  23. package/src/post-script/ast-walker/types.ts +0 -35
  24. package/src/post-script/console-log.ts +0 -24
  25. package/src/post-script/parser/acorn-js-parser.ts +0 -8
  26. package/src/post-script/parser/js-parser.ts +0 -22
  27. package/src/post-script/parser/parser.ts +0 -5
  28. package/src/post-script/post-script-executor.ts +0 -93
  29. package/src/post-script/virtual-machine/virtual-machine.ts +0 -15
  30. package/src/post-script/virtual-machine/vm2-virtual-machine.ts +0 -45
  31. package/src/report-types.ts +0 -127
  32. package/src/request-sequence-result.ts +0 -63
  33. package/src/request-stats.ts +0 -20
  34. package/src/res-keeper.ts +0 -53
  35. package/src/sampler.ts +0 -133
  36. package/src/sequence.ts +0 -1115
  37. package/src/single-runner.ts +0 -68
  38. package/src/test-run-event-emitter.ts +0 -25
  39. package/src/utils.ts +0 -8
  40. package/src/work.ts +0 -17
  41. package/src/ws.ts +0 -286
  42. package/test/post-script-console-log.spec.ts +0 -73
  43. package/test/post-script-executor.spec.ts +0 -685
  44. package/tsconfig.json +0 -9
@@ -1,35 +0,0 @@
1
- import {
2
- ArrayPattern as ESTArrayPattern,
3
- FunctionDeclaration as ESTFunctionDeclaration,
4
- Identifier,
5
- Pattern,
6
- } from 'estree';
7
-
8
- export interface ArrayPattern extends ESTArrayPattern {
9
- elements: Array<Pattern>;
10
- }
11
-
12
- export interface FunctionDeclaration extends ESTFunctionDeclaration {
13
- id: Identifier;
14
- }
15
-
16
- export enum Declarations {
17
- FunctionDeclaration = 'FunctionDeclaration',
18
- VariableDeclaration = 'VariableDeclaration',
19
- }
20
-
21
- export enum Expressions {
22
- AssignmentExpression = 'AssignmentExpression',
23
- SequenceExpression = 'SequenceExpression',
24
- }
25
-
26
- export enum Patterns {
27
- ObjectPattern = 'ObjectPattern',
28
- ArrayPattern = 'ArrayPattern',
29
- RestElement = 'RestElement',
30
- Identifier = 'Identifier',
31
- }
32
-
33
- export enum Statements {
34
- ExpressionStatement = 'ExpressionStatement',
35
- }
@@ -1,24 +0,0 @@
1
- export const LINE_LIMIT = 1000;
2
- export const CONSOLE_LOG_LIMIT = 100;
3
-
4
- const isPassedTheLimit = (line) => line.length > LINE_LIMIT;
5
-
6
- export const myConsole = {
7
- stdout: [] as string[],
8
- log: (...args) => {
9
- if (myConsole.stdout.length >= CONSOLE_LOG_LIMIT) {
10
- return;
11
- }
12
- let line = '';
13
- for (const item of args) {
14
- line += typeof item === 'object' ? JSON.stringify(item, undefined, 2) : item;
15
- if (isPassedTheLimit(line)) {
16
- line = line.substring(0, LINE_LIMIT) + '\n...';
17
- break;
18
- }
19
- line += ' ';
20
- }
21
- !!line && myConsole.stdout.push(line);
22
- },
23
- setStdout: (newStdout) => myConsole.stdout = newStdout,
24
- };
@@ -1,8 +0,0 @@
1
- import { parse, Options } from 'acorn';
2
- import { ESTree, JSParser } from './js-parser';
3
-
4
- export class AcornJSParser implements JSParser {
5
- parse(script: string, options: Options = { ecmaVersion: 2020 }): ESTree {
6
- return parse(script, options) as ESTree;
7
- }
8
- }
@@ -1,22 +0,0 @@
1
- import { Node, Program } from 'estree';
2
- import { Parser } from './parser';
3
-
4
- interface JSParser extends Parser {
5
- parse(script: string, options?: JSParserOptions): ESTree;
6
- }
7
-
8
- type ExtendNode<T> = {
9
- [K in keyof T]: T[K] extends object ? ExtendNode<T[K]> : T[K];
10
- } & (T extends Node ? {
11
- start: number;
12
- end: number;
13
- } : {});
14
-
15
- type ESTree = ExtendNode<Program>;
16
- type JSParserOptions = { ecmaVersion: number | string }
17
-
18
- export {
19
- ESTree,
20
- JSParser,
21
- JSParserOptions,
22
- };
@@ -1,5 +0,0 @@
1
- export interface Parser {
2
- parse(script: string): AbstractSyntaxTree;
3
- }
4
-
5
- export type AbstractSyntaxTree = { [name: string]: any };
@@ -1,93 +0,0 @@
1
- import { strict as assert } from 'assert';
2
- import { SequenceExecutorParameters } from '@loadmill/core/dist/parameters';
3
- import { ParameterFunctionOperations } from '@loadmill/core/dist/parameters/parameter-functions/parameter-functions';
4
- import { ASTWalker } from './ast-walker';
5
- import { VM2VirtualMachine } from './virtual-machine/vm2-virtual-machine';
6
- import { myConsole } from './console-log';
7
-
8
- export class PostScriptRunner {
9
- public run = (
10
- staticContext: OuterStaticContext = { staticContext: { $: {}, __: {} } },
11
- dynamicContext: SequenceExecutorParameters = {},
12
- userCode: string = ''
13
- ): SequenceExecutorParameters => {
14
- const newVariables = new ASTWalker(userCode).detectDeclarations(Object.keys(staticContext));
15
- const wrappedUserCode = this.wrapUserCode(dynamicContext, userCode, newVariables);
16
- const stdout = [];
17
- myConsole.setStdout(stdout);
18
-
19
- const vm = new VM2VirtualMachine();
20
- vm.create({
21
- globalObject: { assert, ...dynamicContext, console: myConsole },
22
- staticObject: staticContext,
23
- });
24
-
25
- const modifiedDynamicContext = vm.run(wrappedUserCode);
26
- this.validateCircularDependency(modifiedDynamicContext);
27
- this.removeAssertModule(modifiedDynamicContext);
28
- return { result: modifiedDynamicContext, stdout: myConsole.stdout };
29
- };
30
-
31
- private wrapUserCode = (
32
- dynamicContext: SequenceExecutorParameters = {},
33
- userCode: string = '',
34
- newVariables: string[] = []
35
- ) => {
36
- const prefix =
37
- `
38
- Buffer = {};
39
- global = { assert: global.assert, console: global.console };
40
- const $ = staticContext.$;
41
- const __ = staticContext.__;
42
- `;
43
-
44
- const suffix =
45
- `
46
- exports = {
47
- ${[...Object.keys(dynamicContext), ...newVariables].join()}
48
- };
49
- `;
50
-
51
- return prefix + userCode + suffix;
52
- }
53
-
54
- validateCircularDependency = (obj: SequenceExecutorParameters) => {
55
- function circularError (propName: string) {
56
- return new TypeError(`Cyclic object value. Circular reference in property ${propName}`);
57
- }
58
-
59
- findCircular(obj);
60
-
61
- function findCircular(
62
- obj: SequenceExecutorParameters,
63
- _refs: WeakSet<SequenceExecutorParameters> = new WeakSet()
64
- ) {
65
- if (typeof obj === 'object' && obj !== null) {
66
- _refs.add(obj);
67
- for (const key in obj) {
68
- if (_refs.has(obj[key])) {
69
- throw circularError(key);
70
- }
71
- findCircular(obj[key], _refs);
72
- }
73
- _refs.delete(obj);
74
- }
75
- }
76
- };
77
-
78
- private removeAssertModule(context: SequenceExecutorParameters) {
79
- Object.keys(context).forEach(key => {
80
- if (context[key]?.assert?.name === 'strict') {
81
- context[key] = {};
82
- }
83
- });
84
- }
85
- }
86
- type OuterStaticContext = {
87
- staticContext: StaticContext;
88
- };
89
-
90
- type StaticContext = {
91
- $: SequenceExecutorParameters;
92
- __: ParameterFunctionOperations;
93
- };
@@ -1,15 +0,0 @@
1
- export interface VirtualMachine {
2
- engine: Engine;
3
- create(options: VirtualMachineOptions): Engine;
4
- run(code: string): object;
5
- }
6
-
7
- export interface VirtualMachineOptions {
8
- globalObject?: object;
9
- staticObject?: object;
10
- timeout?: number;
11
- memoryLimit?: number;
12
- async?: boolean;
13
- }
14
-
15
- export type Engine = any;
@@ -1,45 +0,0 @@
1
- import { VM } from 'vm2';
2
- import { SequenceExecutorParameters } from '../../../../core/dist/parameters';
3
-
4
- import { Engine, VirtualMachine, VirtualMachineOptions } from './virtual-machine';
5
-
6
- export class VM2VirtualMachine implements VirtualMachine {
7
- public engine: VM;
8
- private DEFAULT_TIMOUT_MS = Number(process.env.POSTSCRIPT_TIMEOUT_MS) || 1000;
9
-
10
- create(options: VirtualMachineOptions): Engine {
11
- const { timeout, globalObject, async, staticObject } = options;
12
- this.engine = new VM({
13
- timeout: timeout || this.DEFAULT_TIMOUT_MS,
14
- fixAsync: !async,
15
- eval: false,
16
- wasm: false,
17
- });
18
-
19
- this.makeStatic(staticObject);
20
-
21
- this.setGlobals(globalObject);
22
- }
23
-
24
- makeStatic(obj: object = {}) {
25
- this.deepFreeze(obj);
26
- }
27
-
28
- deepFreeze = (obj: object) => {
29
- for (const key of Object.keys(obj)) {
30
- if (typeof obj[key] === 'object' && obj[key] !== null) {
31
- this.engine.freeze(obj[key], key); // freeze me
32
- this.deepFreeze(obj[key]); // freeze my childrens
33
- }
34
- }
35
- return this.engine.freeze(obj); // freeze root
36
- };
37
-
38
- setGlobals = (globalObject: SequenceExecutorParameters = {}) => {
39
- this.engine.setGlobals(globalObject);
40
- }
41
-
42
- run(code: string): SequenceExecutorParameters {
43
- return this.engine.run(code);
44
- }
45
- }
@@ -1,127 +0,0 @@
1
- import { Failures } from './failures';
2
- import { MillInfo } from './mill-info';
3
- import { PerRequestResTime, PerRequestStats } from './request-stats';
4
-
5
- export type LoadResult = 'done' | 'aborted' | 'failed';
6
-
7
- /**
8
- * This report contains all the info that comes from Redis each time there is a status update.
9
- */
10
- export interface LoadStatusReport extends AggregatedStatsReport {
11
- endTime?: number;
12
- startTime?: number;
13
- result?: LoadResult;
14
- activeSessions?: number;
15
- }
16
-
17
- /**
18
- * This report contains all the aggregated stats computed from individual mill reports.
19
- */
20
- export interface AggregatedStatsReport extends BasicStatsReport {
21
- /**
22
- * The number of separate sessions / mills that sent at least one report.
23
- */
24
- reportedSessions?: number;
25
-
26
- append?: boolean;
27
- failures?: Failures;
28
- browserStatsMap?: StatsMap;
29
- locationStatsMap?: StatsMap;
30
- serverMetrics?: ServerMetrics;
31
- avgResTimeSequence?: SequenceObj;
32
- requestStats?: PerRequestResTime;
33
- failureReportsMap?: HashKeysToReportIds;
34
- }
35
-
36
- /**
37
- * A mapping that aggregates stats according to a certain feature, e.g. stats per browser version.
38
- */
39
- export interface StatsMap {
40
- [feature: string]: BasicStatsReport & {
41
- sessions: number;
42
- };
43
- }
44
-
45
- /**
46
- * A sequence of stats (currently only the average response time) reported within a certain
47
- * interval (between `firstTime` and `lastTime`).
48
- */
49
- export interface SequenceObj {
50
- append?: boolean;
51
- sequence: ClientMetrics;
52
- }
53
-
54
- export type ClientMetrics = ClientMetricsClump[];
55
-
56
- export type ClientMetricsClump = {
57
- s?: number;
58
- f?: number;
59
- avg: number;
60
- time: number;
61
- p50?: number;
62
- p95?: number;
63
- weight: number;
64
- concurrency: number;
65
- };
66
-
67
- export type ServerMetrics = ServerMetricsClump[];
68
-
69
- export type ServerMetricsClump = {
70
- _t: number;
71
-
72
- uc?: number;
73
- tc?: number;
74
-
75
- tm?: number;
76
- hm?: number;
77
- uhm?: number;
78
- };
79
-
80
- export type HashKeysToReportIds = { [hashKey: string]: string };
81
-
82
- /**
83
- * This is the report sent to Redis every time a mill report is received.
84
- */
85
- export type ToStoreStatsReport = StatsReportWithMillInfo & {
86
- timestamp: number;
87
- firstReport: boolean;
88
- };
89
-
90
- /**
91
- * This report includes a single mill stats report along with additional meta data.
92
- */
93
- export type StatsReportWithMillInfo = FromMillStatsReport &
94
- MillInfo & {
95
- lordId: string;
96
- };
97
-
98
- /**
99
- * The stats report sent by the mill.
100
- */
101
- export interface FromMillStatsReport extends BasicStatsReport {
102
- failures?: Failures;
103
- requestStats?: PerRequestStats;
104
- }
105
-
106
- /**
107
- * The basic stats we are usually interested in.
108
- */
109
- export interface BasicStatsReport {
110
- hits?: number;
111
- avgResTime?: number;
112
- failedIterations?: number;
113
- successfulIterations?: number;
114
- }
115
-
116
- export function isInformative({
117
- hits,
118
- avgResTime,
119
- failedIterations,
120
- successfulIterations,
121
- }: FromMillStatsReport) {
122
- return (
123
- (hits && hits > 0 && avgResTime != null && avgResTime >= 0) ||
124
- (failedIterations && failedIterations > 0) ||
125
- (successfulIterations && successfulIterations > 0)
126
- );
127
- }
@@ -1,63 +0,0 @@
1
- import { Failures } from './failures';
2
- import { PerRequestStats } from './request-stats';
3
- import { Parameters } from '@loadmill/core/dist/parameters';
4
- import {
5
- LoadmillHeaders,
6
- LoadmillRequest,
7
- PostFormData,
8
- RequestPostData,
9
- } from '@loadmill/core/dist/request';
10
-
11
- export function extendSequenceResult(
12
- result: RequestSequenceResult,
13
- requests: LoadmillRequest[]
14
- ): ExtendedSequenceResult {
15
- return {
16
- ...result,
17
- resolvedRequests: requests.map((request, index: number) => ({
18
- ...request,
19
- ...result.resolvedRequests[index],
20
- })),
21
- };
22
- }
23
-
24
- export interface ExtendedSequenceResult extends RequestSequenceResult {
25
- resolvedRequests: ExtendedRequest[];
26
- err?: string;
27
- }
28
-
29
- export interface RequestSequenceResult {
30
- failures: Failures;
31
- avgResTime: number;
32
- successfulHits: number;
33
- lastStartedIndex: number;
34
- requestStats: PerRequestStats;
35
- resolvedRequests: ResolvedRequest[];
36
- }
37
-
38
- export type ExtendedRequest = LoadmillRequest & ResolvedRequest;
39
-
40
- export interface ResolvedRequest {
41
- url?: string;
42
- headers?: LoadmillHeaders[];
43
- postData?: RequestPostData;
44
- postFormData?: PostFormData;
45
-
46
- response?: {
47
- type: string;
48
- text: string;
49
- status: number;
50
- statusText: string;
51
- headers: LoadmillHeaders[];
52
- };
53
-
54
- postParameters?: Parameters[];
55
- stdout?: string[];
56
- unexpectedError?: {
57
- stack: string;
58
- message: string;
59
- properties: string;
60
- };
61
-
62
- retried?: number;
63
- }
@@ -1,20 +0,0 @@
1
- export interface PerRequestStats {
2
- [index: number]: RequestStats;
3
- }
4
-
5
- export interface RequestStats {
6
- resTimes: number[];
7
- }
8
-
9
- export interface PerRequestResTime {
10
- [index: number]: { weight: number; avg: number };
11
- }
12
-
13
- export function setReqStats(
14
- reqStats: PerRequestStats,
15
- index,
16
- ...resTimes: number[]
17
- ) {
18
- const stats = reqStats[index] || (reqStats[index] = { resTimes: [] });
19
- stats.resTimes.push(...resTimes);
20
- }
package/src/res-keeper.ts DELETED
@@ -1,53 +0,0 @@
1
- import { ObjectMap } from '@loadmill/universal/dist/object-map';
2
- import * as promiseUtils from '@loadmill/universal/dist/promise-utils';
3
- import { getFailureKeys } from './failures';
4
- import { RequestSequenceResult } from './request-sequence-result';
5
-
6
- export class ResKeeper {
7
- knownKeysToResults: { [key: string]: string } = {};
8
- results = new ObjectMap<RequestSequenceResult>(this.generateResultKey);
9
-
10
- constructor(private generateResultKey?) {}
11
-
12
- getKnownKeys = () => Object.keys(this.knownKeysToResults);
13
-
14
- mapToResultIds = (failureKeys) => {
15
- const map: { [key: string]: string } = {};
16
- failureKeys.forEach((key) => (map[key] = this.knownKeysToResults[key]));
17
-
18
- return map;
19
- };
20
-
21
- keepIfNeeded = (result: RequestSequenceResult, keepFor?: number) => {
22
- const failureKeys = getFailureKeys(result.failures);
23
-
24
- if (failureKeys.find((key) => !this.knownKeysToResults[key])) {
25
- // We keep it:
26
- const id = this.results.add(result);
27
- failureKeys.forEach((key) => (this.knownKeysToResults[key] = id));
28
-
29
- if (keepFor) {
30
- promiseUtils.delay(keepFor).then(() => this.results.remove(id));
31
- }
32
- }
33
- };
34
-
35
- popForKeys = (failureKeys: string[], withIds = false) => {
36
- return failureKeys
37
- .map((key) => {
38
- const id = this.knownKeysToResults[key];
39
-
40
- if (id) {
41
- const res = this.results.get(id);
42
- this.results.remove(id);
43
-
44
- if (withIds && res) {
45
- res['id'] = id;
46
- }
47
-
48
- return res;
49
- }
50
- })
51
- .filter(Boolean);
52
- };
53
- }
package/src/sampler.ts DELETED
@@ -1,133 +0,0 @@
1
- import log from '@loadmill/universal/dist/log';
2
- import * as mathUtils from '@loadmill/universal/dist/math-utils';
3
- import * as promiseUtils from '@loadmill/universal/dist/promise-utils';
4
- import { minimalRemainingDuration, reportDelay } from '@loadmill/core/dist/conf';
5
- import { runSingleIteration } from './single-runner';
6
- import { Failures, mergeFailures } from './failures';
7
- import { PerRequestStats, setReqStats } from './request-stats';
8
- import { Work } from './work';
9
- import { messageCreators } from './message-creators';
10
- import { DEFAULT_DURATION, DEFAULT_ITERATION_DELAY } from '@loadmill/core/dist/conf/defaults';
11
-
12
- // 5 minutes:
13
- const RES_KEEP_TIME = 5 * 60 * 1000;
14
-
15
- export interface SamplerFaja {
16
- cancelWork(finished: boolean);
17
- send(obj: string);
18
- reportIntervalId;
19
- isStopped: boolean;
20
- keeper;
21
- }
22
-
23
- export class Sampler {
24
- avgResTime = 0;
25
- expired = false;
26
- successfulHits = 0;
27
- failedIterations = 0;
28
- startedIterations = 0;
29
- failures: Failures = {};
30
- successfulIterations = 0;
31
- requestStats: PerRequestStats = {};
32
-
33
- constructor(private samplerMill: SamplerFaja, private work: Work) {}
34
-
35
- startSampling = () => {
36
- this.samplerMill.cancelWork(false);
37
-
38
- promiseUtils.delay(this.work.duration || DEFAULT_DURATION).then(
39
- () => {
40
- if (!this.samplerMill.isStopped) {
41
- log.debug('Time\'s up!');
42
- this.expired = true;
43
- this.sendReport();
44
- }
45
- }
46
- );
47
-
48
- const requests = this.work.requests;
49
- const iterationDelay = this.work.iterationDelay || DEFAULT_ITERATION_DELAY;
50
-
51
- const reportDelayy = reportDelay(iterationDelay, requests);
52
- this.samplerMill.reportIntervalId = setInterval(
53
- this.sendReport,
54
- reportDelayy
55
- );
56
-
57
- this.samplerMill.isStopped = false;
58
- this.runAll();
59
- };
60
-
61
- runAll = async () => {
62
- while (!this.isDone()) {
63
- ++this.startedIterations;
64
- log.trace('Executing iteration: ', this.startedIterations);
65
-
66
- const res = await runSingleIteration(this.work);
67
- this.samplerMill.keeper.keepIfNeeded(res, RES_KEEP_TIME);
68
-
69
- const resFailures = res.failures;
70
-
71
- if (!resFailures || Object.keys(resFailures).length === 0) {
72
- ++this.successfulIterations;
73
- } else {
74
- ++this.failedIterations;
75
- mergeFailures(this.failures, resFailures);
76
- }
77
-
78
- this.avgResTime = mathUtils.calcAvg(
79
- this.avgResTime,
80
- this.successfulHits,
81
- res.avgResTime,
82
- res.successfulHits
83
- );
84
- this.successfulHits += res.successfulHits;
85
-
86
- Object.keys(res.requestStats).forEach(index => {
87
- setReqStats(this.requestStats, index, ...res.requestStats[index].resTimes);
88
- });
89
-
90
-
91
- if (!this.isDone()) {
92
- // We count only the delay of the requests that were NOT ATTEMPTED.
93
- // Note that the first request delay is always ignored:
94
- const skipCount = res.lastStartedIndex + 1;
95
- await promiseUtils.delay(
96
- minimalRemainingDuration(
97
- this.work.iterationDelay || DEFAULT_ITERATION_DELAY,
98
- this.work.requests,
99
- skipCount
100
- )
101
- );
102
- }
103
- }
104
-
105
- if (!this.samplerMill.isStopped) {
106
- log.debug('Finished last iteration:', this.startedIterations);
107
- this.sendReport();
108
- this.samplerMill.cancelWork(true);
109
- }
110
- };
111
-
112
- isDone = () => {
113
- return this.samplerMill.isStopped || this.expired || this.startedIterations >= this.work.iterations;
114
- };
115
-
116
- sendReport = () => {
117
- const report = {
118
- failures: this.failures,
119
- hits: this.successfulHits,
120
- avgResTime: this.avgResTime,
121
- requestStats: this.requestStats,
122
- failedIterations: this.failedIterations,
123
- successfulIterations: this.successfulIterations,
124
- };
125
-
126
- log.trace('Sending report:', report);
127
- this.samplerMill.send(messageCreators.report(report));
128
-
129
- this.failures = {};
130
- this.requestStats = {};
131
- this.successfulIterations = this.successfulHits = this.failedIterations = this.avgResTime = 0;
132
- };
133
- }