-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
child_process: check execFile and fork args #2667
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Port of joyent/node commits: * nodejs/node-v0.x-archive@e17c5a7 * nodejs/node-v0.x-archive@70dafa7 Pull over test-child-process-spawn-typeerror.js from v0.12, replacing the existing test in master. The new test includes a broader set of tests on the various arg choices and throws.
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,28 @@ | ||
| 'use strict'; | ||
| var assert = require('assert'); | ||
| var child_process = require('child_process'); | ||
| var spawn = child_process.spawn; | ||
| var cmd = require('../common').isWindows ? 'rundll32' : 'ls'; | ||
| var invalidArgsMsg = /Incorrect value of args option/; | ||
| var invalidOptionsMsg = /options argument must be an object/; | ||
|
|
||
| // verify that args argument must be an array | ||
| assert.throws(function() { | ||
| spawn(cmd, 'this is not an array'); | ||
| }, TypeError); | ||
| var assert = require('assert'), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can these be broken into separate
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I'd prefer that actually. Just literally pulled this over straight from what is in v0.12. |
||
| child_process = require('child_process'), | ||
| spawn = child_process.spawn, | ||
| fork = child_process.fork, | ||
| execFile = child_process.execFile, | ||
| windows = (process.platform === 'win32'), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now have |
||
| cmd = (windows) ? 'rundll32' : 'ls', | ||
| invalidcmd = 'hopefully_you_dont_have_this_on_your_machine', | ||
| invalidArgsMsg = /Incorrect value of args option/, | ||
| invalidOptionsMsg = /options argument must be an object/, | ||
| empty = require('../common').fixturesDir + '/empty.js', | ||
| errors = 0; | ||
|
|
||
| try { | ||
| // Ensure this throws a TypeError | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then we should use |
||
| var child = spawn(invalidcmd, 'this is not an array'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. const child
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not seeing this edit as being critical to land this. can do this in the next pass.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. No problem. We can actually remove the variable and directly attach the even handler. |
||
|
|
||
| child.on('error', function(err) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| errors++; | ||
| }); | ||
|
|
||
| } catch (e) { | ||
| assert.equal(e instanceof TypeError, true); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| // verify that valid argument combinations do not throw | ||
| assert.doesNotThrow(function() { | ||
|
|
@@ -49,3 +62,87 @@ assert.throws(function() { | |
| spawn(cmd, [], 1); | ||
| }, invalidOptionsMsg); | ||
|
|
||
| process.on('exit', function() { | ||
| assert.equal(errors, 0); | ||
| }); | ||
|
|
||
| // Argument types for combinatorics | ||
| var a = [], | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. const |
||
| o = {}, | ||
| c = (function callback() {}), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Parens around the function expression is not necessary I guess. |
||
| s = 'string', | ||
| u = undefined, | ||
| n = null; | ||
|
|
||
| // function spawn(file=f [,args=a] [, options=o]) has valid combinations: | ||
| // (f) | ||
| // (f, a) | ||
| // (f, a, o) | ||
| // (f, o) | ||
| assert.doesNotThrow(function() { spawn(cmd); }); | ||
| assert.doesNotThrow(function() { spawn(cmd, a); }); | ||
| assert.doesNotThrow(function() { spawn(cmd, a, o); }); | ||
| assert.doesNotThrow(function() { spawn(cmd, o); }); | ||
|
|
||
| // Variants of undefined as explicit 'no argument' at a position | ||
| assert.doesNotThrow(function() { spawn(cmd, u, o); }); | ||
| assert.doesNotThrow(function() { spawn(cmd, a, u); }); | ||
|
|
||
| assert.throws(function() { spawn(cmd, n, o); }, TypeError); | ||
| assert.throws(function() { spawn(cmd, a, n); }, TypeError); | ||
|
|
||
| assert.throws(function() { spawn(cmd, s); }, TypeError); | ||
| assert.throws(function() { spawn(cmd, a, s); }, TypeError); | ||
|
|
||
|
|
||
| // verify that execFile has same argument parsing behaviour as spawn | ||
| // | ||
| // function execFile(file=f [,args=a] [, options=o] [, callback=c]) has valid | ||
| // combinations: | ||
| // (f) | ||
| // (f, a) | ||
| // (f, a, o) | ||
| // (f, a, o, c) | ||
| // (f, a, c) | ||
| // (f, o) | ||
| // (f, o, c) | ||
| // (f, c) | ||
| assert.doesNotThrow(function() { execFile(cmd); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, o); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, o, c); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, c); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, o); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, o, c); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, c); }); | ||
|
|
||
| // Variants of undefined as explicit 'no argument' at a position | ||
| assert.doesNotThrow(function() { execFile(cmd, u, o, c); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, u, c); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, o, u); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, n, o, c); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, n, c); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, o, n); }); | ||
|
|
||
| // string is invalid in arg position (this may seem strange, but is | ||
| // consistent across node API, cf. `net.createServer('not options', 'not | ||
| // callback')` | ||
| assert.throws(function() { execFile(cmd, s, o, c); }, TypeError); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, s, c); }); | ||
| assert.doesNotThrow(function() { execFile(cmd, a, o, s); }); | ||
|
|
||
|
|
||
| // verify that fork has same argument parsing behaviour as spawn | ||
| // | ||
| // function fork(file=f [,args=a] [, options=o]) has valid combinations: | ||
| // (f) | ||
| // (f, a) | ||
| // (f, a, o) | ||
| // (f, o) | ||
| assert.doesNotThrow(function() { fork(empty); }); | ||
| assert.doesNotThrow(function() { fork(empty, a); }); | ||
| assert.doesNotThrow(function() { fork(empty, a, o); }); | ||
| assert.doesNotThrow(function() { fork(empty, o); }); | ||
|
|
||
| assert.throws(function() { fork(empty, s); }, TypeError); | ||
| assert.doesNotThrow(function() { fork(empty, a, s); }, TypeError); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible that both of these fail? If neither is supposed to then mind adding a final
elsewith something likethrow new Error('UNREACHABLE');or the like?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the fall through on this particular one is required for the next check. Overall, the currently behavior in v0.12 (and this port of that code) is really just a half measure. We're not actually doing a complete check. For instance,
execFile('ls',{a:1},'test')goes through without a throw even tho a string is passed in for the callback. Internally,execFilejust acts as if the callback wasn't provided at all. Likewise,execFile('ls',[], 'test', function() {})does not throw either. Nor doesexecFile('ls',[],'test','test'). The only type that is actually checked is theargs, so that if you pass inexecFile('ls', 'test'), aTypeErrorwill throw.Given that this is exactly how it behaves in v0.12 currently (for better or worse), I'd be more inclined to land it as is so we can close the convergence loop first, then make additional improvements separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation. Sounds like a good plan. Maybe put a todo comment in the code, or we can open an issue for what you've explained and land the fix in a future master. From how you've described it, there is ambiguity with argument parsing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we pull the common
pos < arguments.lengthcheck outside?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure it's worth it to add the nested conditional. performance-wise there's no measurable gain.
so basically, I don't think it's necessary but I'm also not opposed to the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but it will improve the readability and reduce redundant checks. It's just a suggestion I am okay without that as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we'll need to revisit this anyway later. Let's leave it as is
for now and do any additional cleanups in the next round.
On Sep 2, 2015 10:29 PM, "thefourtheye" notifications@github.com wrote:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool. Since we introduced new exception, this would be semver-major?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically. But this is already in v0.12 and is part of the convergence work. It will land in master and will be cherry picked to v4.x