|
1 | 1 | import path from 'path'; |
2 | 2 | import stream from 'stream'; |
| 3 | +import childProcess from 'child_process'; |
3 | 4 | import test from 'ava'; |
4 | 5 | import getStream from 'get-stream'; |
5 | 6 | import m from './'; |
@@ -33,9 +34,10 @@ test('stdout/stderr available on errors', async t => { |
33 | 34 | t.is(typeof err.stderr, 'string'); |
34 | 35 | }); |
35 | 36 |
|
36 | | -test('include stdout in errors for improved debugging', async t => { |
| 37 | +test('include stdout and stderr in errors for improved debugging', async t => { |
37 | 38 | const err = await t.throws(m('fixtures/error-message.js')); |
38 | 39 | t.regex(err.message, /stdout/); |
| 40 | + t.regex(err.message, /stderr/); |
39 | 41 | }); |
40 | 42 |
|
41 | 43 | test('execa.shell()', async t => { |
@@ -163,3 +165,114 @@ test(`use relative path with '..' chars`, async t => { |
163 | 165 | const {stdout} = await m(pathViaParentDir, ['foo']); |
164 | 166 | t.is(stdout, 'foo'); |
165 | 167 | }); |
| 168 | + |
| 169 | +test('err.killed is true if process was killed directly', async t => { |
| 170 | + const cp = m('forever'); |
| 171 | + |
| 172 | + setTimeout(function () { |
| 173 | + cp.kill(); |
| 174 | + }, 100); |
| 175 | + |
| 176 | + const err = await t.throws(cp); |
| 177 | + |
| 178 | + t.true(err.killed); |
| 179 | +}); |
| 180 | + |
| 181 | +// TODO: Should this really be the case, or should we improve on child_process? |
| 182 | +test('err.killed is false if process was killed indirectly', async t => { |
| 183 | + const cp = m('forever'); |
| 184 | + |
| 185 | + setTimeout(function () { |
| 186 | + process.kill(cp.pid, 'SIGINT'); |
| 187 | + }, 100); |
| 188 | + |
| 189 | + const err = await t.throws(cp); |
| 190 | + |
| 191 | + t.false(err.killed); |
| 192 | +}); |
| 193 | + |
| 194 | +if (process.platform === 'darwin') { |
| 195 | + test.cb('sanity check: child_process.exec also has killed.false if killed indirectly', t => { |
| 196 | + const cp = childProcess.exec('forever', err => { |
| 197 | + t.truthy(err); |
| 198 | + t.false(err.killed); |
| 199 | + t.end(); |
| 200 | + }); |
| 201 | + |
| 202 | + setTimeout(function () { |
| 203 | + process.kill(cp.pid, 'SIGINT'); |
| 204 | + }, 100); |
| 205 | + }); |
| 206 | +} |
| 207 | + |
| 208 | +if (process.platform !== 'win32') { |
| 209 | + test('err.signal is SIGINT', async t => { |
| 210 | + const cp = m('forever'); |
| 211 | + |
| 212 | + setTimeout(function () { |
| 213 | + process.kill(cp.pid, 'SIGINT'); |
| 214 | + }, 100); |
| 215 | + |
| 216 | + const err = await t.throws(cp); |
| 217 | + |
| 218 | + t.is(err.signal, 'SIGINT'); |
| 219 | + }); |
| 220 | + |
| 221 | + test('err.signal is SIGTERM', async t => { |
| 222 | + const cp = m('forever'); |
| 223 | + |
| 224 | + setTimeout(function () { |
| 225 | + process.kill(cp.pid, 'SIGTERM'); |
| 226 | + }, 100); |
| 227 | + |
| 228 | + const err = await t.throws(cp); |
| 229 | + |
| 230 | + t.is(err.signal, 'SIGTERM'); |
| 231 | + }); |
| 232 | +} |
| 233 | + |
| 234 | +test('result.signal is null for successful execution', async t => { |
| 235 | + t.is((await m('noop')).signal, null); |
| 236 | +}); |
| 237 | + |
| 238 | +test('result.signal is null if process failed, but was not killed', async t => { |
| 239 | + const err = await t.throws(m('exit', [2])); |
| 240 | + t.is(err.signal, null); |
| 241 | +}); |
| 242 | + |
| 243 | +async function code(t, num) { |
| 244 | + const err = await t.throws(m('exit', [`${num}`])); |
| 245 | + |
| 246 | + t.is(err.code, num); |
| 247 | +} |
| 248 | + |
| 249 | +test('err.code is 2', code, 2); |
| 250 | +test('err.code is 3', code, 3); |
| 251 | +test('err.code is 4', code, 4); |
| 252 | + |
| 253 | +async function errorMessage(t, expected, ...args) { |
| 254 | + const err = await t.throws(m('exit', args)); |
| 255 | + |
| 256 | + t.regex(err.message, expected); |
| 257 | +} |
| 258 | + |
| 259 | +errorMessage.title = (message, expected) => `err.message matches: ${expected}`; |
| 260 | + |
| 261 | +test(errorMessage, /Command failed: exit 2 foo bar/, 2, 'foo', 'bar'); |
| 262 | +test(errorMessage, /Command failed: exit 3 baz quz/, 3, 'baz', 'quz'); |
| 263 | + |
| 264 | +async function cmd(t, expected, ...args) { |
| 265 | + const err = await t.throws(m('fail', args)); |
| 266 | + |
| 267 | + t.is(err.cmd, `fail${expected}`); |
| 268 | + |
| 269 | + const result = await m('noop', args); |
| 270 | + |
| 271 | + t.is(result.cmd, `noop${expected}`); |
| 272 | +} |
| 273 | + |
| 274 | +cmd.title = (message, expected) => `cmd is: ${JSON.stringify(expected)}`; |
| 275 | + |
| 276 | +test(cmd, ' foo bar', 'foo', 'bar'); |
| 277 | +test(cmd, ' baz quz', 'baz', 'quz'); |
| 278 | +test(cmd, ''); |
0 commit comments