Skip to content

Commit 88e2ee1

Browse files
committed
fix test for #121
dropping support for node v6.x as it is not working correctly with the installed SIGINT handlers
1 parent 0c5bc5c commit 88e2ee1

14 files changed

+222
-87
lines changed

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module.exports = {
2323
"semi": [
2424
"error",
2525
"always"
26-
]
26+
],
27+
"no-extra-boolean-cast": 0
2728
}
2829
};

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ node_js:
44
- "10"
55
- "9"
66
- "8"
7-
- "6"
87
sudo: false
98
cache:
109
directories:

lib/tmp.js

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ const
4343
DIR_MODE = 448 /* 0o700 */,
4444
FILE_MODE = 384 /* 0o600 */,
4545

46-
EVENT = 'exit',
46+
EXIT = 'exit',
47+
48+
SIGINT = 'SIGINT',
4749

4850
// this will hold the objects need to be removed on exit
4951
_removeObjects = [];
@@ -101,7 +103,7 @@ function _isUndefined(obj) {
101103
*/
102104
function _parseArguments(options, callback) {
103105
/* istanbul ignore else */
104-
if (typeof options == 'function') {
106+
if (typeof options === 'function') {
105107
return [{}, options];
106108
}
107109

@@ -132,7 +134,7 @@ function _generateTmpName(opts) {
132134
var template = opts.template;
133135
// make sure that we prepend the tmp path if none was given
134136
/* istanbul ignore else */
135-
if (path.basename(template) == template)
137+
if (path.basename(template) === template)
136138
template = path.join(opts.dir || tmpDir, template);
137139
return template.replace(TEMPLATE_PATTERN, _randomChars(6));
138140
}
@@ -479,7 +481,7 @@ function _prepareRemoveCallback(removeFunction, arg, cleanupCallbackSync) {
479481

480482
called = true;
481483
// sync?
482-
if (removeFunction.length == 1) {
484+
if (removeFunction.length === 1) {
483485
try {
484486
removeFunction(arg);
485487
return next(null);
@@ -550,7 +552,7 @@ function isENOENT(error) {
550552
* error.errno n/a
551553
*/
552554
function isExpectedError(error, code, errno) {
553-
return error.code == code || error.code == errno;
555+
return error.code === code || error.code === errno;
554556
}
555557

556558
/**
@@ -569,60 +571,87 @@ function setGracefulCleanup() {
569571
* @returns {Boolean} true whether listener is a legacy listener
570572
*/
571573
function _is_legacy_listener(listener) {
572-
return (listener.name == '_exit' || listener.name == '_uncaughtExceptionThrown')
574+
return (listener.name === '_exit' || listener.name === '_uncaughtExceptionThrown')
573575
&& listener.toString().indexOf('_garbageCollector();') > -1;
574576
}
575577

576578
/**
577-
* Safely install process exit listeners.
578-
*
579+
* Safely install SIGINT listener.
580+
*
581+
* NOTE: this will only work on OSX and Linux.
582+
*
579583
* @private
580584
*/
581-
function _safely_install_listener() {
582-
var listeners = process.listeners(EVENT);
585+
function _safely_install_sigint_listener() {
583586

584-
// collect any existing listeners
585-
var existingListeners = [];
586-
for (var i = 0, length = listeners.length; i < length; i++) {
587-
var lstnr = listeners[i];
587+
const listeners = process.listeners(SIGINT);
588+
const existingListeners = [];
589+
for (let i = 0, length = listeners.length; i < length; i++) {
590+
const lstnr = listeners[i];
588591
/* istanbul ignore else */
589-
if (lstnr.name == '_tmp$safe_listener' || _is_legacy_listener(lstnr)) {
590-
// we must forget about the uncaughtException listener
591-
if (lstnr.name != '_uncaughtExceptionThrown') existingListeners.push(lstnr);
592-
process.removeListener(EVENT, lstnr);
592+
if (lstnr.name === '_tmp$sigint_listener') {
593+
existingListeners.push(lstnr);
594+
process.removeListener(SIGINT, lstnr);
593595
}
594596
}
595-
596-
// windows does not support signals
597-
// it'd never had won if it wasn't a major PITA
598-
// with node v8.x and win 10 this is no longer an issue
599-
if (process.platform == 'win32') {
600-
var rl = require('readline').createInterface({
601-
input: process.stdin,
602-
output: process.stdout
603-
});
604-
605-
rl.on('SIGINT', function () {
606-
process.emit('SIGINT');
607-
});
608-
}
609-
610-
process.on('SIGINT', function () {
611-
process.exit(0);
597+
process.on(SIGINT, function _tmp$sigint_listener(doExit) {
598+
for (let i = 0, length = existingListeners.length; i < length; i++) {
599+
// let the existing listener do the garbage collection (e.g. jest sandbox)
600+
try {
601+
existingListeners[i](false);
602+
} catch (err) {
603+
// ignore
604+
}
605+
}
606+
try {
607+
// force the garbage collector even it is called again in the exit listener
608+
_garbageCollector();
609+
} finally {
610+
if (!!doExit) {
611+
process.exit(0);
612+
}
613+
}
612614
});
615+
}
616+
617+
/**
618+
* Safely install process exit listener.
619+
*
620+
* @private
621+
*/
622+
function _safely_install_exit_listener() {
623+
const listeners = process.listeners(EXIT);
613624

614-
process.addListener(EVENT, function _tmp$safe_listener(data) {
625+
// collect any existing listeners
626+
const existingListeners = [];
627+
for (let i = 0, length = listeners.length; i < length; i++) {
628+
const lstnr = listeners[i];
615629
/* istanbul ignore else */
616-
if (existingListeners.length) {
617-
for (var i = 0, length = existingListeners.length; i < length; i++) {
630+
// TODO: remove support for legacy listeners once release 1.0.0 is out
631+
if (lstnr.name === '_tmp$safe_listener' || _is_legacy_listener(lstnr)) {
632+
// we must forget about the uncaughtException listener, hopefully it is ours
633+
if (lstnr.name !== '_uncaughtExceptionThrown') {
634+
existingListeners.push(lstnr);
635+
}
636+
process.removeListener(EXIT, lstnr);
637+
}
638+
}
639+
// TODO: what was the data parameter good for?
640+
process.addListener(EXIT, function _tmp$safe_listener(data) {
641+
for (let i = 0, length = existingListeners.length; i < length; i++) {
642+
// let the existing listener do the garbage collection (e.g. jest sandbox)
643+
try {
618644
existingListeners[i](data);
645+
} catch (err) {
646+
// ignore
619647
}
620648
}
621649
_garbageCollector();
622650
});
623651
}
624652

625-
_safely_install_listener();
653+
_safely_install_exit_listener();
654+
_safely_install_sigint_listener();
626655

627656
/**
628657
* Configuration options.

test/child-process.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,25 @@ module.exports.genericChildProcess = _spawnProcess('spawn-generic.js');
1212
module.exports.childProcess = _spawnProcess('spawn-custom.js');
1313

1414
function _spawnProcess(spawnFile) {
15-
return function (testCase, configFile, cb) {
16-
var
15+
return function (testCase, configFile, cb, signal) {
16+
const
1717
configFilePath = path.join(__dirname, 'outband', configFile),
1818
commandArgs = [path.join(__dirname, spawnFile), configFilePath];
1919

2020
exists(configFilePath, function (configExists) {
21-
if (configExists) return _doSpawn(commandArgs, cb);
21+
if (configExists) return _doSpawn(commandArgs, cb, signal);
2222

2323
cb(new Error('ENOENT: configFile ' + configFilePath + ' does not exist'));
2424
});
2525
};
2626
}
2727

28-
function _doSpawn(commandArgs, cb) {
29-
var
28+
function _doSpawn(commandArgs, cb, signal) {
29+
const
3030
node_path = process.argv[0],
3131
stdoutBufs = [],
32-
stderrBufs = [],
32+
stderrBufs = [];
33+
let
3334
child,
3435
done = false,
3536
stderrDone = false,
@@ -48,14 +49,11 @@ function _doSpawn(commandArgs, cb) {
4849
child = spawn(node_path, commandArgs);
4950
child.stdin.end();
5051

51-
// TODO we no longer support node 0.6
52-
// Cannot use 'close' event because not on node-0.6.
5352
function _close() {
54-
var
55-
stderr = _bufferConcat(stderrBufs).toString(),
56-
stdout = _bufferConcat(stdoutBufs).toString();
57-
5853
if (stderrDone && stdoutDone && !done) {
54+
const
55+
stderr = _bufferConcat(stderrBufs).toString(),
56+
stdout = _bufferConcat(stdoutBufs).toString();
5957
done = true;
6058
cb(null, stderr, stdout);
6159
}
@@ -81,6 +79,13 @@ function _doSpawn(commandArgs, cb) {
8179
stderrDone = true;
8280
_close();
8381
});
82+
83+
if (signal) {
84+
setTimeout(function () {
85+
// does not work on node 6
86+
child.kill(signal);
87+
}, 2000);
88+
}
8489
}
8590

8691
function _bufferConcat(buffers) {
@@ -89,10 +94,9 @@ function _bufferConcat(buffers) {
8994
}
9095

9196
return new Buffer(buffers.reduce(function (acc, buf) {
92-
for (var i = 0; i < buf.length; i++) {
97+
for (let i = 0; i < buf.length; i++) {
9398
acc.push(buf[i]);
9499
}
95100
return acc;
96101
}, []));
97102
}
98-

test/issue121-test.js

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,57 @@
22
// vim: expandtab:ts=2:sw=2
33

44
const
5-
assert = require('assert'),
65
assertions = require('./assertions'),
76
childProcess = require('./child-process').childProcess,
8-
signals = ['SIGINT', 'SIGTERM'];
7+
os = require('os'),
8+
rimraf = require('rimraf'),
9+
testCases = [
10+
{ signal: 'SIGINT', expectExists: false },
11+
{ signal: 'SIGTERM', expectExists: true }
12+
];
13+
14+
const isWindows = os.platform() === 'win32';
15+
16+
let tfunc = it;
17+
if (isWindows) {
18+
// skip tests on win32
19+
tfunc = xit;
20+
}
921

1022
describe('tmp', function () {
1123
describe('issue121 - clean up on terminating signals', function () {
12-
for (var i=0; i < signals.length; i++) {
13-
issue121Tests(signals[i]);
24+
for (let tc of testCases) {
25+
tfunc('for signal ' + tc.signal, function (done) {
26+
// increase timeout so that the child process may fail
27+
this.timeout(20000);
28+
issue121Tests(tc.signal, tc.expectExists)(done);
29+
});
1430
}
1531
});
1632
});
1733

18-
function issue121Tests(signal) {
34+
function issue121Tests(signal, expectExists) {
1935
return function (done) {
20-
childProcess('issue121.json', function (err, stderr, stdout) {
36+
childProcess(this, 'issue121.json', function (err, stderr, stdout) {
2137
if (err) return done(err);
2238
else if (stderr) return done(new Error(stderr));
23-
else assertions.assertDoesNotExist(stdout);
24-
done();
25-
}, true);
39+
40+
try {
41+
if (expectExists) {
42+
assertions.assertExists(stdout);
43+
}
44+
else {
45+
assertions.assertDoesNotExist(stdout);
46+
}
47+
done();
48+
} catch (err) {
49+
done(err);
50+
} finally {
51+
// cleanup
52+
if (expectExists) {
53+
rimraf.sync(stdout);
54+
}
55+
}
56+
}, signal);
2657
};
2758
}

test/issue129-sigint-test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* eslint-disable no-octal */
2+
// vim: expandtab:ts=2:sw=2
3+
4+
var
5+
assert = require('assert'),
6+
assertions = require('./assertions'),
7+
childProcess = require('./child-process').childProcess;
8+
9+
describe('tmp', function () {
10+
describe('issue129: safely install sigint listeners', function () {
11+
it('when simulating sandboxed behavior', function (done) {
12+
childProcess(this, 'issue129-sigint.json', function (err, stderr, stdout) {
13+
if (err) return done(err);
14+
if (!stdout && !stderr) return done(new Error('stderr or stdout expected'));
15+
if (stderr) {
16+
try {
17+
assertions.assertDoesNotStartWith(stderr, 'EEXISTS:MULTIPLE');
18+
assertions.assertDoesNotStartWith(stderr, 'ENOAVAIL:EXISTING');
19+
} catch (err) {
20+
return done(err);
21+
}
22+
}
23+
if (stdout) {
24+
try {
25+
assert.equal(stdout, 'EOK', 'existing listeners should have been removed and called');
26+
} catch (err) {
27+
return done(err);
28+
}
29+
}
30+
done();
31+
});
32+
});
33+
});
34+
});

test/outband/issue121.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
/* eslint-disable no-octal */
22
// vim: expandtab:ts=2:sw=2
33

4-
var
5-
fs = require('fs'),
6-
tmp = require('../../lib/tmp'),
7-
// we reuse the fixtures from issue62 here
8-
fixture = require('./issue62');
9-
10-
tmp.setGracefulCleanup();
4+
const
5+
tmp = require('../../lib/tmp');
116

127
// https://github.com/raszi/node-tmp/issues/121
13-
module.exports = function (signal) {
14-
fixture.apply(this, [tmp.dirSync({ unsafeCleanup: true }), tmp]);
8+
module.exports = function () {
9+
10+
tmp.setGracefulCleanup();
11+
12+
const result = tmp.dirSync({ unsafeCleanup: true });
1513

16-
// make sure that the process keeps running
17-
setTimeout(function () {}, 1000000);
14+
this.out(result.name, function () { });
1815

19-
this.kill(signal);
16+
setTimeout(function () {
17+
throw new Error('ran into timeout');
18+
}, 10000);
2019
};

0 commit comments

Comments
 (0)