Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix for #22 #25

Merged
merged 6 commits into from
Jul 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)