From ed9361ca7fc84ebbfac8d7179feabe95b9c5e241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 18 Jul 2018 12:03:06 +0200 Subject: [PATCH 01/12] List percentiles instead of stddev for Latency, fixes #138 This changes the Latency output to Avg / 90% / 99% / Max. The Req/Sec and Bytes/Sec is unchanged. ``` Running 10s test @ https://wlk.yt 10 connections Stat Avg 90% 99% Max Latency (ms) 255.41 337 715 987.17 Stat Avg Stdev Max Req/Sec 38.4 5.97 46 Bytes/Sec 6.14 MB 954 kB 7.37 MB 384 requests in 10s, 61.5 MB read ``` I'm no stats expert but from #138 my understanding is that stdev is fine for the other two metrics so I just split the tables. Maybe latency could use a little cell margin to make it easier to read the percentile figures. Lmk if the percentiles should also be used for the req and bytes/sec --- lib/progressTracker.js | 29 +++++++++++++++++++++-------- test/cli-ipc.test.js | 4 +++- test/cli.test.js | 4 +++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/progressTracker.js b/lib/progressTracker.js index f8f4a4fa..c141bdd8 100644 --- a/lib/progressTracker.js +++ b/lib/progressTracker.js @@ -75,21 +75,24 @@ function track (instance, opts) { // if the user doesn't want to render the table, we can just return early if (!opts.renderResultsTable) return - const out = table([ - asColor(chalk.cyan, ['Stat', 'Avg', 'Stdev', 'Max']), - asRow(chalk.bold('Latency (ms)'), result.latency), - asRow(chalk.bold('Req/Sec'), result.requests), - asRow(chalk.bold('Bytes/Sec'), asBytes(result.throughput)) - ], { + const tableOpts = { border: getBorderCharacters('void'), columnDefault: { paddingLeft: 0, paddingRight: 1 }, drawHorizontalLine: () => false - }) + } - logToStream(out) + logToStream(table([ + asColor(chalk.cyan, ['Stat', 'Avg', '90%', '99%', 'Max']), + asLatencyRow(chalk.bold('Latency (ms)'), result.latency) + ], tableOpts)) + logToStream(table([ + asColor(chalk.cyan, ['Stat', 'Avg', 'Stdev', 'Max']), + asRow(chalk.bold('Req/Sec'), result.requests), + asRow(chalk.bold('Bytes/Sec'), asBytes(result.throughput)) + ], tableOpts)) if (opts.renderLatencyTable) { const latency = table([ @@ -169,6 +172,16 @@ function asRow (name, stat) { ] } +function asLatencyRow (name, stat) { + return [ + name, + stat.average, + stat.p90, + stat.p99, + Math.floor(stat.max * 100) / 100 + ] +} + function asColor (colorise, row) { return row.map((entry) => colorise(entry)) } diff --git a/test/cli-ipc.test.js b/test/cli-ipc.test.js index 309984c6..42443fdf 100644 --- a/test/cli-ipc.test.js +++ b/test/cli-ipc.test.js @@ -13,8 +13,10 @@ const lines = [ /Running 1s test @ http:\/\/example.com\/foo \([^)]*\)$/, /10 connections.*$/, /$/, - /Stat.*Avg.*Stdev.*Max.*$/, + /Stat.*Avg.*90%.*99%.*Max.*$/, /Latency \(ms\).*$/, + /$/, + /Stat.*Avg.*Stdev.*Max.*$/, /Req\/Sec.*$/, /Bytes\/Sec.*$/, /$/, diff --git a/test/cli.test.js b/test/cli.test.js index 12bbdeeb..17de9457 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -10,8 +10,10 @@ const lines = [ /Running 1s test @ .*$/, /10 connections.*$/, /$/, - /Stat.*Avg.*Stdev.*Max.*$/, + /Stat.*Avg.*90%.*99%.*Max.*$/, /Latency \(ms\).*$/, + /$/, + /Stat.*Avg.*Stdev.*Max.*$/, /Req\/Sec.*$/, /Bytes\/Sec.*$/, /$/, From 274c5355562abf6a7153dfed4f95d3d3a77b7a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 18 Jul 2018 13:39:08 +0200 Subject: [PATCH 02/12] use different percentiles, and the same for all stat types --- lib/progressTracker.js | 39 +++++++++++++++++---------------------- lib/run.js | 8 ++++++-- test/cli-ipc.test.js | 4 +--- test/cli.test.js | 4 +--- 4 files changed, 25 insertions(+), 30 deletions(-) diff --git a/lib/progressTracker.js b/lib/progressTracker.js index c141bdd8..803fc7fd 100644 --- a/lib/progressTracker.js +++ b/lib/progressTracker.js @@ -75,24 +75,19 @@ function track (instance, opts) { // if the user doesn't want to render the table, we can just return early if (!opts.renderResultsTable) return - const tableOpts = { + logToStream(table([ + asColor(chalk.cyan, ['Stat', '2.5%', '50%', '97.5%', '99%', 'Avg', 'Stdev', 'Max']), + asRow(chalk.bold('Latency (ms)'), result.latency), + asRow(chalk.bold('Req/Sec'), result.requests), + asRow(chalk.bold('Bytes/Sec'), asBytes(result.throughput)) + ], { border: getBorderCharacters('void'), columnDefault: { paddingLeft: 0, paddingRight: 1 }, drawHorizontalLine: () => false - } - - logToStream(table([ - asColor(chalk.cyan, ['Stat', 'Avg', '90%', '99%', 'Max']), - asLatencyRow(chalk.bold('Latency (ms)'), result.latency) - ], tableOpts)) - logToStream(table([ - asColor(chalk.cyan, ['Stat', 'Avg', 'Stdev', 'Max']), - asRow(chalk.bold('Req/Sec'), result.requests), - asRow(chalk.bold('Bytes/Sec'), asBytes(result.throughput)) - ], tableOpts)) + })) if (opts.renderLatencyTable) { const latency = table([ @@ -166,28 +161,28 @@ function trackAmount (instance, opts, iOpts) { function asRow (name, stat) { return [ name, + stat.p25, + stat.p50, + stat.p975, + stat.p99, stat.average, stat.stddev, typeof stat.max === 'string' ? stat.max : Math.floor(stat.max * 100) / 100 ] } -function asLatencyRow (name, stat) { - return [ - name, - stat.average, - stat.p90, - stat.p99, - Math.floor(stat.max * 100) / 100 - ] -} - function asColor (colorise, row) { return row.map((entry) => colorise(entry)) } function asBytes (stat) { const result = Object.create(stat) + + // Percentiles + Object.keys(stat).filter(k => /^p\d+$/.test(k)).forEach((k) => { + result[k] = prettyBytes(stat[k]) + }) + result.average = prettyBytes(stat.average) result.stddev = prettyBytes(stat.stddev) result.max = prettyBytes(stat.max) diff --git a/lib/run.js b/lib/run.js index 52be72db..22032f93 100644 --- a/lib/run.js +++ b/lib/run.js @@ -13,6 +13,10 @@ const reInterval = require('reinterval') const histAsObj = histUtil.histAsObj const addPercentiles = histUtil.addPercentiles +// TODO upstream to hdr-histogram-percentiles-obj? +histUtil.percentiles.push(2.5, 97.5) +histUtil.percentiles.sort((a, b) => a - b) + function run (opts, cb) { const cbPassedIn = (typeof cb === 'function') @@ -130,9 +134,9 @@ function run (opts, cb) { title: opts.title, url: opts.url, socketPath: opts.socketPath, - requests: histAsObj(requests, totalCompletedRequests), + requests: addPercentiles(requests, histAsObj(requests, totalCompletedRequests)), latency: addPercentiles(latencies, histAsObj(latencies)), - throughput: histAsObj(throughput, totalBytes), + throughput: addPercentiles(throughput, histAsObj(throughput, totalBytes)), errors: errors, timeouts: timeouts, duration: Math.round((Date.now() - startTime) / 1000), diff --git a/test/cli-ipc.test.js b/test/cli-ipc.test.js index 42443fdf..2395a7d8 100644 --- a/test/cli-ipc.test.js +++ b/test/cli-ipc.test.js @@ -13,10 +13,8 @@ const lines = [ /Running 1s test @ http:\/\/example.com\/foo \([^)]*\)$/, /10 connections.*$/, /$/, - /Stat.*Avg.*90%.*99%.*Max.*$/, + /Stat.*2\.5%.*50%.*97\.5%.*99%.*Avg.*Stdev.*Max.*$/, /Latency \(ms\).*$/, - /$/, - /Stat.*Avg.*Stdev.*Max.*$/, /Req\/Sec.*$/, /Bytes\/Sec.*$/, /$/, diff --git a/test/cli.test.js b/test/cli.test.js index 17de9457..531b54a1 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -10,10 +10,8 @@ const lines = [ /Running 1s test @ .*$/, /10 connections.*$/, /$/, - /Stat.*Avg.*90%.*99%.*Max.*$/, + /Stat.*2\.5%.*50%.*97\.5%.*99%.*Avg.*Stdev.*Max.*$/, /Latency \(ms\).*$/, - /$/, - /Stat.*Avg.*Stdev.*Max.*$/, /Req\/Sec.*$/, /Bytes\/Sec.*$/, /$/, From 37e0d7fc19bbdec9a2059c4b67eabb08a02f540e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 18 Jul 2018 14:58:36 +0200 Subject: [PATCH 03/12] Add API tests for percentile properties Just the ones used by the CLI here. Need to do `t.type()` instead of `ok()` because a lot of them are too fast to be not-0 :) --- test/run.test.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/run.test.js b/test/run.test.js index f25afadc..3f803ad1 100644 --- a/test/run.test.js +++ b/test/run.test.js @@ -28,6 +28,10 @@ test('run', (t) => { t.ok(result.latency.stddev, 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') t.ok(result.latency.max, 'latency.max exists') + t.type(result.latency.p25, 'number', 'latency.p25 (2.5%) exists') + t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') + t.type(result.latency.p975, 'number', 'latency.p975 (97.5%) exists') + t.type(result.latency.p99, 'number', 'latency.p99 (99%) exists') t.ok(result.requests, 'requests exists') t.ok(result.requests.average, 'requests.average exists') @@ -37,6 +41,10 @@ test('run', (t) => { t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') t.ok(result.requests.sent, 'sent exists') t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') + t.type(result.requests.p25, 'number', 'requests.p25 (2.5%) exists') + t.type(result.requests.p50, 'number', 'requests.p50 (50%) exists') + t.type(result.requests.p975, 'number', 'requests.p975 (97.5%) exists') + t.type(result.requests.p99, 'number', 'requests.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') t.ok(result.throughput.average, 'throughput.average exists') @@ -44,6 +52,10 @@ test('run', (t) => { t.ok(result.throughput.min, 'throughput.min exists') t.ok(result.throughput.max, 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') + t.type(result.throughput.p25, 'number', 'throughput.p25 (2.5%) exists') + t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') + t.type(result.throughput.p975, 'number', 'throughput.p975 (97.5%) exists') + t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.ok(result.start, 'start time exists') t.ok(result.finish, 'finish time exists') @@ -79,6 +91,10 @@ test('tracker.stop()', (t) => { t.ok(result.latency.stddev, 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') t.ok(result.latency.max, 'latency.max exists') + t.type(result.latency.p25, 'number', 'latency.p25 (2.5%) exists') + t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') + t.type(result.latency.p975, 'number', 'latency.p975 (97.5%) exists') + t.type(result.latency.p99, 'number', 'latency.p99 (99%) exists') t.ok(result.requests, 'requests exists') t.ok(result.requests.average, 'requests.average exists') @@ -88,6 +104,10 @@ test('tracker.stop()', (t) => { t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') t.ok(result.requests.sent, 'sent exists') t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') + t.type(result.requests.p25, 'number', 'requests.p25 (2.5%) exists') + t.type(result.requests.p50, 'number', 'requests.p50 (50%) exists') + t.type(result.requests.p975, 'number', 'requests.p975 (97.5%) exists') + t.type(result.requests.p99, 'number', 'requests.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') t.ok(result.throughput.average, 'throughput.average exists') @@ -95,6 +115,10 @@ test('tracker.stop()', (t) => { t.ok(result.throughput.min, 'throughput.min exists') t.ok(result.throughput.max, 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') + t.type(result.throughput.p25, 'number', 'throughput.p25 (2.5%) exists') + t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') + t.type(result.throughput.p975, 'number', 'throughput.p975 (97.5%) exists') + t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.ok(result.start, 'start time exists') t.ok(result.finish, 'finish time exists') @@ -283,6 +307,10 @@ for (let i = 1; i <= 5; i++) { t.ok(result.latency.stddev, 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') t.ok(result.latency.max, 'latency.max exists') + t.type(result.latency.p25, 'number', 'latency.p25 (2.5%) exists') + t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') + t.type(result.latency.p975, 'number', 'latency.p975 (97.5%) exists') + t.type(result.latency.p99, 'number', 'latency.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') t.ok(!Number.isNaN(result.throughput.average), 'throughput.average is not NaN') @@ -291,6 +319,10 @@ for (let i = 1; i <= 5; i++) { t.ok(result.throughput.min, 'throughput.min exists') t.ok(result.throughput.max, 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') + t.type(result.throughput.p25, 'number', 'throughput.p25 (2.5%) exists') + t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') + t.type(result.throughput.p975, 'number', 'throughput.p975 (97.5%) exists') + t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.end() }) @@ -333,6 +365,10 @@ test('run will exclude non 2xx stats from latency and throughput averages if exc t.equal(result.latency.stddev, 0, 'latency.stddev should be 0') t.equal(result.latency.min, 0, 'latency.min should be 0') t.equal(result.latency.max, 0, 'latency.max should be 0') + t.equal(result.latency.p25, 0, 'latency.p25 (2.5%) should be 0') + t.equal(result.latency.p50, 0, 'latency.p50 (50%) should be 0') + t.equal(result.latency.p975, 0, 'latency.p975 (97.5%) should be 0') + t.equal(result.latency.p99, 0, 'latency.p99 (99%) should be 0') t.ok(result.throughput, 'throughput exists') t.equal(result.throughput.average, 0, 'throughput.average should be 0') @@ -340,6 +376,10 @@ test('run will exclude non 2xx stats from latency and throughput averages if exc t.equal(result.throughput.min, 0, 'throughput.min should be 0') t.equal(result.throughput.max, 0, 'throughput.max should be 0') t.equal(result.throughput.total, 0, 'throughput.total should be 0') + t.equal(result.throughput.p25, 0, 'throughput.p25 (2.5%) should be 0') + t.equal(result.throughput.p50, 0, 'throughput.p50 (50%) should be 0') + t.equal(result.throughput.p975, 0, 'throughput.p975 (97.5%) should be 0') + t.equal(result.throughput.p99, 0, 'throughput.p99 (99%) should be 0') t.end() }) From 3381fc40efea2c7f60bc3087bbf985b8e98f6ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Wed, 18 Jul 2018 16:12:59 +0200 Subject: [PATCH 04/12] Update percentile property names [skip ci] Needs a version bump for hdr-histogram-percentiles-obj before it'll work :) --- lib/progressTracker.js | 4 ++-- lib/run.js | 4 ---- test/run.test.js | 40 ++++++++++++++++++++-------------------- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/lib/progressTracker.js b/lib/progressTracker.js index 803fc7fd..b47b634e 100644 --- a/lib/progressTracker.js +++ b/lib/progressTracker.js @@ -161,9 +161,9 @@ function trackAmount (instance, opts, iOpts) { function asRow (name, stat) { return [ name, - stat.p25, + stat.p2_5, stat.p50, - stat.p975, + stat.p97_5, stat.p99, stat.average, stat.stddev, diff --git a/lib/run.js b/lib/run.js index 22032f93..458141b5 100644 --- a/lib/run.js +++ b/lib/run.js @@ -13,10 +13,6 @@ const reInterval = require('reinterval') const histAsObj = histUtil.histAsObj const addPercentiles = histUtil.addPercentiles -// TODO upstream to hdr-histogram-percentiles-obj? -histUtil.percentiles.push(2.5, 97.5) -histUtil.percentiles.sort((a, b) => a - b) - function run (opts, cb) { const cbPassedIn = (typeof cb === 'function') diff --git a/test/run.test.js b/test/run.test.js index 3f803ad1..e50e9cf1 100644 --- a/test/run.test.js +++ b/test/run.test.js @@ -28,9 +28,9 @@ test('run', (t) => { t.ok(result.latency.stddev, 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') t.ok(result.latency.max, 'latency.max exists') - t.type(result.latency.p25, 'number', 'latency.p25 (2.5%) exists') + t.type(result.latency.p2_5, 'number', 'latency.p2_5 (2.5%) exists') t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') - t.type(result.latency.p975, 'number', 'latency.p975 (97.5%) exists') + t.type(result.latency.p97_5, 'number', 'latency.p97_5 (97.5%) exists') t.type(result.latency.p99, 'number', 'latency.p99 (99%) exists') t.ok(result.requests, 'requests exists') @@ -41,9 +41,9 @@ test('run', (t) => { t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') t.ok(result.requests.sent, 'sent exists') t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') - t.type(result.requests.p25, 'number', 'requests.p25 (2.5%) exists') + t.type(result.requests.p2_5, 'number', 'requests.p2_5 (2.5%) exists') t.type(result.requests.p50, 'number', 'requests.p50 (50%) exists') - t.type(result.requests.p975, 'number', 'requests.p975 (97.5%) exists') + t.type(result.requests.p97_5, 'number', 'requests.p97_5 (97.5%) exists') t.type(result.requests.p99, 'number', 'requests.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') @@ -52,9 +52,9 @@ test('run', (t) => { t.ok(result.throughput.min, 'throughput.min exists') t.ok(result.throughput.max, 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') - t.type(result.throughput.p25, 'number', 'throughput.p25 (2.5%) exists') + t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') - t.type(result.throughput.p975, 'number', 'throughput.p975 (97.5%) exists') + t.type(result.throughput.p97_5, 'number', 'throughput.p97_5 (97.5%) exists') t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.ok(result.start, 'start time exists') @@ -91,9 +91,9 @@ test('tracker.stop()', (t) => { t.ok(result.latency.stddev, 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') t.ok(result.latency.max, 'latency.max exists') - t.type(result.latency.p25, 'number', 'latency.p25 (2.5%) exists') + t.type(result.latency.p2_5, 'number', 'latency.p2_5 (2.5%) exists') t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') - t.type(result.latency.p975, 'number', 'latency.p975 (97.5%) exists') + t.type(result.latency.p97_5, 'number', 'latency.p97_5 (97.5%) exists') t.type(result.latency.p99, 'number', 'latency.p99 (99%) exists') t.ok(result.requests, 'requests exists') @@ -104,9 +104,9 @@ test('tracker.stop()', (t) => { t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') t.ok(result.requests.sent, 'sent exists') t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') - t.type(result.requests.p25, 'number', 'requests.p25 (2.5%) exists') + t.type(result.requests.p2_5, 'number', 'requests.p2_5 (2.5%) exists') t.type(result.requests.p50, 'number', 'requests.p50 (50%) exists') - t.type(result.requests.p975, 'number', 'requests.p975 (97.5%) exists') + t.type(result.requests.p97_5, 'number', 'requests.p97_5 (97.5%) exists') t.type(result.requests.p99, 'number', 'requests.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') @@ -115,9 +115,9 @@ test('tracker.stop()', (t) => { t.ok(result.throughput.min, 'throughput.min exists') t.ok(result.throughput.max, 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') - t.type(result.throughput.p25, 'number', 'throughput.p25 (2.5%) exists') + t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') - t.type(result.throughput.p975, 'number', 'throughput.p975 (97.5%) exists') + t.type(result.throughput.p97_5, 'number', 'throughput.p97_5 (97.5%) exists') t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.ok(result.start, 'start time exists') @@ -307,9 +307,9 @@ for (let i = 1; i <= 5; i++) { t.ok(result.latency.stddev, 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') t.ok(result.latency.max, 'latency.max exists') - t.type(result.latency.p25, 'number', 'latency.p25 (2.5%) exists') + t.type(result.latency.p2_5, 'number', 'latency.p2_5 (2.5%) exists') t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') - t.type(result.latency.p975, 'number', 'latency.p975 (97.5%) exists') + t.type(result.latency.p97_5, 'number', 'latency.p97_5 (97.5%) exists') t.type(result.latency.p99, 'number', 'latency.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') @@ -319,9 +319,9 @@ for (let i = 1; i <= 5; i++) { t.ok(result.throughput.min, 'throughput.min exists') t.ok(result.throughput.max, 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') - t.type(result.throughput.p25, 'number', 'throughput.p25 (2.5%) exists') + t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') - t.type(result.throughput.p975, 'number', 'throughput.p975 (97.5%) exists') + t.type(result.throughput.p97_5, 'number', 'throughput.p97_5 (97.5%) exists') t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.end() @@ -365,9 +365,9 @@ test('run will exclude non 2xx stats from latency and throughput averages if exc t.equal(result.latency.stddev, 0, 'latency.stddev should be 0') t.equal(result.latency.min, 0, 'latency.min should be 0') t.equal(result.latency.max, 0, 'latency.max should be 0') - t.equal(result.latency.p25, 0, 'latency.p25 (2.5%) should be 0') + t.equal(result.latency.p2_5, 0, 'latency.p2_5 (2.5%) should be 0') t.equal(result.latency.p50, 0, 'latency.p50 (50%) should be 0') - t.equal(result.latency.p975, 0, 'latency.p975 (97.5%) should be 0') + t.equal(result.latency.p97_5, 0, 'latency.p97_5 (97.5%) should be 0') t.equal(result.latency.p99, 0, 'latency.p99 (99%) should be 0') t.ok(result.throughput, 'throughput exists') @@ -376,9 +376,9 @@ test('run will exclude non 2xx stats from latency and throughput averages if exc t.equal(result.throughput.min, 0, 'throughput.min should be 0') t.equal(result.throughput.max, 0, 'throughput.max should be 0') t.equal(result.throughput.total, 0, 'throughput.total should be 0') - t.equal(result.throughput.p25, 0, 'throughput.p25 (2.5%) should be 0') + t.equal(result.throughput.p2_5, 0, 'throughput.p2_5 (2.5%) should be 0') t.equal(result.throughput.p50, 0, 'throughput.p50 (50%) should be 0') - t.equal(result.throughput.p975, 0, 'throughput.p975 (97.5%) should be 0') + t.equal(result.throughput.p97_5, 0, 'throughput.p97_5 (97.5%) should be 0') t.equal(result.throughput.p99, 0, 'throughput.p99 (99%) should be 0') t.end() From 3da1ffd01938ceea9b64a3fb49a2b576c9b3ae63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 20 Jul 2018 10:52:57 +0200 Subject: [PATCH 05/12] Fix latency table for new hdr-histogram-percentiles-obj. --- lib/progressTracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/progressTracker.js b/lib/progressTracker.js index b47b634e..147849b1 100644 --- a/lib/progressTracker.js +++ b/lib/progressTracker.js @@ -93,7 +93,7 @@ function track (instance, opts) { const latency = table([ asColor(chalk.cyan, ['Percentile', 'Latency (ms)']) ].concat(percentiles.map((perc) => { - const key = ('p' + perc).replace('.', '') + const key = ('p' + perc).replace('.', '_') return [ chalk.bold('' + perc), result.latency[key] From 93445220be0484280103b65cbb1dad47fe59bde4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 20 Jul 2018 10:57:15 +0200 Subject: [PATCH 06/12] Fix formatting of Bytes/Sec --- lib/progressTracker.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/progressTracker.js b/lib/progressTracker.js index 147849b1..17d50a71 100644 --- a/lib/progressTracker.js +++ b/lib/progressTracker.js @@ -93,7 +93,7 @@ function track (instance, opts) { const latency = table([ asColor(chalk.cyan, ['Percentile', 'Latency (ms)']) ].concat(percentiles.map((perc) => { - const key = ('p' + perc).replace('.', '_') + const key = `p${perc}`.replace('.', '_') return [ chalk.bold('' + perc), result.latency[key] @@ -178,9 +178,9 @@ function asColor (colorise, row) { function asBytes (stat) { const result = Object.create(stat) - // Percentiles - Object.keys(stat).filter(k => /^p\d+$/.test(k)).forEach((k) => { - result[k] = prettyBytes(stat[k]) + percentiles.forEach((p) => { + const key = `p${p}`.replace('.', '_') + result[key] = prettyBytes(stat[key]) }) result.average = prettyBytes(stat.average) From 7bafd6d4180690fc37b7d682a1461a14d51ddd72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 20 Jul 2018 12:44:01 +0200 Subject: [PATCH 07/12] Bump hdr-histogram-percentiles-obj. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d387fe6..0f88ebbb 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "chalk": "^2.4.1", "color-support": "^1.1.1", "hdr-histogram-js": "^1.1.4", - "hdr-histogram-percentiles-obj": "^1.2.0", + "hdr-histogram-percentiles-obj": "^2.0.0", "http-parser-js": "^0.4.13", "hyperid": "^1.4.1", "minimist": "^1.2.0", From eb2dbc4168098c98fbb7bb8043459bd7274435ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 20 Jul 2018 12:46:58 +0200 Subject: [PATCH 08/12] Use t.type() over t.ok() to assert numberic props exist --- test/run.test.js | 56 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/test/run.test.js b/test/run.test.js index e50e9cf1..84694d6c 100644 --- a/test/run.test.js +++ b/test/run.test.js @@ -24,22 +24,22 @@ test('run', (t) => { t.equal(result.pipelining, 1, 'pipelining is the default') t.ok(result.latency, 'latency exists') - t.ok(result.latency.average, 'latency.average exists') - t.ok(result.latency.stddev, 'latency.stddev exists') + t.type(result.latency.average, 'number', 'latency.average exists') + t.type(result.latency.stddev, 'number', 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') - t.ok(result.latency.max, 'latency.max exists') + t.type(result.latency.max, 'number', 'latency.max exists') t.type(result.latency.p2_5, 'number', 'latency.p2_5 (2.5%) exists') t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') t.type(result.latency.p97_5, 'number', 'latency.p97_5 (97.5%) exists') t.type(result.latency.p99, 'number', 'latency.p99 (99%) exists') t.ok(result.requests, 'requests exists') - t.ok(result.requests.average, 'requests.average exists') - t.ok(result.requests.stddev, 'requests.stddev exists') - t.ok(result.requests.min, 'requests.min exists') - t.ok(result.requests.max, 'requests.max exists') + t.type(result.requests.average, 'number', 'requests.average exists') + t.type(result.requests.stddev, 'number', 'requests.stddev exists') + t.type(result.requests.min, 'number', 'requests.min exists') + t.type(result.requests.max, 'number', 'requests.max exists') t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') - t.ok(result.requests.sent, 'sent exists') + t.type(result.requests.sent, 'number', 'sent exists') t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') t.type(result.requests.p2_5, 'number', 'requests.p2_5 (2.5%) exists') t.type(result.requests.p50, 'number', 'requests.p50 (50%) exists') @@ -47,10 +47,10 @@ test('run', (t) => { t.type(result.requests.p99, 'number', 'requests.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') - t.ok(result.throughput.average, 'throughput.average exists') + t.type(result.throughput.average, 'number', 'throughput.average exists') t.type(result.throughput.stddev, 'number', 'throughput.stddev exists') - t.ok(result.throughput.min, 'throughput.min exists') - t.ok(result.throughput.max, 'throughput.max exists') + t.type(result.throughput.min, 'number', 'throughput.min exists') + t.type(result.throughput.max, 'number', 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') @@ -87,22 +87,22 @@ test('tracker.stop()', (t) => { t.equal(result.pipelining, 1, 'pipelining is the default') t.ok(result.latency, 'latency exists') - t.ok(result.latency.average, 'latency.average exists') - t.ok(result.latency.stddev, 'latency.stddev exists') + t.type(result.latency.average, 'number', 'latency.average exists') + t.type(result.latency.stddev, 'number', 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') - t.ok(result.latency.max, 'latency.max exists') + t.type(result.latency.max, 'number', 'latency.max exists') t.type(result.latency.p2_5, 'number', 'latency.p2_5 (2.5%) exists') t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') t.type(result.latency.p97_5, 'number', 'latency.p97_5 (97.5%) exists') t.type(result.latency.p99, 'number', 'latency.p99 (99%) exists') t.ok(result.requests, 'requests exists') - t.ok(result.requests.average, 'requests.average exists') - t.ok(result.requests.stddev, 'requests.stddev exists') - t.ok(result.requests.min, 'requests.min exists') - t.ok(result.requests.max, 'requests.max exists') + t.type(result.requests.average, 'number', 'requests.average exists') + t.type(result.requests.stddev, 'number', 'requests.stddev exists') + t.type(result.requests.min, 'number', 'requests.min exists') + t.type(result.requests.max, 'number', 'requests.max exists') t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') - t.ok(result.requests.sent, 'sent exists') + t.type(result.requests.sent, 'number', 'sent exists') t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') t.type(result.requests.p2_5, 'number', 'requests.p2_5 (2.5%) exists') t.type(result.requests.p50, 'number', 'requests.p50 (50%) exists') @@ -110,10 +110,10 @@ test('tracker.stop()', (t) => { t.type(result.requests.p99, 'number', 'requests.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') - t.ok(result.throughput.average, 'throughput.average exists') + t.type(result.throughput.average, 'number', 'throughput.average exists') t.type(result.throughput.stddev, 'number', 'throughput.stddev exists') - t.ok(result.throughput.min, 'throughput.min exists') - t.ok(result.throughput.max, 'throughput.max exists') + t.type(result.throughput.min, 'number', 'throughput.min exists') + t.type(result.throughput.max, 'number', 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') @@ -303,10 +303,10 @@ for (let i = 1; i <= 5; i++) { t.ok(result.latency, 'latency exists') t.ok(!Number.isNaN(result.latency.average), 'latency.average is not NaN') - t.ok(result.latency.average, 'latency.average exists') - t.ok(result.latency.stddev, 'latency.stddev exists') + t.type(result.latency.average, 'number', 'latency.average exists') + t.type(result.latency.stddev, 'number', 'latency.stddev exists') t.ok(result.latency.min >= 0, 'latency.min exists') - t.ok(result.latency.max, 'latency.max exists') + t.type(result.latency.max, 'number', 'latency.max exists') t.type(result.latency.p2_5, 'number', 'latency.p2_5 (2.5%) exists') t.type(result.latency.p50, 'number', 'latency.p50 (50%) exists') t.type(result.latency.p97_5, 'number', 'latency.p97_5 (97.5%) exists') @@ -314,10 +314,10 @@ for (let i = 1; i <= 5; i++) { t.ok(result.throughput, 'throughput exists') t.ok(!Number.isNaN(result.throughput.average), 'throughput.average is not NaN') - t.ok(result.throughput.average, 'throughput.average exists') + t.type(result.throughput.average, 'number', 'throughput.average exists') t.type(result.throughput.stddev, 'number', 'throughput.stddev exists') - t.ok(result.throughput.min, 'throughput.min exists') - t.ok(result.throughput.max, 'throughput.max exists') + t.type(result.throughput.min, 'number', 'throughput.min exists') + t.type(result.throughput.max, 'number', 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') From 56377f2f39184112b6b5890ae33640f1e0722589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 20 Jul 2018 13:07:36 +0200 Subject: [PATCH 09/12] Document the results object. --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 85e7f0e8..724fb410 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,33 @@ Because an autocannon instance is an `EventEmitter`, it emits several events. th * `reqError`: Emitted in the case of a request error e.g. a timeout. * `error`: Emitted if there is an error during the setup phase of autocannon. +### results + +The results object emitted by `done` and passed to the `autocannon()` callback has these properties: + +* `title`: Value of the `title` option passed to `autocannon()`. +* `url`: The URL that was targeted. +* `socketPath`: The UNIX Domain Socket or Windows Named Pipe that was targeted, or `undefined`. +* `requests`: A histogram object containing statistics about the amount of requests that were sent per second. +* `latency`: A histogram object containing statistics about response latency. +* `throughput`: A histogram object containing statistics about the response data throughput per second. +* `duration`: The amount of time the test took, **in seconds**. +* `errors`: The number of connection errors (including timeouts) that occurred. +* `timeouts`: The number of connection timeouts that occurred. +* `start`: A Date object representing when the test started. +* `finish`: A Date object representing when the test ended. +* `connections`: The amount of connections used (value of `opts.connections`). +* `pipelining`: The number of pipelined requests used per connection (value of `opts.pipelining`). +* `non2xx`: The number of non-2xx response status codes received. + +The histogram objects for `requests`, `latency` and `throughput` are [hdr-histogram-percentiles-obj](https://github.com/thekemkid/hdr-histogram-percentiles-obj) objects and have this shape: + +* `min`: The lowest value for this statistic. +* `max`: The highest value for this statistic. +* `average`: The average (mean) value. +* `stddev`: The standard deviation. +* `p*`: The XXth percentile value for this statistic. The percentile properties are: `p2_5`, `p50`, `p75`, `p90`, `p97_5`, `p99`, `p99_9`, `p99_99`, `p99_999`. + ### `Client` API This object is passed as the first parameter of both the `setupClient` function and the `response` event from an autocannon instance. You can use this to modify the requests you are sending while benchmarking. This is also an `EventEmitter`, with the events and their params listed below. From 50e2fbbf0492a353c79e841057d0c0bc9fa1193c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 20 Jul 2018 16:11:45 +0200 Subject: [PATCH 10/12] Try a different table layout ``` 10 connections Stat 2.5% 50% 97.5% 99% Avg Stdev Max Latency 17 ms 29 ms 204 ms 334 ms 45.32 ms 64.1 ms 954.08 ms Stat 1% 2.5% 50% 97.5% Avg Stdev Min Req/Sec 91 91 274 288 217.67 89.75 91 Bytes/Sec 688 kB 688 kB 2.03 MB 2.1 MB 1.58 MB 642 kB 657 kB Req/Bytes counts sampled once per second. 653 requests in 3s, 4.71 MB read ``` --- lib/progressTracker.js | 47 +++++++++++++++++++++++++++++++++++------- test/cli-ipc.test.js | 6 +++++- test/cli.test.js | 6 +++++- test/envPort.test.js | 8 +++++-- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/lib/progressTracker.js b/lib/progressTracker.js index 17d50a71..2868e2fd 100644 --- a/lib/progressTracker.js +++ b/lib/progressTracker.js @@ -75,19 +75,25 @@ function track (instance, opts) { // if the user doesn't want to render the table, we can just return early if (!opts.renderResultsTable) return - logToStream(table([ - asColor(chalk.cyan, ['Stat', '2.5%', '50%', '97.5%', '99%', 'Avg', 'Stdev', 'Max']), - asRow(chalk.bold('Latency (ms)'), result.latency), - asRow(chalk.bold('Req/Sec'), result.requests), - asRow(chalk.bold('Bytes/Sec'), asBytes(result.throughput)) - ], { + const tableOpts = { border: getBorderCharacters('void'), columnDefault: { paddingLeft: 0, paddingRight: 1 }, drawHorizontalLine: () => false - })) + } + + logToStream(table([ + asColor(chalk.cyan, ['Stat', '2.5%', '50%', '97.5%', '99%', 'Avg', 'Stdev', 'Max']), + asLowRow(chalk.bold('Latency'), asMs(result.latency)) + ], tableOpts)) + logToStream(table([ + asColor(chalk.cyan, ['Stat', '1%', '2.5%', '50%', '97.5%', 'Avg', 'Stdev', 'Min']), + asHighRow(chalk.bold('Req/Sec'), result.requests), + asHighRow(chalk.bold('Bytes/Sec'), asBytes(result.throughput)) + ], tableOpts)) + logToStream('Req/Bytes counts sampled once per second.\n') if (opts.renderLatencyTable) { const latency = table([ @@ -158,7 +164,8 @@ function trackAmount (instance, opts, iOpts) { return progressBar } -function asRow (name, stat) { +// create a table row for stats where low values is better +function asLowRow (name, stat) { return [ name, stat.p2_5, @@ -171,10 +178,34 @@ function asRow (name, stat) { ] } +// create a table row for stats where high values is better +function asHighRow (name, stat) { + return [ + name, + stat.p1, + stat.p2_5, + stat.p50, + stat.p97_5, + stat.average, + stat.stddev, + typeof stat.min === 'string' ? stat.min : Math.floor(stat.min * 100) / 100 + ] +} + function asColor (colorise, row) { return row.map((entry) => colorise(entry)) } +function asMs (stat) { + const result = Object.create(null) + Object.keys(stat).forEach((k) => { + result[k] = `${stat[k]} ms` + }) + result.max = typeof stat.max === 'string' ? stat.max : `${Math.floor(stat.max * 100) / 100} ms` + + return result +} + function asBytes (stat) { const result = Object.create(stat) diff --git a/test/cli-ipc.test.js b/test/cli-ipc.test.js index 2395a7d8..ef1835a2 100644 --- a/test/cli-ipc.test.js +++ b/test/cli-ipc.test.js @@ -14,10 +14,14 @@ const lines = [ /10 connections.*$/, /$/, /Stat.*2\.5%.*50%.*97\.5%.*99%.*Avg.*Stdev.*Max.*$/, - /Latency \(ms\).*$/, + /Latency.*$/, + /$/, + /Stat.*1%.*2\.5%.*50%.*97\.5%.*Avg.*Stdev.*Min.*$/, /Req\/Sec.*$/, /Bytes\/Sec.*$/, /$/, + /Req\/Bytes counts sampled once per second.*$/, + /$/, /.* requests in \d+s, .* read/ ] diff --git a/test/cli.test.js b/test/cli.test.js index 531b54a1..8fbf6f1f 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -11,10 +11,14 @@ const lines = [ /10 connections.*$/, /$/, /Stat.*2\.5%.*50%.*97\.5%.*99%.*Avg.*Stdev.*Max.*$/, - /Latency \(ms\).*$/, + /Latency.*$/, + /$/, + /Stat.*1%.*2\.5%.*50%.*97\.5%.*Avg.*Stdev.*Min.*$/, /Req\/Sec.*$/, /Bytes\/Sec.*$/, /$/, + /Req\/Bytes counts sampled once per second.*$/, + /$/, /.* requests in \d+s, .* read/ ] diff --git a/test/envPort.test.js b/test/envPort.test.js index f7713ae1..409b09b7 100644 --- a/test/envPort.test.js +++ b/test/envPort.test.js @@ -10,11 +10,15 @@ const lines = [ /Running 1s test @ .*$/, /10 connections.*$/, /$/, - /Stat.*Avg.*Stdev.*Max.*$/, - /Latency \(ms\).*$/, + /Stat.*2\.5%.*50%.*97\.5%.*99%.*Avg.*Stdev.*Max.*$/, + /Latency.*$/, + /$/, + /Stat.*1%.*2\.5%.*50%.*97\.5%.*Avg.*Stdev.*Min.*$/, /Req\/Sec.*$/, /Bytes\/Sec.*$/, /$/, + /Req\/Bytes counts sampled once per second.*$/, + /$/, /.* requests in \d+s, .* read/ ] From 13fdfc36f68939b0d594c42a5bcab586d41a0ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 20 Jul 2018 16:35:26 +0200 Subject: [PATCH 11/12] Test existence of relevant percentiles --- test/run.test.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/run.test.js b/test/run.test.js index 84694d6c..63032d12 100644 --- a/test/run.test.js +++ b/test/run.test.js @@ -41,10 +41,10 @@ test('run', (t) => { t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') t.type(result.requests.sent, 'number', 'sent exists') t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') + t.type(result.requests.p1, 'number', 'requests.p1 (1%) exists') t.type(result.requests.p2_5, 'number', 'requests.p2_5 (2.5%) exists') t.type(result.requests.p50, 'number', 'requests.p50 (50%) exists') t.type(result.requests.p97_5, 'number', 'requests.p97_5 (97.5%) exists') - t.type(result.requests.p99, 'number', 'requests.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') t.type(result.throughput.average, 'number', 'throughput.average exists') @@ -52,10 +52,10 @@ test('run', (t) => { t.type(result.throughput.min, 'number', 'throughput.min exists') t.type(result.throughput.max, 'number', 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') + t.type(result.throughput.p1, 'number', 'throughput.p1 (1%) exists') t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') t.type(result.throughput.p97_5, 'number', 'throughput.p97_5 (97.5%) exists') - t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.ok(result.start, 'start time exists') t.ok(result.finish, 'finish time exists') @@ -104,10 +104,10 @@ test('tracker.stop()', (t) => { t.ok(result.requests.total >= result.requests.average * 2 / 100 * 95, 'requests.total exists') t.type(result.requests.sent, 'number', 'sent exists') t.ok(result.requests.sent >= result.requests.total, 'total requests made should be more than or equal to completed requests total') + t.type(result.requests.p1, 'number', 'requests.p1 (1%) exists') t.type(result.requests.p2_5, 'number', 'requests.p2_5 (2.5%) exists') t.type(result.requests.p50, 'number', 'requests.p50 (50%) exists') t.type(result.requests.p97_5, 'number', 'requests.p97_5 (97.5%) exists') - t.type(result.requests.p99, 'number', 'requests.p99 (99%) exists') t.ok(result.throughput, 'throughput exists') t.type(result.throughput.average, 'number', 'throughput.average exists') @@ -115,10 +115,10 @@ test('tracker.stop()', (t) => { t.type(result.throughput.min, 'number', 'throughput.min exists') t.type(result.throughput.max, 'number', 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') + t.type(result.throughput.p1, 'number', 'throughput.p1 (1%) exists') t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') t.type(result.throughput.p97_5, 'number', 'throughput.p97_5 (97.5%) exists') - t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.ok(result.start, 'start time exists') t.ok(result.finish, 'finish time exists') @@ -319,10 +319,10 @@ for (let i = 1; i <= 5; i++) { t.type(result.throughput.min, 'number', 'throughput.min exists') t.type(result.throughput.max, 'number', 'throughput.max exists') t.ok(result.throughput.total >= result.throughput.average * 2 / 100 * 95, 'throughput.total exists') + t.type(result.throughput.p1, 'number', 'throughput.p1 (1%) exists') t.type(result.throughput.p2_5, 'number', 'throughput.p2_5 (2.5%) exists') t.type(result.throughput.p50, 'number', 'throughput.p50 (50%) exists') t.type(result.throughput.p97_5, 'number', 'throughput.p97_5 (97.5%) exists') - t.type(result.throughput.p99, 'number', 'throughput.p99 (99%) exists') t.end() }) @@ -365,6 +365,7 @@ test('run will exclude non 2xx stats from latency and throughput averages if exc t.equal(result.latency.stddev, 0, 'latency.stddev should be 0') t.equal(result.latency.min, 0, 'latency.min should be 0') t.equal(result.latency.max, 0, 'latency.max should be 0') + t.equal(result.latency.p1, 0, 'latency.p1 (1%) should be 0') t.equal(result.latency.p2_5, 0, 'latency.p2_5 (2.5%) should be 0') t.equal(result.latency.p50, 0, 'latency.p50 (50%) should be 0') t.equal(result.latency.p97_5, 0, 'latency.p97_5 (97.5%) should be 0') @@ -376,10 +377,10 @@ test('run will exclude non 2xx stats from latency and throughput averages if exc t.equal(result.throughput.min, 0, 'throughput.min should be 0') t.equal(result.throughput.max, 0, 'throughput.max should be 0') t.equal(result.throughput.total, 0, 'throughput.total should be 0') + t.equal(result.throughput.p1, 0, 'throughput.p1 (1%) should be 0') t.equal(result.throughput.p2_5, 0, 'throughput.p2_5 (2.5%) should be 0') t.equal(result.throughput.p50, 0, 'throughput.p50 (50%) should be 0') t.equal(result.throughput.p97_5, 0, 'throughput.p97_5 (97.5%) should be 0') - t.equal(result.throughput.p99, 0, 'throughput.p99 (99%) should be 0') t.end() }) From c5977709c339741f52090ea1b53f1663117f10cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Fri, 10 Aug 2018 12:52:32 +0200 Subject: [PATCH 12/12] Increase tap test timeout, should fix appveyor flakiness --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0f88ebbb..4c90b09c 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "autocannon": "autocannon.js" }, "scripts": { - "test": "standard && tap test/*.test.js" + "test": "standard && tap --timeout 45 test/*.test.js" }, "pre-commit": [ "test"