Skip to content

Commit

Permalink
Merge pull request #25 from paulpflug/fix-for-#22
Browse files Browse the repository at this point in the history
fix for #22
  • Loading branch information
keithamus committed Jul 28, 2015
2 parents 68f83b8 + f094e9a commit c39d0f7
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 29 deletions.
18 changes: 15 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,24 @@ function status () {

// closes all children and the process
function close (code) {
var i, len;
var i, len, closed = 0, opened = 0;

for (i = 0, len = children.length; i < len; i++) {
if (!children[i].exitCode) {
opened++;
children[i].removeAllListeners('close');
children[i].kill('SIGINT');
children[i].kill("SIGINT");
if (verbose) console.log('`' + children[i].cmd + '` will now be closed');
children[i].on('close', function() {
closed++;
if (opened == closed) {
process.exit(code);
}
});
}
}
process.exit(code);
if (opened == closed) {process.exit(code);}

}

// cross platform compatibility
Expand All @@ -89,6 +98,9 @@ if (process.platform === 'win32') {
// start the children
children = [];
cmds.forEach(function (cmd) {
if (process.platform != 'win32') {
cmd = "exec "+cmd;
}
var child = spawn(sh,[shFlag,cmd], {
cwd: process.cwd,
env: process.env,
Expand Down
87 changes: 61 additions & 26 deletions test/index.coffee
Original file line number Diff line number Diff line change
@@ -1,84 +1,119 @@
chai = require "chai"
should = chai.should()
spawn = require("child_process").spawn;
spawn = require("child_process").spawn
Promise = require("bluebird")

verbose = 0

# cross platform compatibility
if process.platform == "win32"
sh = "cmd";
shFlag = "/c";
sh = "cmd"
shArg = "/c"
else
sh = "sh";
shFlag = "-c";

sh = "sh"
shArg = "-c"

# children
waitingProcess = "\\\"node -e 'setTimeout(function(){},10000);'\\\""
failingProcess = "\\\"node -e 'throw new Error(\"someError\");'\\\""
waitingProcess = "\"node -e 'setTimeout(function(){},10000);'\""
failingProcess = "\"node -e 'throw new Error();'\""

usageInfo = """
-h, --help output usage information
-v, --verbose verbose logging
-w, --wait will not close sibling processes on error
""".split("\n")

cmdWrapper = (cmd) ->
if process.platform != "win32"
cmd = "exec "+cmd
if verbose
console.log "Calling: "+cmd
return cmd

spawnParallelshell = (cmd) ->
return spawn sh, [shFlag, "node './index.js' " + cmd], {
cwd: process.cwd
}
return spawn sh, [shArg, cmdWrapper("node ./index.js "+cmd )], {
cwd: process.cwd
}

killPs = (ps) ->
ps.kill "SIGINT"

spyOnPs = (ps, verbosity=1) ->
if verbose >= verbosity
ps.stdout.setEncoding("utf8")
ps.stdout.on "data", (data) ->
console.log data
ps.stderr.setEncoding("utf8")
ps.stderr.on "data", (data) ->
console.log "err: "+data

testOutput = (cmd, expectedOutput) ->
return new Promise (resolve) ->
ps = spawnParallelshell(cmd)
spyOnPs ps, 3
ps.stdout.setEncoding("utf8")
output = []
ps.stdout.on "data", (data) ->
lines = data.split("\n")
lines.pop() if lines[lines.length-1] == ""
output = output.concat(lines)
ps.stdout.on "end", () ->
for line,i in output
line.should.equal expectedOutput[i]
for line,i in expectedOutput
line.should.equal output[i]
resolve()

describe "parallelshell", ->
it "should print on -h and --help", (done) ->
Promise.all([testOutput("-h", usageInfo), testOutput("-help", usageInfo)])
.finally done
Promise.all([testOutput("-h", usageInfo), testOutput("--help", usageInfo)])
.then -> done()
.catch done

it "should close with exitCode 2 on child error", (done) ->
it "should close with exitCode 1 on child error", (done) ->
ps = spawnParallelshell(failingProcess)
spyOnPs ps, 2
ps.on "close", () ->
ps.exitCode.should.equal 2
ps.exitCode.should.equal 1
done()

it "should run with a normal child", (done) ->
ps = spawnParallelshell(waitingProcess)
spyOnPs ps, 1
ps.on "close", () ->
ps.signalCode.should.equal "SIGINT"
done()

setTimeout (() ->
should.not.exist(ps.signalCode)
ps.kill()
done()
),100
killPs(ps)
),50


it "should close sibling processes on child error", (done) ->
ps = spawnParallelshell([waitingProcess,failingProcess,waitingProcess].join(" "))
spyOnPs ps,2
ps.on "close", () ->
ps.exitCode.should.equal 2
ps.exitCode.should.equal 1
done()

it "should wait for sibling processes on child error when called with -w or --wait", (done) ->
ps = spawnParallelshell(["-w",waitingProcess,failingProcess,waitingProcess].join(" "))
ps2 = spawnParallelshell(["--wait",waitingProcess,failingProcess,waitingProcess].join(" "))
spyOnPs ps,2
spyOnPs ps2,2
setTimeout (() ->
should.not.exist(ps.signalCode)
should.not.exist(ps2.signalCode)
ps.kill()
ps2.kill()
done()
),100
killPs(ps)
killPs(ps2)
),50
Promise.all [new Promise((resolve) -> ps.on("close",resolve)),
new Promise (resolve) -> ps2.on("close",resolve)]
.then -> done()
.catch done
it "should close on CTRL+C / SIGINT", (done) ->
ps = spawnParallelshell(["-w",waitingProcess,failingProcess,waitingProcess].join(" "))
spyOnPs ps,2
ps.on "close", () ->
ps.signalCode.should.equal "SIGINT"
done()
ps.kill("SIGINT")
killPs(ps)

0 comments on commit c39d0f7

Please sign in to comment.