diff --git a/README.md b/README.md index 96ac21a..cc635d3 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,60 @@ -# template-typescript-cli-app +# Perf Tests -A Repository Template for TypeScript Command Line Applications / Tools +This is a collection of perf tests. They are designed to check assumptions on performance. This is a simple command line tool that lists files matching the provided globs. ## Getting Started -1. Install [`pnpm`](https://pnppm.io) - -1. `pnpm i` - -1. `pnpm test` - -1. `pnpm run app --help` - - - - ``` - Usage: list-files [options] - - List Files - - Arguments: - files Files to scan for injected content. - - Options: - --no-must-find-files No error if files are not found. - --cwd Current Directory - --color Force color. - --no-color Do not use color. - -V, --version output the version number - -h, --help display help for command - ``` - - - -1. `pnpm run app "*"` - - **Example:** - - - - ``` - Find Files: - - LICENSE - - README.md - - bin.mjs - - coverage - - cspell.config.yaml - - dist - - package.json - - pnpm-lock.yaml - - release-please-config.json - - scripts - - src - - static - - tsconfig.json - - vitest.config.ts - done. - ``` - - - -## `pnpm` - this template uses pnpm. - -See: https://pnpm.io/ - -## `vitest` - this template uses ViTest for testing. - -See: https://vitest.dev/ - -## ES Modules - -This tools is setup to use ES Modules. - -## GitHub Workflows - -This template includes GitHub Workflows for: - -- testing -- code coverage -- lint -- release please (for generating releases) -- CodeQL +1. Install [`pnpm`](https://pnppm.io) +1. `pnpm i` +1. `pnpm build` +1. `pnpm test` +1. `pnpm run app --help` + + + + ``` + Usage: perf runner [options] [test-methods...] + + Run performance tests. + + Arguments: + test-methods list of test methods to run (choices: "search", + "anonymous", "map", "all", default: ["all"]) + + Options: + -t, --timeout timeout for each test (default: 1000) + -V, --version output the version number + -h, --help display help for command + ``` + + + +1. `pnpm run app map` + + **Example:** + + + + ``` + Running test: map + Running: Map Anonymous: + ✔ (a) => a.length : ops: 30756.44 cnt: 30482 mean: 0.056665 p95: 0.20633 min/max: 0.020739/ 1.2881 991.08ms + ✔ filter Boolean : ops: 10380.80 cnt: 10339 mean: 0.14109 p95: 0.30647 min/max: 0.044631/ 0.65964 995.97ms + ✔ filter (a) => a : ops: 14978.50 cnt: 14922 mean: 0.10100 p95: 0.23417 min/max: 0.038621/ 0.43152 996.23ms + ✔ filter (a) => !!a : ops: 12926.77 cnt: 12883 mean: 0.11535 p95: 0.26402 min/max: 0.042264/ 0.71627 996.61ms + ✔ (a) => { return a.length; }: ops: 31771.79 cnt: 31558 mean: 0.050315 p95: 0.13926 min/max: 0.021965/ 0.38313 993.27ms + ✔ (fnLen) : ops: 29314.10 cnt: 29129 mean: 0.055057 p95: 0.15008 min/max: 0.021760/ 0.48103 993.69ms + ✔ (a) => fnLen(a) : ops: 29233.77 cnt: 29043 mean: 0.055084 p95: 0.14469 min/max: 0.021979/ 0.37546 993.47ms + ✔ (vfLen) : ops: 31732.97 cnt: 31525 mean: 0.051079 p95: 0.13862 min/max: 0.021709/ 0.36765 993.45ms + ✔ for of : ops: 21886.60 cnt: 21765 mean: 0.061837 p95: 0.18468 min/max: 0.036873/ 1.0753 994.44ms + ✔ for i : ops: 24704.07 cnt: 24556 mean: 0.051921 p95: 0.14648 min/max: 0.032250/ 0.64389 994.01ms + ✔ for i r[i]=v : ops: 19494.68 cnt: 19400 mean: 0.065557 p95: 0.17119 min/max: 0.040939/ 0.49368 995.14ms + ✔ for i Array.from(words) : ops: 33609.02 cnt: 33345 mean: 0.040022 p95: 0.12977 min/max: 0.025327/ 0.69438 992.14ms + ✔ for i Array.from : ops: 2041.26 cnt: 2040 mean: 0.50531 p95: 0.70403 min/max: 0.43681/ 1.2301 999.38ms + ✔ for i Array(size) : ops: 32333.11 cnt: 32082 mean: 0.056348 p95: 0.63695 min/max: 0.024706/ 6.5348 992.23ms + done. + ``` + + diff --git a/package.json b/package.json index 569cc0b..0f9a303 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "build": "tsc -p .", "build:readme": "pnpm build:readme:help && pnpm build:readme:example && pnpm build:readme:inject && prettier -w README.md", "build:readme:help": "./bin.mjs --help > static/help.txt", - "build:readme:example": "./bin.mjs \"*\" > static/example.txt", + "build:readme:example": "./bin.mjs map > static/example.txt", "build:readme:inject": "inject-markdown README.md", "watch": "tsc -p . --watch", "coverage": "vitest run --coverage", diff --git a/src/runner.mts b/src/runner.mts index 074d927..cdc1781 100644 --- a/src/runner.mts +++ b/src/runner.mts @@ -67,9 +67,7 @@ interface ProgressReporting { testStart?(name: string): void; testEnd?(result: TestResult): void; testIteration?(name: string, iteration: number, duration: number): void; - log?: typeof console.log; error?: typeof console.error; - stderr?: typeof process.stderr; stdout?: typeof process.stdout; spinner?: Ora; } @@ -81,13 +79,13 @@ export async function runTests( testWrapperFn: (context: RunnerContext) => void | Promise, progress?: ProgressReporting, ): Promise { - const log = progress?.log || console.log; - const stderr = progress?.stderr || process.stderr; - const spinner = progress?.spinner || ora({ stream: stderr }); + const stdout = progress?.stdout || process.stdout; + const spinner = progress?.spinner || ora({ stream: stdout }); + const log = (msg: string) => stdout.write(msg + '\n'); let nameWidth = 0; - const reportTestStart = progress?.testStart ?? ((name: string) => spinner.start(name)); + const reportTestStart = progress?.testStart ?? ((name: string) => stdout.isTTY && spinner.start(name)); const reportTestEnd = (result: TestResult) => { if (progress?.testEnd) { if (spinner.isSpinning) { @@ -96,34 +94,19 @@ export async function runTests( return progress.testEnd(result); } - const { name, duration, iterations, sd } = result; - - const min = sd.min; - const max = sd.max; - const p95 = sd.ok ? sd.p95 : NaN; - const mean = sd.ok ? sd.mean : NaN; - const ops = (iterations * 1000) / duration; + const msg = formatResult(result, nameWidth); if (spinner.isSpinning) { if (result.error) { - spinner.fail(`${name} ${duration.toFixed(2)}ms`); - log('Error: %o', result.error); + spinner.fail(msg); } else { - spinner.succeed( - `${name.padEnd(nameWidth)}: ` + - `ops: ${ops.toFixed(2).padStart(8)} ` + - `cnt: ${iterations.toFixed(0).padStart(6)} ` + - `mean: ${mean.toPrecision(5).padStart(8)} ` + - `p95: ${p95.toPrecision(5).padStart(8)} ` + - `min/max: ${min.toPrecision(5).padStart(8)}/${max.toPrecision(5).padStart(8)} ` + - `${duration.toFixed(2)}ms `, - ); + spinner.succeed(msg); } } else { if (result.error) { - log(`Test: ${name} finished in ${duration}ms with error: %o`, result.error); + log(`X ${msg}`); } else { - log(`Test: ${name} finished in ${duration}ms`); + log(`✔ ${msg}`); } } }; @@ -248,6 +231,31 @@ function toError(e: unknown): Error { return e instanceof Error ? e : new Error(String(e)); } +function formatResult(result: TestResult, nameWidth: number): string { + const { name, duration, iterations, sd } = result; + + const min = sd.min; + const max = sd.max; + const p95 = sd.ok ? sd.p95 : NaN; + const mean = sd.ok ? sd.mean : NaN; + const ops = (iterations * 1000) / duration; + + if (result.error) { + return `${name.padEnd(nameWidth)}: ${duration.toFixed(2)}ms\n` + `Error: ${result.error}`; + } + + const msg = + `${name.padEnd(nameWidth)}: ` + + `ops: ${ops.toFixed(2).padStart(8)} ` + + `cnt: ${iterations.toFixed(0).padStart(6)} ` + + `mean: ${mean.toPrecision(5).padStart(8)} ` + + `p95: ${p95.toPrecision(5).padStart(8)} ` + + `min/max: ${min.toPrecision(5).padStart(8)}/${max.toPrecision(5).padStart(8)} ` + + `${duration.toFixed(2)}ms `; + + return msg; +} + // function wait(time: number): Promise { // return new Promise((resolve) => setTimeout(resolve, time)); // } diff --git a/static/example.txt b/static/example.txt index f291263..da61166 100644 --- a/static/example.txt +++ b/static/example.txt @@ -1,16 +1,17 @@ -Find Files: - - LICENSE - - README.md - - bin.mjs - - coverage - - cspell.config.yaml - - dist - - package.json - - pnpm-lock.yaml - - release-please-config.json - - scripts - - src - - static - - tsconfig.json - - vitest.config.ts +Running test: map +Running: Map Anonymous: +✔ (a) => a.length : ops: 30756.44 cnt: 30482 mean: 0.056665 p95: 0.20633 min/max: 0.020739/ 1.2881 991.08ms +✔ filter Boolean : ops: 10380.80 cnt: 10339 mean: 0.14109 p95: 0.30647 min/max: 0.044631/ 0.65964 995.97ms +✔ filter (a) => a : ops: 14978.50 cnt: 14922 mean: 0.10100 p95: 0.23417 min/max: 0.038621/ 0.43152 996.23ms +✔ filter (a) => !!a : ops: 12926.77 cnt: 12883 mean: 0.11535 p95: 0.26402 min/max: 0.042264/ 0.71627 996.61ms +✔ (a) => { return a.length; }: ops: 31771.79 cnt: 31558 mean: 0.050315 p95: 0.13926 min/max: 0.021965/ 0.38313 993.27ms +✔ (fnLen) : ops: 29314.10 cnt: 29129 mean: 0.055057 p95: 0.15008 min/max: 0.021760/ 0.48103 993.69ms +✔ (a) => fnLen(a) : ops: 29233.77 cnt: 29043 mean: 0.055084 p95: 0.14469 min/max: 0.021979/ 0.37546 993.47ms +✔ (vfLen) : ops: 31732.97 cnt: 31525 mean: 0.051079 p95: 0.13862 min/max: 0.021709/ 0.36765 993.45ms +✔ for of : ops: 21886.60 cnt: 21765 mean: 0.061837 p95: 0.18468 min/max: 0.036873/ 1.0753 994.44ms +✔ for i : ops: 24704.07 cnt: 24556 mean: 0.051921 p95: 0.14648 min/max: 0.032250/ 0.64389 994.01ms +✔ for i r[i]=v : ops: 19494.68 cnt: 19400 mean: 0.065557 p95: 0.17119 min/max: 0.040939/ 0.49368 995.14ms +✔ for i Array.from(words) : ops: 33609.02 cnt: 33345 mean: 0.040022 p95: 0.12977 min/max: 0.025327/ 0.69438 992.14ms +✔ for i Array.from : ops: 2041.26 cnt: 2040 mean: 0.50531 p95: 0.70403 min/max: 0.43681/ 1.2301 999.38ms +✔ for i Array(size) : ops: 32333.11 cnt: 32082 mean: 0.056348 p95: 0.63695 min/max: 0.024706/ 6.5348 992.23ms done. diff --git a/static/help.txt b/static/help.txt index 6bc8a98..bf21a1e 100644 --- a/static/help.txt +++ b/static/help.txt @@ -1,14 +1,12 @@ -Usage: list-files [options] +Usage: perf runner [options] [test-methods...] -List Files +Run performance tests. Arguments: - files Files to scan for injected content. + test-methods list of test methods to run (choices: "search", + "anonymous", "map", "all", default: ["all"]) Options: - --no-must-find-files No error if files are not found. - --cwd Current Directory - --color Force color. - --no-color Do not use color. - -V, --version output the version number - -h, --help display help for command + -t, --timeout timeout for each test (default: 1000) + -V, --version output the version number + -h, --help display help for command