[Pkg-javascript-commits] [node-signal-exit] 01/07: Import Upstream version 3.0.1
Sruthi Chandran
srud-guest at moszumanska.debian.org
Tue Oct 18 16:20:49 UTC 2016
This is an automated email from the git hooks/post-receive script.
srud-guest pushed a commit to branch master
in repository node-signal-exit.
commit b4a65be8b26bb5fee973cd3b0bed3520f28d09a9
Author: Sruthi <srud at disroot.org>
Date: Tue Oct 18 20:03:56 2016 +0530
Import Upstream version 3.0.1
---
.gitignore | 5 +
.travis.yml | 6 +
CHANGELOG.md | 27 ++
LICENSE.txt | 14 +
README.md | 40 ++
appveyor.yml | 19 +
index.js | 148 +++++++
package.json | 38 ++
signals.js | 53 +++
test/all-integration-test.js | 97 +++++
test/fixtures/awaiter.js | 35 ++
test/fixtures/change-code-expect.json | 800 ++++++++++++++++++++++++++++++++++
test/fixtures/change-code.js | 99 +++++
test/fixtures/end-of-execution.js | 5 +
test/fixtures/exit-last.js | 14 +
test/fixtures/exit.js | 7 +
test/fixtures/exiter.js | 44 ++
test/fixtures/load-unload.js | 7 +
test/fixtures/multiple-load.js | 52 +++
test/fixtures/parent.js | 51 +++
test/fixtures/sigint.js | 11 +
test/fixtures/sigkill.js | 19 +
test/fixtures/signal-default.js | 99 +++++
test/fixtures/signal-last.js | 17 +
test/fixtures/signal-listener.js | 23 +
test/fixtures/sigpipe.js | 8 +
test/fixtures/sigterm.js | 9 +
test/fixtures/unwrap.js | 37 ++
test/multi-exit.js | 61 +++
test/signal-exit-test.js | 116 +++++
30 files changed, 1961 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fad8f86
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+node_modules
+.DS_Store
+nyc_output
+coverage
+.nyc_output
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..089861a
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+sudo: false
+language: node_js
+node_js:
+ - '0.12'
+ - '4'
+ - '5'
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e2f70d2
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,27 @@
+# Change Log
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+<a name="3.0.1"></a>
+## [3.0.1](https://github.com/tapjs/signal-exit/compare/v3.0.0...v3.0.1) (2016-09-08)
+
+
+### Bug Fixes
+
+* do not listen on SIGBUS, SIGFPE, SIGSEGV and SIGILL ([#40](https://github.com/tapjs/signal-exit/issues/40)) ([5b105fb](https://github.com/tapjs/signal-exit/commit/5b105fb))
+
+
+
+<a name="3.0.0"></a>
+# [3.0.0](https://github.com/tapjs/signal-exit/compare/v2.1.2...v3.0.0) (2016-06-13)
+
+
+### Bug Fixes
+
+* get our test suite running on Windows ([#23](https://github.com/tapjs/signal-exit/issues/23)) ([6f3eda8](https://github.com/tapjs/signal-exit/commit/6f3eda8))
+* hooking SIGPROF was interfering with profilers see [#21](https://github.com/tapjs/signal-exit/issues/21) ([#24](https://github.com/tapjs/signal-exit/issues/24)) ([1248a4c](https://github.com/tapjs/signal-exit/commit/1248a4c))
+
+
+### BREAKING CHANGES
+
+* signal-exit no longer wires into SIGPROF
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..c7e2747
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,14 @@
+Copyright (c) 2015, Contributors
+
+Permission to use, copy, modify, and/or distribute this software
+for any purpose with or without fee is hereby granted, provided
+that the above copyright notice and this permission notice
+appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
+LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8ebccab
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+# signal-exit
+
+[![Build Status](https://travis-ci.org/tapjs/signal-exit.png)](https://travis-ci.org/tapjs/signal-exit)
+[![Coverage](https://coveralls.io/repos/tapjs/signal-exit/badge.svg?branch=master)](https://coveralls.io/r/tapjs/signal-exit?branch=master)
+[![NPM version](https://img.shields.io/npm/v/signal-exit.svg)](https://www.npmjs.com/package/signal-exit)
+[![Windows Tests](https://img.shields.io/appveyor/ci/bcoe/signal-exit/master.svg?label=Windows%20Tests)](https://ci.appveyor.com/project/bcoe/signal-exit)
+[![Standard Version](https://img.shields.io/badge/release-standard%20version-brightgreen.svg)](https://github.com/conventional-changelog/standard-version)
+
+When you want to fire an event no matter how a process exits:
+
+* reaching the end of execution.
+* explicitly having `process.exit(code)` called.
+* having `process.kill(pid, sig)` called.
+* receiving a fatal signal from outside the process
+
+Use `signal-exit`.
+
+```js
+var onExit = require('signal-exit')
+
+onExit(function (code, signal) {
+ console.log('process exited!')
+})
+```
+
+## API
+
+`var remove = onExit(function (code, signal) {}, options)`
+
+The return value of the function is a function that will remove the
+handler.
+
+Note that the function *only* fires for signals if the signal would
+cause the proces to exit. That is, there are no other listeners, and
+it is a fatal signal.
+
+## Options
+
+* `alwaysLast`: Run this handler after any other signal or exit
+ handlers. This causes `process.emit` to be monkeypatched.
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..b29e6c6
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,19 @@
+environment:
+ matrix:
+ - nodejs_version: '5'
+ - nodejs_version: '4'
+ - nodejs_version: '0.12'
+install:
+ - ps: Install-Product node $env:nodejs_version
+ - set CI=true
+ - npm -g install npm at latest
+ - set PATH=%APPDATA%\npm;%PATH%
+ - npm install
+matrix:
+ fast_finish: true
+build: off
+version: '{build}'
+shallow_clone: true
+clone_depth: 1
+test_script:
+ - npm test
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..7dd8d91
--- /dev/null
+++ b/index.js
@@ -0,0 +1,148 @@
+// Note: since nyc uses this module to output coverage, any lines
+// that are in the direct sync flow of nyc's outputCoverage are
+// ignored, since we can never get coverage for them.
+var assert = require('assert')
+var signals = require('./signals.js')
+
+var EE = require('events')
+/* istanbul ignore if */
+if (typeof EE !== 'function') {
+ EE = EE.EventEmitter
+}
+
+var emitter
+if (process.__signal_exit_emitter__) {
+ emitter = process.__signal_exit_emitter__
+} else {
+ emitter = process.__signal_exit_emitter__ = new EE()
+ emitter.count = 0
+ emitter.emitted = {}
+}
+
+module.exports = function (cb, opts) {
+ assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler')
+
+ if (loaded === false) {
+ load()
+ }
+
+ var ev = 'exit'
+ if (opts && opts.alwaysLast) {
+ ev = 'afterexit'
+ }
+
+ var remove = function () {
+ emitter.removeListener(ev, cb)
+ if (emitter.listeners('exit').length === 0 &&
+ emitter.listeners('afterexit').length === 0) {
+ unload()
+ }
+ }
+ emitter.on(ev, cb)
+
+ return remove
+}
+
+module.exports.unload = unload
+function unload () {
+ if (!loaded) {
+ return
+ }
+ loaded = false
+
+ signals.forEach(function (sig) {
+ try {
+ process.removeListener(sig, sigListeners[sig])
+ } catch (er) {}
+ })
+ process.emit = originalProcessEmit
+ process.reallyExit = originalProcessReallyExit
+ emitter.count -= 1
+}
+
+function emit (event, code, signal) {
+ if (emitter.emitted[event]) {
+ return
+ }
+ emitter.emitted[event] = true
+ emitter.emit(event, code, signal)
+}
+
+// { <signal>: <listener fn>, ... }
+var sigListeners = {}
+signals.forEach(function (sig) {
+ sigListeners[sig] = function listener () {
+ // If there are no other listeners, an exit is coming!
+ // Simplest way: remove us and then re-send the signal.
+ // We know that this will kill the process, so we can
+ // safely emit now.
+ var listeners = process.listeners(sig)
+ if (listeners.length === emitter.count) {
+ unload()
+ emit('exit', null, sig)
+ /* istanbul ignore next */
+ emit('afterexit', null, sig)
+ /* istanbul ignore next */
+ process.kill(process.pid, sig)
+ }
+ }
+})
+
+module.exports.signals = function () {
+ return signals
+}
+
+module.exports.load = load
+
+var loaded = false
+
+function load () {
+ if (loaded) {
+ return
+ }
+ loaded = true
+
+ // This is the number of onSignalExit's that are in play.
+ // It's important so that we can count the correct number of
+ // listeners on signals, and don't wait for the other one to
+ // handle it instead of us.
+ emitter.count += 1
+
+ signals = signals.filter(function (sig) {
+ try {
+ process.on(sig, sigListeners[sig])
+ return true
+ } catch (er) {
+ return false
+ }
+ })
+
+ process.emit = processEmit
+ process.reallyExit = processReallyExit
+}
+
+var originalProcessReallyExit = process.reallyExit
+function processReallyExit (code) {
+ process.exitCode = code || 0
+ emit('exit', process.exitCode, null)
+ /* istanbul ignore next */
+ emit('afterexit', process.exitCode, null)
+ /* istanbul ignore next */
+ originalProcessReallyExit.call(process, process.exitCode)
+}
+
+var originalProcessEmit = process.emit
+function processEmit (ev, arg) {
+ if (ev === 'exit') {
+ if (arg !== undefined) {
+ process.exitCode = arg
+ }
+ var ret = originalProcessEmit.apply(this, arguments)
+ emit('exit', process.exitCode, null)
+ /* istanbul ignore next */
+ emit('afterexit', process.exitCode, null)
+ return ret
+ } else {
+ return originalProcessEmit.apply(this, arguments)
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..5cd4455
--- /dev/null
+++ b/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "signal-exit",
+ "version": "3.0.1",
+ "description": "when you want to fire an event no matter how a process exits.",
+ "main": "index.js",
+ "scripts": {
+ "pretest": "standard",
+ "test": "tap --timeout=240 ./test/*.js --cov",
+ "coverage": "nyc report --reporter=text-lcov | coveralls",
+ "release": "standard-version"
+ },
+ "files": [
+ "index.js",
+ "signals.js"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tapjs/signal-exit.git"
+ },
+ "keywords": [
+ "signal",
+ "exit"
+ ],
+ "author": "Ben Coe <ben at npmjs.com>",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/tapjs/signal-exit/issues"
+ },
+ "homepage": "https://github.com/tapjs/signal-exit",
+ "devDependencies": {
+ "chai": "^3.5.0",
+ "coveralls": "^2.11.10",
+ "nyc": "^7.0.0",
+ "standard": "^7.1.2",
+ "standard-version": "^2.3.0",
+ "tap": "^6.2.0"
+ }
+}
diff --git a/signals.js b/signals.js
new file mode 100644
index 0000000..3bd67a8
--- /dev/null
+++ b/signals.js
@@ -0,0 +1,53 @@
+// This is not the set of all possible signals.
+//
+// It IS, however, the set of all signals that trigger
+// an exit on either Linux or BSD systems. Linux is a
+// superset of the signal names supported on BSD, and
+// the unknown signals just fail to register, so we can
+// catch that easily enough.
+//
+// Don't bother with SIGKILL. It's uncatchable, which
+// means that we can't fire any callbacks anyway.
+//
+// If a user does happen to register a handler on a non-
+// fatal signal like SIGWINCH or something, and then
+// exit, it'll end up firing `process.emit('exit')`, so
+// the handler will be fired anyway.
+//
+// SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
+// artificially, inherently leave the process in a
+// state from which it is not safe to try and enter JS
+// listeners.
+module.exports = [
+ 'SIGABRT',
+ 'SIGALRM',
+ 'SIGHUP',
+ 'SIGINT',
+ 'SIGTERM'
+]
+
+if (process.platform !== 'win32') {
+ module.exports.push(
+ 'SIGVTALRM',
+ 'SIGXCPU',
+ 'SIGXFSZ',
+ 'SIGUSR2',
+ 'SIGTRAP',
+ 'SIGSYS',
+ 'SIGQUIT',
+ 'SIGIOT'
+ // should detect profiler and enable/disable accordingly.
+ // see #21
+ // 'SIGPROF'
+ )
+}
+
+if (process.platform === 'linux') {
+ module.exports.push(
+ 'SIGIO',
+ 'SIGPOLL',
+ 'SIGPWR',
+ 'SIGSTKFLT',
+ 'SIGUNUSED'
+ )
+}
diff --git a/test/all-integration-test.js b/test/all-integration-test.js
new file mode 100644
index 0000000..704a4d1
--- /dev/null
+++ b/test/all-integration-test.js
@@ -0,0 +1,97 @@
+/* global describe, it */
+
+var exec = require('child_process').exec
+var assert = require('assert')
+var isWindows = process.platform === 'win32'
+var shell = isWindows ? null : { shell: '/bin/bash' }
+var node = isWindows ? '"' + process.execPath + '"' : process.execPath
+
+require('chai').should()
+require('tap').mochaGlobals()
+
+var onSignalExit = require('../')
+
+describe('all-signals-integration-test', function () {
+ // These are signals that are aliases for other signals, so
+ // the result will sometimes be one of the others. For these,
+ // we just verify that we GOT a signal, not what it is.
+ function weirdSignal (sig) {
+ return sig === 'SIGIOT' ||
+ sig === 'SIGIO' ||
+ sig === 'SIGSYS' ||
+ sig === 'SIGIOT' ||
+ sig === 'SIGABRT' ||
+ sig === 'SIGPOLL' ||
+ sig === 'SIGUNUSED'
+ }
+
+ // Exhaustively test every signal, and a few numbers.
+ // signal-exit does not currently support process.kill()
+ // on win32.
+ var signals = isWindows ? [] : onSignalExit.signals()
+ signals.concat('', 0, 1, 2, 3, 54).forEach(function (sig) {
+ var js = require.resolve('./fixtures/exiter.js')
+ it('exits properly: ' + sig, function (done) {
+ // issues with SIGUSR1 on Node 0.10.x
+ if (process.version.match(/^v0\.10\./) && sig === 'SIGUSR1') return done()
+
+ var cmd = node + ' ' + js + ' ' + sig
+ exec(cmd, shell, function (err, stdout, stderr) {
+ if (sig) {
+ if (!isWindows) assert(err)
+ if (!isNaN(sig)) {
+ if (!isWindows) assert.equal(err.code, sig)
+ } else if (!weirdSignal(sig)) {
+ if (!isWindows) err.signal.should.equal(sig)
+ } else if (sig) {
+ if (!isWindows) assert(err.signal)
+ }
+ } else {
+ assert.ifError(err)
+ }
+
+ try {
+ var data = JSON.parse(stdout)
+ } catch (er) {
+ console.error('invalid json: %j', stdout, stderr)
+ throw er
+ }
+
+ if (weirdSignal(sig)) {
+ data.wanted[1] = true
+ data.found[1] = !!data.found[1]
+ }
+ assert.deepEqual(data.found, data.wanted)
+ done()
+ })
+ })
+ })
+
+ signals.forEach(function (sig) {
+ var js = require.resolve('./fixtures/parent.js')
+ it('exits properly: (external sig) ' + sig, function (done) {
+ // issues with SIGUSR1 on Node 0.10.x
+ if (process.version.match(/^v0\.10\./) && sig === 'SIGUSR1') return done()
+
+ var cmd = node + ' ' + js + ' ' + sig
+ exec(cmd, shell, function (err, stdout, stderr) {
+ assert.ifError(err)
+ try {
+ var data = JSON.parse(stdout)
+ } catch (er) {
+ console.error('invalid json: %j', stdout, stderr)
+ throw er
+ }
+
+ if (weirdSignal(sig)) {
+ data.wanted[1] = true
+ data.found[1] = !!data.found[1]
+ data.external[1] = !!data.external[1]
+ }
+ assert.deepEqual(data.found, data.wanted)
+ assert.deepEqual(data.external, data.wanted)
+ done()
+ })
+ })
+ })
+})
diff --git a/test/fixtures/awaiter.js b/test/fixtures/awaiter.js
new file mode 100644
index 0000000..5bc3f68
--- /dev/null
+++ b/test/fixtures/awaiter.js
@@ -0,0 +1,35 @@
+var expectSignal = process.argv[2]
+
+if (!expectSignal || !isNaN(expectSignal)) {
+ throw new Error('signal not provided')
+}
+
+var onSignalExit = require('../../')
+
+onSignalExit(function (code, signal) {
+ // some signals don't always get recognized properly, because
+ // they have the same numeric code.
+ if (wanted[1] === true) {
+ signal = !!signal
+ }
+ console.log('%j', {
+ found: [ code, signal ],
+ wanted: wanted
+ })
+})
+
+var wanted
+switch (expectSignal) {
+ case 'SIGIOT':
+ case 'SIGUNUSED':
+ case 'SIGPOLL':
+ wanted = [ null, true ]
+ break
+ default:
+ wanted = [ null, expectSignal ]
+ break
+}
+
+console.error('want', wanted)
+
+setTimeout(function () {}, 1000)
diff --git a/test/fixtures/change-code-expect.json b/test/fixtures/change-code-expect.json
new file mode 100644
index 0000000..7eeeb4c
--- /dev/null
+++ b/test/fixtures/change-code-expect.json
@@ -0,0 +1,800 @@
+{
+ "explicit 0 nochange sigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "explicit 0 nochange nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "explicit 0 change sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "explicit 0 change nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "explicit 0 code sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "explicit 0 code nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "explicit 0 twice sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "explicit 0 twice nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "explicit 0 twicecode sigexit": {
+ "code": 6,
+ "signal": null,
+ "exitCode": 6,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "explicit 0 twicecode nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "explicit 2 nochange sigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 2,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "second code=2"
+ ]
+ },
+ "explicit 2 nochange nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 2,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "second code=2"
+ ]
+ },
+ "explicit 2 change sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5"
+ ]
+ },
+ "explicit 2 change nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5"
+ ]
+ },
+ "explicit 2 code sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5",
+ "second code=2"
+ ]
+ },
+ "explicit 2 code nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5",
+ "second code=2"
+ ]
+ },
+ "explicit 2 twice sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5"
+ ]
+ },
+ "explicit 2 twice nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5"
+ ]
+ },
+ "explicit 2 twicecode sigexit": {
+ "code": 6,
+ "signal": null,
+ "exitCode": 6,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "explicit 2 twicecode nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "explicit null nochange sigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "explicit null nochange nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "explicit null change sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "explicit null change nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "explicit null code sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "explicit null code nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "explicit null twice sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "explicit null twice nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "explicit null twicecode sigexit": {
+ "code": 6,
+ "signal": null,
+ "exitCode": 6,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "explicit null twicecode nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "code 0 nochange sigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "code 0 nochange nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "code 0 change sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "code 0 change nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "code 0 code sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "code 0 code nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "code 0 twice sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "code 0 twice nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "code 0 twicecode sigexit": {
+ "code": 6,
+ "signal": null,
+ "exitCode": 6,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "code 0 twicecode nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "code 2 nochange sigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 2,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "second code=2"
+ ]
+ },
+ "code 2 nochange nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 2,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "second code=2"
+ ]
+ },
+ "code 2 change sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5"
+ ]
+ },
+ "code 2 change nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5"
+ ]
+ },
+ "code 2 code sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5",
+ "second code=2"
+ ]
+ },
+ "code 2 code nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5",
+ "second code=2"
+ ]
+ },
+ "code 2 twice sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5"
+ ]
+ },
+ "code 2 twice nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5"
+ ]
+ },
+ "code 2 twicecode sigexit": {
+ "code": 6,
+ "signal": null,
+ "exitCode": 6,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "code 2 twicecode nosigexit": {
+ "code": 2,
+ "signal": null,
+ "exitCode": 2,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=2",
+ "set code from 2 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "code null nochange sigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "code null nochange nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "code null change sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "code null change nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "code null code sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "code null code nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "code null twice sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "code null twice nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "code null twicecode sigexit": {
+ "code": 6,
+ "signal": null,
+ "exitCode": 6,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "code null twicecode nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "normal 0 nochange sigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "normal 0 nochange nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 0,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "second code=0"
+ ]
+ },
+ "normal 0 change sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "normal 0 change nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "normal 0 code sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "normal 0 code nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "second code=0"
+ ]
+ },
+ "normal 0 twice sigexit": {
+ "code": 5,
+ "signal": null,
+ "exitCode": 5,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "normal 0 twice nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 5,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5"
+ ]
+ },
+ "normal 0 twicecode sigexit": {
+ "code": 6,
+ "signal": null,
+ "exitCode": 6,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ },
+ "normal 0 twicecode nosigexit": {
+ "code": 0,
+ "signal": null,
+ "exitCode": 0,
+ "actualCode": 6,
+ "actualSignal": null,
+ "stderr": [
+ "first code=0",
+ "set code from 0 to 5",
+ "set code from 5 to 6"
+ ]
+ }
+}
diff --git a/test/fixtures/change-code.js b/test/fixtures/change-code.js
new file mode 100644
index 0000000..0a4388e
--- /dev/null
+++ b/test/fixtures/change-code.js
@@ -0,0 +1,99 @@
+var join = require('path').join
+
+if (process.argv.length === 2) {
+ var types = [ 'explicit', 'code', 'normal' ]
+ var codes = [ 0, 2, 'null' ]
+ var changes = [ 'nochange', 'change', 'code', 'twice', 'twicecode' ]
+ var handlers = [ 'sigexit', 'nosigexit' ]
+ var opts = []
+ types.forEach(function (type) {
+ var testCodes = type === 'normal' ? [ 0 ] : codes
+ testCodes.forEach(function (code) {
+ changes.forEach(function (change) {
+ handlers.forEach(function (handler) {
+ opts.push([type, code, change, handler].join(' '))
+ })
+ })
+ })
+ })
+
+ var results = {}
+
+ var exec = require('child_process').exec
+ run(opts.shift())
+} else {
+ var type = process.argv[2]
+ var code = +process.argv[3]
+ var change = process.argv[4]
+ var sigexit = process.argv[5] !== 'nosigexit'
+
+ if (sigexit) {
+ var onSignalExit = require('../../')
+ onSignalExit(listener)
+ } else {
+ process.on('exit', listener)
+ }
+
+ process.on('exit', function (code) {
+ console.error('first code=%j', code)
+ })
+
+ if (change !== 'nochange') {
+ process.once('exit', function (code) {
+ console.error('set code from %j to %j', code, 5)
+ if (change === 'code' || change === 'twicecode') {
+ process.exitCode = 5
+ } else {
+ process.exit(5)
+ }
+ })
+ if (change === 'twicecode' || change === 'twice') {
+ process.once('exit', function (code) {
+ code = process.exitCode || code
+ console.error('set code from %j to %j', code, code + 1)
+ process.exit(code + 1)
+ })
+ }
+ }
+
+ process.on('exit', function (code) {
+ console.error('second code=%j', code)
+ })
+
+ if (type === 'explicit') {
+ if (code || code === 0) {
+ process.exit(code)
+ } else {
+ process.exit()
+ }
+ } else if (type === 'code') {
+ process.exitCode = +code || 0
+ }
+}
+
+function listener (code, signal) {
+ signal = signal || null
+ console.log('%j', { code: code, signal: signal, exitCode: process.exitCode || 0 })
+}
+
+function run (opt) {
+ console.error(opt)
+ var shell = process.platform === 'win32' ? null : { shell: '/bin/bash' }
+ exec(join(process.execPath, ' ', __filename, ' ' + opt), shell, function (err, stdout, stderr) {
+ var res = JSON.parse(stdout)
+ if (err) {
+ res.actualCode = err.code
+ res.actualSignal = err.signal
+ } else {
+ res.actualCode = 0
+ res.actualSignal = null
+ }
+ res.stderr = stderr.trim().split('\n')
+ results[opt] = res
+ if (opts.length) {
+ run(opts.shift())
+ } else {
+ console.log(JSON.stringify(results, null, 2))
+ }
+ })
+}
diff --git a/test/fixtures/end-of-execution.js b/test/fixtures/end-of-execution.js
new file mode 100644
index 0000000..8b8f245
--- /dev/null
+++ b/test/fixtures/end-of-execution.js
@@ -0,0 +1,5 @@
+var onSignalExit = require('../../')
+
+onSignalExit(function (code, signal) {
+ console.log('reached end of execution, ' + code + ', ' + signal)
+})
diff --git a/test/fixtures/exit-last.js b/test/fixtures/exit-last.js
new file mode 100644
index 0000000..899f475
--- /dev/null
+++ b/test/fixtures/exit-last.js
@@ -0,0 +1,14 @@
+var onSignalExit = require('../../')
+var counter = 0
+
+onSignalExit(function (code, signal) {
+ counter++
+ console.log('last counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+}, {alwaysLast: true})
+
+onSignalExit(function (code, signal) {
+ counter++
+ console.log('first counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+})
diff --git a/test/fixtures/exit.js b/test/fixtures/exit.js
new file mode 100644
index 0000000..c1aab3e
--- /dev/null
+++ b/test/fixtures/exit.js
@@ -0,0 +1,7 @@
+var onSignalExit = require('../../')
+
+onSignalExit(function (code, signal) {
+ console.log('exited with process.exit(), ' + code + ', ' + signal)
+})
+
+process.exit(32)
diff --git a/test/fixtures/exiter.js b/test/fixtures/exiter.js
new file mode 100644
index 0000000..61dc120
--- /dev/null
+++ b/test/fixtures/exiter.js
@@ -0,0 +1,44 @@
+var exit = process.argv[2] || 0
+
+var onSignalExit = require('../../')
+
+onSignalExit(function (code, signal) {
+ // some signals don't always get recognized properly, because
+ // they have the same numeric code.
+ if (wanted[1] === true) {
+ signal = !!signal
+ }
+ console.log('%j', {
+ found: [ code, signal ],
+ wanted: wanted
+ })
+})
+
+var wanted
+if (isNaN(exit)) {
+ switch (exit) {
+ case 'SIGIOT':
+ case 'SIGUNUSED':
+ case 'SIGPOLL':
+ wanted = [ null, true ]
+ break
+ default:
+ wanted = [ null, exit ]
+ break
+ }
+
+ try {
+ process.kill(process.pid, exit)
+ setTimeout(function () {}, 1000)
+ } catch (er) {
+ wanted = [ 0, null ]
+ }
+} else {
+ exit = +exit
+ wanted = [ exit, null ]
+ // If it's explicitly requested 0, then explicitly call it.
+ // "no arg" = "exit naturally"
+ if (exit || process.argv[2]) {
+ process.exit(exit)
+ }
+}
diff --git a/test/fixtures/load-unload.js b/test/fixtures/load-unload.js
new file mode 100644
index 0000000..5509e2e
--- /dev/null
+++ b/test/fixtures/load-unload.js
@@ -0,0 +1,7 @@
+// just be silly with calling these functions a bunch
+// mostly just to get coverage of the guard branches
+var onSignalExit = require('../../')
+onSignalExit.load()
+onSignalExit.load()
+onSignalExit.unload()
+onSignalExit.unload()
diff --git a/test/fixtures/multiple-load.js b/test/fixtures/multiple-load.js
new file mode 100644
index 0000000..02fcd7f
--- /dev/null
+++ b/test/fixtures/multiple-load.js
@@ -0,0 +1,52 @@
+// simulate cases where the module could be loaded from multiple places
+var onSignalExit = require('../../')
+var counter = 0
+
+onSignalExit(function (code, signal) {
+ counter++
+ console.log('last counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+}, {alwaysLast: true})
+
+onSignalExit(function (code, signal) {
+ counter++
+ console.log('first counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+})
+
+delete require('module')._cache[require.resolve('../../')]
+onSignalExit = require('../../')
+
+onSignalExit(function (code, signal) {
+ counter++
+ console.log('last counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+}, {alwaysLast: true})
+
+onSignalExit(function (code, signal) {
+ counter++
+ console.log('first counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+})
+
+// Lastly, some that should NOT be shown
+delete require('module')._cache[require.resolve('../../')]
+onSignalExit = require('../../')
+
+var unwrap = onSignalExit(function (code, signal) {
+ counter++
+ console.log('last counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+}, {alwaysLast: true})
+unwrap()
+
+unwrap = onSignalExit(function (code, signal) {
+ counter++
+ console.log('first counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+})
+
+unwrap()
+
+process.kill(process.pid, 'SIGHUP')
+setTimeout(function () {}, 1000)
diff --git a/test/fixtures/parent.js b/test/fixtures/parent.js
new file mode 100644
index 0000000..5dcc382
--- /dev/null
+++ b/test/fixtures/parent.js
@@ -0,0 +1,51 @@
+var signal = process.argv[2]
+var gens = +process.argv[3] || 0
+
+if (!signal || !isNaN(signal)) {
+ throw new Error('signal not provided')
+}
+
+var spawn = require('child_process').spawn
+var file = require.resolve('./awaiter.js')
+console.error(process.pid, signal, gens)
+
+if (gens > 0) {
+ file = __filename
+}
+
+var child = spawn(process.execPath, [file, signal, gens - 1], {
+ stdio: [ 0, 'pipe', 'pipe' ]
+})
+
+if (!gens) {
+ child.stderr.on('data', function () {
+ child.kill(signal)
+ })
+}
+
+var result = ''
+child.stdout.on('data', function (c) {
+ result += c
+})
+
+child.on('close', function (code, sig) {
+ try {
+ result = JSON.parse(result)
+ } catch (er) {
+ console.log('%j', {
+ error: 'failed to parse json\n' + er.message,
+ result: result,
+ pid: process.pid,
+ child: child.pid,
+ gens: gens,
+ expect: [ null, signal ],
+ actual: [ code, sig ]
+ })
+ return
+ }
+ if (result.wanted[1] === true) {
+ sig = !!sig
+ }
+ result.external = result.external || [ code, sig ]
+ console.log('%j', result)
+})
diff --git a/test/fixtures/sigint.js b/test/fixtures/sigint.js
new file mode 100644
index 0000000..769a076
--- /dev/null
+++ b/test/fixtures/sigint.js
@@ -0,0 +1,11 @@
+var onSignalExit = require('../../')
+
+onSignalExit(function (code, signal) {
+ console.log('exited with sigint, ' + code + ', ' + signal)
+})
+
+// For some reason, signals appear to not always be fast enough
+// to come in before the process exits. Just a few ticks needed.
+setTimeout(function () {}, 1000)
+
+process.kill(process.pid, 'SIGINT')
diff --git a/test/fixtures/sigkill.js b/test/fixtures/sigkill.js
new file mode 100644
index 0000000..88492d2
--- /dev/null
+++ b/test/fixtures/sigkill.js
@@ -0,0 +1,19 @@
+// SIGKILL can't be caught, and in fact, even trying to add the
+// listener will throw an error.
+// We handle that nicely.
+//
+// This is just here to get another few more lines of test
+// coverage. That's also why it lies about being on a linux
+// platform so that we pull in those other event types.
+
+Object.defineProperty(process, 'platform', {
+ value: 'linux',
+ writable: false,
+ enumerable: true,
+ configurable: true
+})
+
+var signals = require('../../signals.js')
+signals.push('SIGKILL')
+var onSignalExit = require('../../')
+onSignalExit.load()
diff --git a/test/fixtures/signal-default.js b/test/fixtures/signal-default.js
new file mode 100644
index 0000000..7016007
--- /dev/null
+++ b/test/fixtures/signal-default.js
@@ -0,0 +1,99 @@
+// This fixture is not used in any tests. It is here merely as a way to
+// do research into the various signal behaviors on Linux and Darwin.
+// Run with no args to cycle through every signal type. Run with a signal
+// arg to learn about how that signal behaves.
+
+if (process.argv[2]) {
+ child(process.argv[2])
+} else {
+ var signals = [
+ 'SIGABRT',
+ 'SIGALRM',
+ 'SIGBUS',
+ 'SIGCHLD',
+ 'SIGCLD',
+ 'SIGCONT',
+ 'SIGEMT',
+ 'SIGFPE',
+ 'SIGHUP',
+ 'SIGILL',
+ 'SIGINFO',
+ 'SIGINT',
+ 'SIGIO',
+ 'SIGIOT',
+ 'SIGKILL',
+ 'SIGLOST',
+ 'SIGPIPE',
+ 'SIGPOLL',
+ // 'SIGPROF', see #21
+ 'SIGPWR',
+ 'SIGQUIT',
+ 'SIGSEGV',
+ 'SIGSTKFLT',
+ 'SIGSTOP',
+ 'SIGSYS',
+ 'SIGTERM',
+ 'SIGTRAP',
+ 'SIGTSTP',
+ 'SIGTTIN',
+ 'SIGTTOU',
+ 'SIGUNUSED',
+ 'SIGURG',
+ 'SIGUSR1',
+ 'SIGUSR2',
+ 'SIGVTALRM',
+ 'SIGWINCH',
+ 'SIGXCPU',
+ 'SIGXFSZ'
+ ]
+
+ var spawn = require('child_process').spawn
+ ;(function test (signal) {
+ if (!signal) {
+ return
+ }
+ var child = spawn(process.execPath, [__filename, signal], { stdio: 'inherit' })
+ var timer = setTimeout(function () {
+ console.log('requires SIGCONT')
+ process.kill(child.pid, 'SIGCONT')
+ }, 750)
+
+ child.on('close', function (code, signal) {
+ console.log('code=%j signal=%j\n', code, signal)
+ clearTimeout(timer)
+ test(signals.pop())
+ })
+ })(signals.pop())
+}
+
+function child (signal) {
+ console.log('signal=%s', signal)
+
+ // set a timeout so we know whether or not the process terminated.
+ setTimeout(function () {
+ console.log('not terminated')
+ }, 200)
+
+ process.on('exit', function (code) {
+ console.log('emit exit code=%j', code)
+ })
+
+ try {
+ process.on(signal, function fn () {
+ console.log('signal is catchable', signal)
+ process.removeListener(signal, fn)
+ setTimeout(function () {
+ console.error('signal again')
+ process.kill(process.pid, signal)
+ })
+ })
+ } catch (er) {
+ console.log('not listenable')
+ }
+
+ try {
+ process.kill(process.pid, signal)
+ } catch (er) {
+ console.log('not issuable')
+ }
+}
diff --git a/test/fixtures/signal-last.js b/test/fixtures/signal-last.js
new file mode 100644
index 0000000..9e7dec8
--- /dev/null
+++ b/test/fixtures/signal-last.js
@@ -0,0 +1,17 @@
+var onSignalExit = require('../../')
+var counter = 0
+
+onSignalExit(function (code, signal) {
+ counter++
+ console.log('last counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+}, {alwaysLast: true})
+
+onSignalExit(function (code, signal) {
+ counter++
+ console.log('first counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+})
+
+process.kill(process.pid, 'SIGHUP')
+setTimeout(function () {}, 1000)
diff --git a/test/fixtures/signal-listener.js b/test/fixtures/signal-listener.js
new file mode 100644
index 0000000..5a84d12
--- /dev/null
+++ b/test/fixtures/signal-listener.js
@@ -0,0 +1,23 @@
+var onSignalExit = require('../../')
+
+setTimeout(function () {})
+
+var calledListener = 0
+onSignalExit(function (code, signal) {
+ console.log('exited calledListener=%j, code=%j, signal=%j',
+ calledListener, code, signal)
+})
+
+process.on('SIGHUP', listener)
+process.kill(process.pid, 'SIGHUP')
+
+function listener () {
+ calledListener++
+ if (calledListener > 3) {
+ process.removeListener('SIGHUP', listener)
+ }
+
+ setTimeout(function () {
+ process.kill(process.pid, 'SIGHUP')
+ })
+}
diff --git a/test/fixtures/sigpipe.js b/test/fixtures/sigpipe.js
new file mode 100644
index 0000000..169faed
--- /dev/null
+++ b/test/fixtures/sigpipe.js
@@ -0,0 +1,8 @@
+var onSignalExit = require('../..')
+onSignalExit(function (code, signal) {
+ console.error('onSignalExit(%j,%j)', code, signal)
+})
+setTimeout(function () {
+ console.log('hello')
+})
+process.kill(process.pid, 'SIGPIPE')
diff --git a/test/fixtures/sigterm.js b/test/fixtures/sigterm.js
new file mode 100644
index 0000000..85b598a
--- /dev/null
+++ b/test/fixtures/sigterm.js
@@ -0,0 +1,9 @@
+var onSignalExit = require('../../')
+
+onSignalExit(function (code, signal) {
+ console.log('exited with sigterm, ' + code + ', ' + signal)
+})
+
+setTimeout(function () {}, 1000)
+
+process.kill(process.pid, 'SIGTERM')
diff --git a/test/fixtures/unwrap.js b/test/fixtures/unwrap.js
new file mode 100644
index 0000000..8d8b1ad
--- /dev/null
+++ b/test/fixtures/unwrap.js
@@ -0,0 +1,37 @@
+// simulate cases where the module could be loaded from multiple places
+
+// Need to lie about this a little bit, since nyc uses this module
+// for its coverage wrap-up handling
+if (process.env.NYC_CWD) {
+ var emitter = process.__signal_exit_emitter__
+ var listeners = emitter.listeners('afterexit')
+ process.removeAllListeners('SIGHUP')
+ delete process.__signal_exit_emitter__
+ delete require('module')._cache[require.resolve('../../')]
+}
+
+var onSignalExit = require('../../')
+var counter = 0
+
+var unwrap = onSignalExit(function (code, signal) {
+ counter++
+ console.log('last counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+}, {alwaysLast: true})
+unwrap()
+
+unwrap = onSignalExit(function (code, signal) {
+ counter++
+ console.log('first counter=%j, code=%j, signal=%j',
+ counter, code, signal)
+})
+unwrap()
+
+if (global.__coverage__ && listeners && listeners.length) {
+ listeners.forEach(function (fn) {
+ onSignalExit(fn, { alwaysLast: true })
+ })
+}
+
+process.kill(process.pid, 'SIGHUP')
+setTimeout(function () {}, 1000)
diff --git a/test/multi-exit.js b/test/multi-exit.js
new file mode 100644
index 0000000..0bcbb30
--- /dev/null
+++ b/test/multi-exit.js
@@ -0,0 +1,61 @@
+var exec = require('child_process').exec
+var t = require('tap')
+var isWindows = process.platform === 'win32'
+var shell = isWindows ? null : { shell: '/bin/bash' }
+var node = isWindows ? '"' + process.execPath + '"' : process.execPath
+
+var fixture = require.resolve('./fixtures/change-code.js')
+var expect = require('./fixtures/change-code-expect.json')
+
+// process.exitCode has problems prior to:
+// https://github.com/joyent/node/commit/c0d81f90996667a658aa4403123e02161262506a
+function isZero10 () {
+ return /^v0\.10\..+$/.test(process.version)
+}
+
+// process.exit(code), process.exitCode = code, normal exit
+var types = [ 'explicit', 'normal' ]
+if (!isZero10()) types.push('code')
+
+// initial code that is set. Note, for 'normal' exit, there's no
+// point doing these, because we just exit without modifying code
+var codes = [ 0, 2, 'null' ]
+
+// do not change, change to 5 with exit(), change to 5 with exitCode,
+// change to 5 and then to 2 with exit(), change twice with exitcode
+var changes = [ 'nochange', 'change', 'twice' ]
+if (!isZero10()) changes.push('code', 'twicecode')
+
+// use signal-exit, use process.on('exit')
+var handlers = [ 'sigexit', 'nosigexit' ]
+
+var opts = []
+types.forEach(function (type) {
+ var testCodes = type === 'normal' ? [0] : codes
+ testCodes.forEach(function (code) {
+ changes.forEach(function (change) {
+ handlers.forEach(function (handler) {
+ opts.push([type, code, change, handler].join(' '))
+ })
+ })
+ })
+})
+
+opts.forEach(function (opt) {
+ t.test(opt, function (t) {
+ var cmd = node + ' ' + fixture + ' ' + opt
+ exec(cmd, shell, function (err, stdout, stderr) {
+ var res = JSON.parse(stdout)
+ if (err) {
+ res.actualCode = err.code
+ res.actualSignal = err.signal
+ } else {
+ res.actualCode = 0
+ res.actualSignal = null
+ }
+ res.stderr = stderr.trim().split('\n')
+ t.same(res, expect[opt])
+ t.end()
+ })
+ })
+})
diff --git a/test/signal-exit-test.js b/test/signal-exit-test.js
new file mode 100644
index 0000000..b816273
--- /dev/null
+++ b/test/signal-exit-test.js
@@ -0,0 +1,116 @@
+/* global describe, it */
+
+var exec = require('child_process').exec
+var expect = require('chai').expect
+var assert = require('assert')
+var isWindows = process.platform === 'win32'
+var shell = isWindows ? null : { shell: '/bin/bash' }
+var node = isWindows ? '"' + process.execPath + '"' : process.execPath
+
+require('chai').should()
+require('tap').mochaGlobals()
+
+describe('signal-exit', function () {
+ it('receives an exit event when a process exits normally', function (done) {
+ exec(node + ' ./test/fixtures/end-of-execution.js', shell, function (err, stdout, stderr) {
+ expect(err).to.equal(null)
+ stdout.should.match(/reached end of execution, 0, null/)
+ done()
+ })
+ })
+
+ it('receives an exit event when process.exit() is called', function (done) {
+ exec(node + ' ./test/fixtures/exit.js', shell, function (err, stdout, stderr) {
+ if (!isWindows) err.code.should.equal(32)
+ stdout.should.match(/exited with process\.exit\(\), 32, null/)
+ done()
+ })
+ })
+
+ it('ensures that if alwaysLast=true, the handler is run last (signal)', function (done) {
+ exec(node + ' ./test/fixtures/signal-last.js', shell, function (err, stdout, stderr) {
+ assert(err)
+ stdout.should.match(/first counter=1/)
+ stdout.should.match(/last counter=2/)
+ done()
+ })
+ })
+
+ it('ensures that if alwaysLast=true, the handler is run last (normal exit)', function (done) {
+ exec(node + ' ./test/fixtures/exit-last.js', shell, function (err, stdout, stderr) {
+ assert.ifError(err)
+ stdout.should.match(/first counter=1/)
+ stdout.should.match(/last counter=2/)
+ done()
+ })
+ })
+
+ it('works when loaded multiple times', function (done) {
+ exec(node + ' ./test/fixtures/multiple-load.js', shell, function (err, stdout, stderr) {
+ assert(err)
+ stdout.should.match(/first counter=1/)
+ stdout.should.match(/first counter=2/)
+ stdout.should.match(/last counter=3/)
+ stdout.should.match(/last counter=4/)
+ done()
+ })
+ })
+
+ it('removes handlers when fully unwrapped', function (done) {
+ exec(node + ' ./test/fixtures/unwrap.js', shell, function (err, stdout, stderr) {
+ assert(err)
+ if (!isWindows) err.signal.should.equal('SIGHUP')
+ if (!isWindows) expect(err.code).to.equal(null)
+ done()
+ })
+ })
+
+ it('does not load() or unload() more than once', function (done) {
+ exec(node + ' ./test/fixtures/load-unload.js', shell, function (err, stdout, stderr) {
+ assert.ifError(err)
+ done()
+ })
+ })
+
+ if (!isWindows) {
+ it('receives an exit event when a process is terminated with sigint', function (done) {
+ exec(node + ' ./test/fixtures/sigint.js', shell, function (err, stdout, stderr) {
+ assert(err)
+ stdout.should.match(/exited with sigint, null, SIGINT/)
+ done()
+ })
+ })
+
+ it('receives an exit event when a process is terminated with sigterm', function (done) {
+ exec(node + ' ./test/fixtures/sigterm.js', shell, function (err, stdout, stderr) {
+ assert(err)
+ stdout.should.match(/exited with sigterm, null, SIGTERM/)
+ done()
+ })
+ })
+
+ it('does not exit on sigpipe', function (done) {
+ exec(node + ' ./test/fixtures/sigpipe.js', shell, function (err, stdout, stderr) {
+ assert.ifError(err)
+ stdout.should.match(/hello/)
+ stderr.should.match(/onSignalExit\(0,null\)/)
+ done()
+ })
+ })
+
+ it('handles uncatchable signals with grace and poise', function (done) {
+ exec(node + ' ./test/fixtures/sigkill.js', shell, function (err, stdout, stderr) {
+ assert.ifError(err)
+ done()
+ })
+ })
+
+ it('does not exit if user handles signal', function (done) {
+ exec(node + ' ./test/fixtures/signal-listener.js', shell, function (err, stdout, stderr) {
+ assert(err)
+ assert.equal(stdout, 'exited calledListener=4, code=null, signal="SIGHUP"\n')
+ done()
+ })
+ })
+ }
+})
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-signal-exit.git
More information about the Pkg-javascript-commits
mailing list