diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0696c1c..e380146 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,7 +23,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
- node-version: [12.13.0, 12.x, 14.x, 16.x, 17.x]
+ node-version: [12.x, 14.x, 16.x, 17.x]
runs-on: ${{matrix.os}}
steps:
diff --git a/README.md b/README.md
index 9a6b622..c455e9d 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,7 @@ To avoid that behavior you can either quote ("--", '--') or escape (`--`) the do
## Supported Node.js versions
Clinic.js relies heavily on Node.js core instrumentation available in later versions.
-Currently the supported Node.js versions are `>= 12.13.0`.
+Currently the supported Node.js versions are `>= 12.22.7`.
## Examples and Demos
@@ -123,6 +123,7 @@ clinic heapprofiler --help
--on-port Run a script when the server starts listening on a port.
--autocannon Run the autocannon benchmarking tool when the server starts listening on a port.
--dest Destination for the collect data (default .).
+--stop-delay Add a delay to close the process when a job is done through either `autocannon` or `on-port` flag (milliseconds)
```
## Programmable Interfaces
diff --git a/bin.js b/bin.js
index 6fa02c8..0f9973f 100755
--- a/bin.js
+++ b/bin.js
@@ -76,7 +76,8 @@ const result = commist()
'visualize-only',
'sample-interval',
'on-port',
- 'dest'
+ 'dest',
+ 'stop-delay'
],
default: {
'sample-interval': '10',
@@ -117,7 +118,8 @@ const result = commist()
],
string: [
'visualize-only',
- 'dest'
+ 'dest',
+ 'stop-delay'
],
default: {
open: true,
@@ -158,7 +160,8 @@ const result = commist()
],
string: [
'visualize-only',
- 'dest'
+ 'dest',
+ 'stop-delay'
],
default: {
open: true,
@@ -198,7 +201,8 @@ const result = commist()
],
string: [
'visualize-only',
- 'dest'
+ 'dest',
+ 'stop-delay'
],
default: {
open: true,
@@ -322,6 +326,8 @@ async function runTool (toolName, Tool, version, args, uiOptions) {
kernelTracing: args['kernel-tracing']
})
+ const stopDelayMs = parseInt(args['stop-delay'])
+
const spinner = ora({
text: 'Analysing data',
color: uiOptions.color,
@@ -337,7 +343,16 @@ async function runTool (toolName, Tool, version, args, uiOptions) {
tool.on('port', function (port, proc, cb) {
process.env.PORT = port
// inline the PORT env to make it easier for cross platform usage
- execspawn(envString(onPort, { PORT: port }), { stdio: 'inherit' }).on('exit', cb)
+ execspawn(envString(onPort, { PORT: port }), { stdio: 'inherit' })
+ .on('exit', () => {
+ if (stopDelayMs) {
+ tool.emit('status', 'Waiting to close the process')
+ if (spinner.isEnabled && !spinner.isSpinning) spinner.start()
+ setTimeout(() => cb(), stopDelayMs)
+ } else {
+ cb()
+ }
+ })
})
tool.on('analysing', function (message = 'Analysing data') {
diff --git a/docs/clinic-bubbleprof.txt b/docs/clinic-bubbleprof.txt
index 270fe77..7ccac01 100644
--- a/docs/clinic-bubbleprof.txt
+++ b/docs/clinic-bubbleprof.txt
@@ -41,3 +41,4 @@
--autocannon Run the autocannon benchmarking tool when the server starts listening on a port.
--open Boolean to enable or disable your report opening in your web browser.
--dest Destination for the collected data (default .clinic/
).
+ --stop-delay Add a delay to close the process when a job is done through either `autocannon` or `on-port` flag (milliseconds)
diff --git a/docs/clinic-doctor.txt b/docs/clinic-doctor.txt
index be4f6e9..e7b33de 100644
--- a/docs/clinic-doctor.txt
+++ b/docs/clinic-doctor.txt
@@ -44,3 +44,4 @@
--autocannon Run the autocannon benchmarking tool when the server starts listening on a port.
--open Boolean to enable or disable your report opening in your web browser.
--dest Destination for the collected data (default .clinic/
).
+ --stop-delay Add a delay to close the process when a job is done through either `autocannon` or `on-port` flag (milliseconds)
diff --git a/docs/clinic-flame.txt b/docs/clinic-flame.txt
index 3f02987..1b7aab2 100644
--- a/docs/clinic-flame.txt
+++ b/docs/clinic-flame.txt
@@ -50,3 +50,4 @@
--open Boolean to enable or disable your report opening in your web browser.
--dest Destination for the collected data (default .clinic/
).
--kernel-tracing Profile application using linux_perf (linux only).
+ --stop-delay Add a delay to close the process when a job is done through either `autocannon` or `on-port` flag (milliseconds)
diff --git a/docs/clinic-heap-profiler.txt b/docs/clinic-heap-profiler.txt
index fd8a870..4d7e5a4 100644
--- a/docs/clinic-heap-profiler.txt
+++ b/docs/clinic-heap-profiler.txt
@@ -43,3 +43,4 @@
--autocannon Run the autocannon benchmarking tool when the server starts listening on a port.
--open Boolean to enable or disable your report opening in your web browser.
--dest Destination for the collected data (default .clinic/
).
+ --stop-delay Add a delay to close the process when a job is done through either `autocannon` or `on-port` flag (milliseconds)
diff --git a/package.json b/package.json
index 1af72b2..0d4be43 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"repository": "clinicjs/node-clinic",
"version": "11.0.0",
"engines": {
- "node": ">= 12.13.0"
+ "node": ">= 12.22.7"
},
"bin": {
"clinic": "bin.js"
diff --git a/test/cli-bubbleprof-stop-delay.test.js b/test/cli-bubbleprof-stop-delay.test.js
new file mode 100644
index 0000000..0e7a5fa
--- /dev/null
+++ b/test/cli-bubbleprof-stop-delay.test.js
@@ -0,0 +1,40 @@
+'use strict'
+
+const fs = require('fs')
+const url = require('url')
+const async = require('async')
+const path = require('path')
+const test = require('tap').test
+const cli = require('./cli.js')
+
+test('clinic bubbleprof --stop-delay --on-port - no issues', function (t) {
+ cli({}, [
+ 'clinic', 'bubbleprof', '--no-open', '--stop-delay', '500', '--on-port', 'node -e "setTimeout(() => {}, 0)"',
+ '--', 'node', '-e', `
+ const http = require('http')
+
+ http.createServer((req, res) => res.end('ok')).listen(0)
+ `
+ ], function (err, stdout, stderr, tempdir) {
+ t.error(err)
+
+ const dirname = stdout.match(/(\.clinic[/\\]\d+.clinic-bubbleprof)/)[1]
+ const fullpath = url.pathToFileURL(fs.realpathSync(path.resolve(tempdir, dirname)))
+ t.equal(stdout.split('\n')[0], 'Waiting to close the process')
+ t.equal(stdout.split('\n')[1], 'Analysing data')
+ t.equal(stdout.split('\n')[2], `Generated HTML file is ${fullpath}.html`)
+
+ // check that files exists
+ async.parallel({
+ sourceData (done) {
+ fs.access(path.resolve(tempdir, dirname), done)
+ },
+ htmlFile (done) {
+ fs.access(path.resolve(tempdir, dirname + '.html'), done)
+ }
+ }, function (err) {
+ t.error(err)
+ t.end()
+ })
+ })
+})
diff --git a/test/cli-doctor-stop-delay.test.js b/test/cli-doctor-stop-delay.test.js
new file mode 100644
index 0000000..2978791
--- /dev/null
+++ b/test/cli-doctor-stop-delay.test.js
@@ -0,0 +1,59 @@
+'use strict'
+
+const fs = require('fs')
+const url = require('url')
+const async = require('async')
+const path = require('path')
+const test = require('tap').test
+const cli = require('./cli.js')
+
+test('clinic doctor --stop-delay --on-port - no issues', function (t) {
+ cli({}, [
+ 'clinic', 'doctor', '--no-open', '--stop-delay', '500', '--on-port', 'node -e "setTimeout(() => {}, 0)"',
+ '--', 'node', '-e', `
+ const http = require('http')
+
+ http.createServer((req, res) => res.end('ok')).listen(0)
+ `
+ ], function (err, stdout, stderr, tempdir) {
+ t.error(err)
+
+ const dirname = stdout.match(/(\.clinic[/\\]\d+.clinic-doctor)/)[1]
+ const fullpath = url.pathToFileURL(fs.realpathSync(path.resolve(tempdir, dirname)))
+ t.equal(stdout.split('\n')[0], 'Waiting to close the process')
+ t.equal(stdout.split('\n')[1], 'Analysing data')
+ t.equal(stdout.split('\n')[2], `Generated HTML file is ${fullpath}.html`)
+
+ // check that files exists
+ async.parallel({
+ sourceData (done) {
+ fs.access(path.resolve(tempdir, dirname), done)
+ },
+ htmlFile (done) {
+ fs.access(path.resolve(tempdir, dirname + '.html'), done)
+ }
+ }, function (err) {
+ t.error(err)
+ t.end()
+ })
+ })
+})
+
+test('clinic doctor --stop-delay --on-port - exceeding timeout', function (t) {
+ const onPortDuration = 500
+ setTimeout(() => {
+ t.pass('timeout should be called before t.fail')
+ t.end()
+ process.exit(0)
+ }, onPortDuration + 500)
+ cli({}, [
+ 'clinic', 'doctor', '--no-open', '--stop-delay', '2000', '--on-port', `node -e "setTimeout(() => {}, ${onPortDuration})"`,
+ '--', 'node', '-e', `
+ const http = require('http')
+
+ http.createServer((req, res) => res.end('ok')).listen(0)
+ `
+ ], function () {
+ t.fail('it should not be called once timeout is reached')
+ })
+})
diff --git a/test/cli-flame-stop-delay.test.js b/test/cli-flame-stop-delay.test.js
new file mode 100644
index 0000000..7910941
--- /dev/null
+++ b/test/cli-flame-stop-delay.test.js
@@ -0,0 +1,40 @@
+'use strict'
+
+const fs = require('fs')
+const url = require('url')
+const async = require('async')
+const path = require('path')
+const test = require('tap').test
+const cli = require('./cli.js')
+
+test('clinic flame --stop-delay --on-port - no issues', function (t) {
+ cli({}, [
+ 'clinic', 'flame', '--no-open', '--stop-delay', '500', '--on-port', 'node -e "setTimeout(() => {}, 0)"',
+ '--', 'node', '-e', `
+ const http = require('http')
+
+ http.createServer((req, res) => res.end('ok')).listen(0)
+ `
+ ], function (err, stdout, stderr, tempdir) {
+ t.error(err)
+
+ const dirname = stdout.match(/(\.clinic[/\\]\d+.clinic-flame)/)[1]
+ const fullpath = url.pathToFileURL(fs.realpathSync(path.resolve(tempdir, dirname)))
+ t.equal(stdout.split('\n')[0], 'Waiting to close the process')
+ t.equal(stdout.split('\n')[1], 'Analysing data')
+ t.equal(stdout.split('\n')[2], `Generated HTML file is ${fullpath}.html`)
+
+ // check that files exists
+ async.parallel({
+ sourceData (done) {
+ fs.access(path.resolve(tempdir, dirname), done)
+ },
+ htmlFile (done) {
+ fs.access(path.resolve(tempdir, dirname + '.html'), done)
+ }
+ }, function (err) {
+ t.error(err)
+ t.end()
+ })
+ })
+})
diff --git a/test/cli-heapprofiler-stop-delay.test.js b/test/cli-heapprofiler-stop-delay.test.js
new file mode 100644
index 0000000..66115cd
--- /dev/null
+++ b/test/cli-heapprofiler-stop-delay.test.js
@@ -0,0 +1,40 @@
+'use strict'
+
+const fs = require('fs')
+const url = require('url')
+const async = require('async')
+const path = require('path')
+const test = require('tap').test
+const cli = require('./cli.js')
+
+test('clinic heapprofiler --stop-delay --on-port - no issues', function (t) {
+ cli({}, [
+ 'clinic', 'heapprofiler', '--no-open', '--stop-delay', '500', '--on-port', 'node -e "setTimeout(() => {}, 0)"',
+ '--', 'node', '-e', `
+ const http = require('http')
+
+ http.createServer((req, res) => res.end('ok')).listen(0)
+ `
+ ], function (err, stdout, stderr, tempdir) {
+ t.error(err)
+
+ const dirname = stdout.match(/(\.clinic[/\\]\d+.clinic-heapprofile)/)[1]
+ const fullpath = url.pathToFileURL(fs.realpathSync(path.resolve(tempdir, dirname)))
+ t.equal(stdout.split('\n')[0], 'Waiting to close the process')
+ t.equal(stdout.split('\n')[1], 'Analysing data')
+ t.equal(stdout.split('\n')[2], `Generated HTML file is ${fullpath}.html`)
+
+ // check that files exists
+ async.parallel({
+ sourceData (done) {
+ fs.access(path.resolve(tempdir, dirname), done)
+ },
+ htmlFile (done) {
+ fs.access(path.resolve(tempdir, dirname + '.html'), done)
+ }
+ }, function (err) {
+ t.error(err)
+ t.end()
+ })
+ })
+})