flexi-bench 0.0.0-alpha.3 → 0.0.0-alpha.4

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.
@@ -1,33 +1,33 @@
1
1
  import { Variation } from './variation';
2
- import { BenchmarkReporter, Result } from './api-types';
3
- import { ActionMethod } from './api-types';
4
- import { TeardownMethod } from './api-types';
5
- import { SetupMethod } from './api-types';
6
- export declare class Benchmark {
2
+ import { TeardownMethod, SetupMethod, Action, BenchmarkReporter } from './api-types';
3
+ import { Result } from './results';
4
+ import { PerformanceObserverOptions } from './performance-observer';
5
+ import { BenchmarkBase } from './shared-api';
6
+ export declare class Benchmark extends BenchmarkBase {
7
7
  name: string;
8
- private setupMethods;
9
- private teardownMethods;
10
- private action?;
11
8
  variations: Variation[];
12
9
  private iterations?;
13
10
  private timeout?;
14
11
  private reporter;
12
+ private watcher?;
15
13
  constructor(name: string, options?: {
16
14
  setup?: SetupMethod;
17
15
  teardown?: TeardownMethod;
18
- action?: ActionMethod;
16
+ action?: Action;
19
17
  iterations?: number;
20
18
  timeout?: number;
21
19
  reporter?: BenchmarkReporter;
22
20
  });
23
21
  withVariation(name: string, builder: (variation: Variation) => Variation): this;
22
+ withVariation(variation: Variation): this;
24
23
  withVariations(variations: Variation[]): this;
25
- withSetup(setup: () => void): this;
26
- withTeardown(teardown: () => void): this;
27
- withAction(action: () => void): this;
24
+ withSetup(setup: SetupMethod): this;
25
+ withTeardown(teardown: TeardownMethod): this;
26
+ withAction(action: Action): this;
28
27
  withIterations(iterations: number): this;
29
28
  withTimeout(timeout: number): this;
30
29
  withReporter(reporter: BenchmarkReporter): this;
30
+ withPerformanceObserver(options?: PerformanceObserverOptions): this;
31
31
  run(): Promise<Result[]>;
32
32
  private validate;
33
33
  }
package/dist/benchmark.js CHANGED
@@ -10,13 +10,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.Benchmark = void 0;
13
- const benchmark_console_reporter_1 = require("./benchmark-console-reporter");
13
+ const benchmark_console_reporter_1 = require("./reporters/benchmark-console-reporter");
14
14
  const variation_1 = require("./variation");
15
- class Benchmark {
15
+ const child_process_1 = require("child_process");
16
+ const results_1 = require("./results");
17
+ const performance_observer_1 = require("./performance-observer");
18
+ const shared_api_1 = require("./shared-api");
19
+ class Benchmark extends shared_api_1.BenchmarkBase {
16
20
  constructor(name, options) {
21
+ super();
17
22
  this.name = name;
18
- this.setupMethods = [];
19
- this.teardownMethods = [];
20
23
  this.variations = [];
21
24
  if (options === null || options === void 0 ? void 0 : options.action) {
22
25
  this.action = options.action;
@@ -35,8 +38,17 @@ class Benchmark {
35
38
  }
36
39
  this.reporter = (options === null || options === void 0 ? void 0 : options.reporter) || new benchmark_console_reporter_1.BenchmarkConsoleReporter();
37
40
  }
38
- withVariation(name, builder) {
39
- this.variations.push(builder(new variation_1.Variation(name)));
41
+ withVariation(nameOrVariation, builder) {
42
+ if (nameOrVariation instanceof variation_1.Variation) {
43
+ this.variations.push(nameOrVariation);
44
+ }
45
+ else if (builder) {
46
+ this.variations.push(builder(new variation_1.Variation(nameOrVariation)));
47
+ return this;
48
+ }
49
+ else {
50
+ throw new Error('`withVariation` must be called with either a Variation or a name and a builder function.');
51
+ }
40
52
  return this;
41
53
  }
42
54
  withVariations(variations) {
@@ -67,8 +79,13 @@ class Benchmark {
67
79
  this.reporter = reporter;
68
80
  return this;
69
81
  }
82
+ withPerformanceObserver(options) {
83
+ this.watcher = new performance_observer_1.PerformanceWatcher(options);
84
+ return this;
85
+ }
70
86
  run() {
71
87
  return __awaiter(this, void 0, void 0, function* () {
88
+ var _a, _b;
72
89
  this.validate();
73
90
  let results = [];
74
91
  if (this.variations.length === 0) {
@@ -107,14 +124,20 @@ class Benchmark {
107
124
  : null;
108
125
  let running = true;
109
126
  while (running) {
127
+ for (const setup of this.setupEachMethods.concat(variation.setupEachMethods)) {
128
+ yield setup(variation);
129
+ }
110
130
  const a = performance.now();
111
131
  if (variation.action) {
112
- yield variation.action(variation);
132
+ yield runAction(variation.action, variation);
113
133
  }
114
134
  else if (this.action) {
115
- yield this.action(variation);
135
+ yield runAction(this.action, variation);
116
136
  }
117
137
  const b = performance.now();
138
+ for (const teardown of this.teardownEachMethods.concat(variation.teardownEachMethods)) {
139
+ yield teardown(variation);
140
+ }
118
141
  const duration = b - a;
119
142
  completedIterations++;
120
143
  totalCompletedIterations++;
@@ -155,25 +178,38 @@ class Benchmark {
155
178
  }
156
179
  process.env = oldEnv;
157
180
  // REPORT
158
- const min = Math.min(...timings);
159
- const max = Math.max(...timings);
160
- const sum = timings.reduce((a, b) => a + b, 0);
161
- const average = sum / timings.length;
162
- const sortedTimings = [...timings].sort((a, b) => a - b);
163
- const p95 = sortedTimings[Math.floor(sortedTimings.length * 0.95)];
164
- results.push({
165
- label: variation.name,
166
- min,
167
- max,
168
- average,
169
- p95,
170
- });
181
+ const result = (0, results_1.calculateResultsFromDurations)(variation.name, timings);
182
+ // PerformanceObserver needs a chance to flush
183
+ if (this.watcher) {
184
+ const measures = yield this.watcher.getMeasures();
185
+ for (const key in measures) {
186
+ (_a = result.subresults) !== null && _a !== void 0 ? _a : (result.subresults = []);
187
+ result.subresults.push((0, results_1.calculateResultsFromDurations)(key, measures[key]));
188
+ }
189
+ this.watcher.clearMeasures();
190
+ }
191
+ results.push(result);
171
192
  }
193
+ (_b = this.watcher) === null || _b === void 0 ? void 0 : _b.disconnect();
172
194
  this.reporter.report(this, results);
173
195
  return results;
174
196
  });
175
197
  }
176
198
  validate() {
199
+ if (!this.timeout && !this.iterations) {
200
+ this.iterations = 5;
201
+ }
202
+ const missingActions = [];
203
+ for (const variation of this.variations) {
204
+ let action = variation.action || this.action;
205
+ if (!action) {
206
+ missingActions.push(variation.name);
207
+ continue;
208
+ }
209
+ if (typeof action !== 'string' && variation.cliArgs.length > 0) {
210
+ throw new Error(`Cannot use CLI args with a non-command action for ${this.name}:${variation.name}`);
211
+ }
212
+ }
177
213
  if (!this.action) {
178
214
  const missingActions = this.variations.filter((v) => !v.action);
179
215
  if (missingActions.length > 0) {
@@ -188,3 +224,26 @@ class Benchmark {
188
224
  }
189
225
  }
190
226
  exports.Benchmark = Benchmark;
227
+ function runAction(action, variation) {
228
+ return __awaiter(this, void 0, void 0, function* () {
229
+ if (typeof action === 'string') {
230
+ return new Promise((resolve, reject) => {
231
+ const child = (0, child_process_1.spawn)(action, variation.cliArgs, {
232
+ shell: true,
233
+ windowsHide: true,
234
+ });
235
+ child.on('exit', (code) => {
236
+ if (code === 0) {
237
+ resolve();
238
+ }
239
+ else {
240
+ reject(`Action failed with code ${code}`);
241
+ }
242
+ });
243
+ });
244
+ }
245
+ else {
246
+ return action(variation);
247
+ }
248
+ });
249
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
+ export * from './reporters/benchmark-console-reporter';
2
+ export * from './reporters/markdown-benchmark-reporter';
3
+ export * from './reporters/suite-console-reporter';
1
4
  export * from './api-types';
2
5
  export * from './benchmark';
3
6
  export * from './variation';
4
- export * from './benchmark-console-reporter';
5
7
  export * from './suite';
8
+ export * from './performance-observer';
9
+ export * from './benchmark-runner';
package/dist/index.js CHANGED
@@ -14,8 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./reporters/benchmark-console-reporter"), exports);
18
+ __exportStar(require("./reporters/markdown-benchmark-reporter"), exports);
19
+ __exportStar(require("./reporters/suite-console-reporter"), exports);
17
20
  __exportStar(require("./api-types"), exports);
18
21
  __exportStar(require("./benchmark"), exports);
19
22
  __exportStar(require("./variation"), exports);
20
- __exportStar(require("./benchmark-console-reporter"), exports);
21
23
  __exportStar(require("./suite"), exports);
24
+ __exportStar(require("./performance-observer"), exports);
25
+ __exportStar(require("./benchmark-runner"), exports);
@@ -0,0 +1,12 @@
1
+ export declare class PerformanceWatcher {
2
+ private observer?;
3
+ private measures;
4
+ constructor(opts?: PerformanceObserverOptions);
5
+ getMeasures(): Promise<Record<string, number[]>>;
6
+ clearMeasures(): void;
7
+ disconnect(): void;
8
+ }
9
+ export type PerformanceObserverOptions = {
10
+ measureFilter?: (entry: PerformanceEntry) => boolean;
11
+ label?: Record<string, string> | ((label: string) => string);
12
+ };
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PerformanceWatcher = void 0;
13
+ const node_perf_hooks_1 = require("node:perf_hooks");
14
+ class PerformanceWatcher {
15
+ constructor(opts = {}) {
16
+ this.measures = {};
17
+ this.observer = new node_perf_hooks_1.PerformanceObserver((list) => {
18
+ var _a;
19
+ var _b;
20
+ for (const entry of list.getEntries()) {
21
+ if (entry.entryType === 'measure' &&
22
+ (!opts.measureFilter || opts.measureFilter(entry))) {
23
+ const label = normalizeLabel(entry.name, opts === null || opts === void 0 ? void 0 : opts.label);
24
+ (_a = (_b = this.measures)[label]) !== null && _a !== void 0 ? _a : (_b[label] = []);
25
+ this.measures[label].push(entry.duration);
26
+ }
27
+ }
28
+ });
29
+ this.observer.observe({ entryTypes: ['measure'] });
30
+ }
31
+ // This method is async to give the observer time to collect the measures...
32
+ // If the action being ran by the benchmark is synchronous, the observer will
33
+ // not fire until the next tick so we need that to happen before the measures are
34
+ // retrieved. Since this method is async, it must be awaited, which actually gives
35
+ // the observer time to collect the measures.
36
+ getMeasures() {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ return this.measures;
39
+ });
40
+ }
41
+ clearMeasures() {
42
+ for (const key in this.measures) {
43
+ delete this.measures[key];
44
+ }
45
+ }
46
+ disconnect() {
47
+ var _a;
48
+ (_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
49
+ delete this.observer;
50
+ }
51
+ }
52
+ exports.PerformanceWatcher = PerformanceWatcher;
53
+ function normalizeLabel(label, transform) {
54
+ if (transform) {
55
+ return typeof transform === 'function'
56
+ ? transform(label)
57
+ : transform[label];
58
+ }
59
+ return label;
60
+ }
@@ -1,7 +1,9 @@
1
- import { Result, BenchmarkReporter, ProgressContext } from './api-types';
2
- import { Benchmark } from './benchmark';
1
+ import { BenchmarkReporter, ProgressContext } from '../api-types';
2
+ import { Benchmark } from '../benchmark';
3
+ import { SingleBar } from 'cli-progress';
4
+ import { Result } from '../results';
3
5
  export declare class BenchmarkConsoleReporter implements BenchmarkReporter {
4
- private bar;
6
+ bar: SingleBar;
5
7
  constructor();
6
8
  progress(name: string, percent: number, context: ProgressContext): void;
7
9
  report(benchmark: Benchmark, results: Result[]): void;
@@ -5,10 +5,12 @@ const cli_progress_1 = require("cli-progress");
5
5
  class BenchmarkConsoleReporter {
6
6
  constructor() {
7
7
  this.bar = new cli_progress_1.SingleBar({
8
- format: 'Running: {label}\n{bar} {percentage}% | {value}/{total} - ETA: {eta}s',
8
+ format: 'Running variation {label}: {bar} {percentage}% | {value}/{total} - ETA: {eta}s',
9
9
  barCompleteChar: '\u2588',
10
10
  barIncompleteChar: '\u2591',
11
11
  hideCursor: true,
12
+ stopOnComplete: true,
13
+ clearOnComplete: true,
12
14
  });
13
15
  }
14
16
  progress(name, percent, context) {
@@ -17,13 +19,8 @@ class BenchmarkConsoleReporter {
17
19
  this.bar.start((_a = context.totalIterations) !== null && _a !== void 0 ? _a : 100, 0);
18
20
  }
19
21
  this.bar.update(context.completedIterations, { label: name });
20
- process.stdout.moveCursor(0, -1);
21
- // console.log(`Progress: ${name} ${Math.round(percent * 10000) / 100}%`);
22
22
  }
23
23
  report(benchmark, results) {
24
- this.bar.stop();
25
- process.stdout.moveCursor(0, -1);
26
- process.stdout.clearScreenDown();
27
24
  console.log(`Benchmark: ${benchmark.name}`);
28
25
  console.table(results);
29
26
  }
@@ -0,0 +1,12 @@
1
+ import { BenchmarkReporter } from '../api-types';
2
+ import { Benchmark } from '../benchmark';
3
+ import { Result } from '../results';
4
+ export declare class MarkdownBenchmarkReporter implements BenchmarkReporter {
5
+ outputFile: string;
6
+ fields: Array<keyof Omit<Result, 'subresults' | 'label'>>;
7
+ constructor(opts: {
8
+ outputFile: string;
9
+ fields?: MarkdownBenchmarkReporter['fields'];
10
+ });
11
+ report: (benchmark: Benchmark, results: Result[]) => void;
12
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MarkdownBenchmarkReporter = void 0;
4
+ const fs_1 = require("fs");
5
+ const markdown_factory_1 = require("markdown-factory");
6
+ class MarkdownBenchmarkReporter {
7
+ constructor(opts) {
8
+ var _a;
9
+ this.report = (benchmark, results) => {
10
+ (0, fs_1.writeFileSync)(this.outputFile, (0, markdown_factory_1.h1)(benchmark.name, results.some((r) => !!r.subresults)
11
+ ? (0, markdown_factory_1.lines)(results.map((r) => {
12
+ var _a;
13
+ const entries = [Object.assign(Object.assign({}, r), { label: 'total' })];
14
+ delete entries[0].subresults;
15
+ for (const subresult of (_a = r.subresults) !== null && _a !== void 0 ? _a : []) {
16
+ entries.push(subresult);
17
+ }
18
+ return (0, markdown_factory_1.h2)(r.label, (0, markdown_factory_1.table)(entries, [
19
+ { field: 'label', label: '' },
20
+ ...this.fields,
21
+ ]));
22
+ }))
23
+ : (0, markdown_factory_1.table)(results, [{ field: 'label', label: '' }, ...this.fields])));
24
+ };
25
+ this.outputFile = opts.outputFile;
26
+ this.fields = (_a = opts.fields) !== null && _a !== void 0 ? _a : ['min', 'average', 'p95', 'max'];
27
+ }
28
+ }
29
+ exports.MarkdownBenchmarkReporter = MarkdownBenchmarkReporter;
@@ -0,0 +1,4 @@
1
+ import { BenchmarkReporter, SuiteReporter } from '../api-types';
2
+ export declare class NoopReporter implements BenchmarkReporter, SuiteReporter {
3
+ report: () => void;
4
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoopReporter = void 0;
4
+ class NoopReporter {
5
+ constructor() {
6
+ this.report = () => { };
7
+ }
8
+ }
9
+ exports.NoopReporter = NoopReporter;
@@ -1,4 +1,5 @@
1
- import { Result, SuiteReporter } from './api-types';
1
+ import { SuiteReporter } from '../api-types';
2
+ import { Result } from '../results';
2
3
  export declare class SuiteConsoleReporter implements SuiteReporter {
3
4
  report: (results: Record<string, Result[]>) => void;
4
5
  }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * A measurement result.
3
+ */
4
+ export type Result = {
5
+ /**
6
+ * The label of the result. Typically the name of the benchmark or variation.
7
+ */
8
+ label: string;
9
+ /**
10
+ * The minimum duration.
11
+ */
12
+ min: number;
13
+ /**
14
+ * The maximum duration.
15
+ */
16
+ max: number;
17
+ /**
18
+ * The average duration.
19
+ */
20
+ average: number;
21
+ /**
22
+ * The 95th percentile duration.
23
+ */
24
+ p95: number;
25
+ /**
26
+ * The raw durations, in order. Used for custom reporters.
27
+ */
28
+ raw: number[];
29
+ /**
30
+ * Subresults, if any. Typically sourced from performance observer.
31
+ */
32
+ subresults?: Result[];
33
+ };
34
+ export declare function calculateResultsFromDurations(label: string, durations: number[]): Result;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateResultsFromDurations = calculateResultsFromDurations;
4
+ function calculateResultsFromDurations(label, durations) {
5
+ durations.sort((a, b) => a - b);
6
+ return {
7
+ label,
8
+ min: Math.min(...durations),
9
+ max: Math.max(...durations),
10
+ average: durations.reduce((acc, duration) => acc + duration, 0) / durations.length,
11
+ p95: durations[Math.floor(durations.length * 0.95)],
12
+ raw: durations,
13
+ };
14
+ }
@@ -0,0 +1,13 @@
1
+ import { Action, SetupMethod, TeardownMethod } from './api-types';
2
+ export declare class BenchmarkBase {
3
+ setupEachMethods: SetupMethod[];
4
+ setupMethods: SetupMethod[];
5
+ teardownMethods: TeardownMethod[];
6
+ teardownEachMethods: TeardownMethod[];
7
+ action?: Action;
8
+ withSetup(setup: SetupMethod): this;
9
+ withSetupEach(setup: SetupMethod): this;
10
+ withTeardown(teardown: TeardownMethod): this;
11
+ withTeardownEach(teardown: TeardownMethod): this;
12
+ withAction(action: Action): this;
13
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BenchmarkBase = void 0;
4
+ class BenchmarkBase {
5
+ constructor() {
6
+ this.setupEachMethods = [];
7
+ this.setupMethods = [];
8
+ this.teardownMethods = [];
9
+ this.teardownEachMethods = [];
10
+ }
11
+ withSetup(setup) {
12
+ this.setupMethods.push(setup);
13
+ return this;
14
+ }
15
+ withSetupEach(setup) {
16
+ this.setupEachMethods.push(setup);
17
+ return this;
18
+ }
19
+ withTeardown(teardown) {
20
+ this.teardownMethods.push(teardown);
21
+ return this;
22
+ }
23
+ withTeardownEach(teardown) {
24
+ this.teardownEachMethods.push(teardown);
25
+ return this;
26
+ }
27
+ withAction(action) {
28
+ this.action = action;
29
+ return this;
30
+ }
31
+ }
32
+ exports.BenchmarkBase = BenchmarkBase;
package/dist/suite.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { SuiteReporter } from './api-types';
1
+ import { BenchmarkReporter, SuiteReporter } from './api-types';
2
2
  import { Benchmark } from './benchmark';
3
+ import { Result } from './results';
3
4
  import { Variation } from './variation';
4
5
  export interface SuiteOptions {
5
6
  reporter?: SuiteReporter;
@@ -9,9 +10,12 @@ export declare class Suite {
9
10
  private benchmarks;
10
11
  private variations;
11
12
  private reporter;
13
+ private benchmarkReporter?;
12
14
  constructor(name: string, options?: SuiteOptions);
13
15
  addBenchmark(benchmark: Benchmark): this;
14
- addVariation(variation: Variation): this;
15
- addVariations(variations: Variation[]): this;
16
- run(): Promise<void>;
16
+ withVariation(variation: Variation): this;
17
+ withVariations(variations: Variation[]): this;
18
+ withReporter(reporter: SuiteReporter): this;
19
+ withBenchmarkReporter(reporter: BenchmarkReporter): this;
20
+ run(): Promise<Record<string, Result[]>>;
17
21
  }
package/dist/suite.js CHANGED
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.Suite = void 0;
13
- const suite_console_reporter_1 = require("./suite-console-reporter");
13
+ const suite_console_reporter_1 = require("./reporters/suite-console-reporter");
14
14
  class Suite {
15
15
  constructor(name, options) {
16
16
  this.name = name;
@@ -24,22 +24,34 @@ class Suite {
24
24
  this.benchmarks.push(benchmark);
25
25
  return this;
26
26
  }
27
- addVariation(variation) {
27
+ withVariation(variation) {
28
28
  this.variations.push(variation);
29
29
  return this;
30
30
  }
31
- addVariations(variations) {
31
+ withVariations(variations) {
32
32
  this.variations.push(...variations);
33
33
  return this;
34
34
  }
35
+ withReporter(reporter) {
36
+ this.reporter = reporter;
37
+ return this;
38
+ }
39
+ withBenchmarkReporter(reporter) {
40
+ this.benchmarkReporter = reporter;
41
+ return this;
42
+ }
35
43
  run() {
36
44
  return __awaiter(this, void 0, void 0, function* () {
37
45
  const results = {};
38
46
  for (const benchmark of this.benchmarks) {
39
47
  benchmark.withVariations(this.variations);
48
+ if (this.benchmarkReporter) {
49
+ benchmark.withReporter(this.benchmarkReporter);
50
+ }
40
51
  results[benchmark.name] = yield benchmark.run();
41
52
  }
42
53
  this.reporter.report(results);
54
+ return results;
43
55
  });
44
56
  }
45
57
  }
@@ -0,0 +1 @@
1
+ export declare function findCombinations<T>(variables: T[][]): T[][];
package/dist/utils.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findCombinations = findCombinations;
4
+ function findCombinations(variables) {
5
+ // Generates all possible combinations of the given variables.
6
+ // e.g. findCombinations([[1, 2], [3, 4]]) => [[1, 3], [1, 4], [2, 3], [2, 4]]
7
+ return variables.reduce((acc, values) => {
8
+ return acc.flatMap((accItem) => {
9
+ return values.map((value) => {
10
+ return [...accItem, value];
11
+ });
12
+ });
13
+ }, [[]]);
14
+ }
@@ -1,15 +1,29 @@
1
- import { SetupMethod, TeardownMethod, ActionMethod, EnvironmentVariableOptions } from './api-types';
2
- export declare class Variation {
1
+ import { EnvironmentVariableOptions } from './api-types';
2
+ import { BenchmarkBase } from './shared-api';
3
+ export declare class Variation extends BenchmarkBase {
3
4
  name: string;
4
- setupMethods: SetupMethod[];
5
- teardownMethods: TeardownMethod[];
6
5
  environment: Partial<NodeJS.ProcessEnv>;
7
- action?: ActionMethod;
6
+ cliArgs: string[];
8
7
  constructor(name: string);
9
8
  static FromEnvironmentVariables(variables: EnvironmentVariableOptions): Variation[];
10
- withSetup(setup: SetupMethod): this;
11
- withTeardown(teardown: TeardownMethod): this;
9
+ /**
10
+ *
11
+ * @param args An array of options to pass to the CLI. If an element is an array, they will be treated as alternatives.
12
+ * @returns An array of variations. Can be applied with `withVariations`.
13
+ * @example
14
+ * // Creates 2 variations, both with --flag, but one with --no-daemon and the other with --daemon.
15
+ * const variations = Variation.FromCliArgs([
16
+ * '--flag',
17
+ * ['--no-daemon', '--daemon'],
18
+ * ]);
19
+ *
20
+ * // Creates 4 variations, with combinations of --a and --b.
21
+ * const variations = Variation.FromCliArgs([
22
+ * ['--a', '--b'],
23
+ * ]);
24
+ */
25
+ static FromCliArgs(args: Array<string | string[]>): Variation[];
12
26
  withEnvironmentVariables(env: Partial<NodeJS.ProcessEnv>): this;
13
27
  withEnvironmentVariable(name: string, value: string): this;
14
- withAction(action: ActionMethod): this;
28
+ withCliArgs(...args: string[]): this;
15
29
  }