Skip to content

Commit

Permalink
Separate error tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Jun 23, 2019
1 parent b04a328 commit dd84fbd
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 158 deletions.
165 changes: 165 additions & 0 deletions test/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import path from 'path';
import childProcess from 'child_process';
import test from 'ava';
import execa from '..';

process.env.PATH = path.join(__dirname, 'fixtures') + path.delimiter + process.env.PATH;

const TIMEOUT_REGEXP = /timed out after/;

const getExitRegExp = exitMessage => new RegExp(`failed with exit code ${exitMessage}`);

test('stdout/stderr/all available on errors', async t => {
const {stdout, stderr, all} = await t.throwsAsync(execa('exit', ['2']), {message: getExitRegExp('2')});
t.is(typeof stdout, 'string');
t.is(typeof stderr, 'string');
t.is(typeof all, 'string');
});

const WRONG_COMMAND = process.platform === 'win32' ?
'\'wrong\' is not recognized as an internal or external command,\r\noperable program or batch file.' :
'';

test('stdout/stderr/all on process errors', async t => {
const {stdout, stderr, all} = await t.throwsAsync(execa('wrong command'));
t.is(stdout, '');
t.is(stderr, WRONG_COMMAND);
t.is(all, WRONG_COMMAND);
});

test('stdout/stderr/all on process errors, in sync mode', t => {
const {stdout, stderr, all} = t.throws(() => {
execa.sync('wrong command');
});
t.is(stdout, '');
t.is(stderr, WRONG_COMMAND);
t.is(all, undefined);
});

test('allow unknown exit code', async t => {
const {exitCode, exitCodeName} = await t.throwsAsync(execa('exit', ['255']), {message: /exit code 255 \(Unknown system error -255\)/});
t.is(exitCode, 255);
t.is(exitCodeName, 'Unknown system error -255');
});

test('execa() does not return code and failed properties on success', async t => {
const {exitCode, exitCodeName, failed} = await execa('noop', ['foo']);
t.is(exitCode, 0);
t.is(exitCodeName, 'SUCCESS');
t.false(failed);
});

test('execa() returns code and failed properties', async t => {
const {exitCode, exitCodeName, failed} = await t.throwsAsync(execa('exit', ['2']), {message: getExitRegExp('2')});
t.is(exitCode, 2);
const expectedName = process.platform === 'win32' ? 'Unknown system error -2' : 'ENOENT';
t.is(exitCodeName, expectedName);
t.true(failed);
});

test('error.killed is true if process was killed directly', async t => {
const cp = execa('forever');

cp.kill();

const {killed} = await t.throwsAsync(cp, {message: /was killed with SIGTERM/});
t.true(killed);
});

test('error.killed is false if process was killed indirectly', async t => {
const cp = execa('forever');

process.kill(cp.pid, 'SIGINT');

// `process.kill()` is emulated by Node.js on Windows
const message = process.platform === 'win32' ? /failed with exit code 1/ : /was killed with SIGINT/;
const {killed} = await t.throwsAsync(cp, {message});
t.false(killed);
});

test('result.killed is false if not killed', async t => {
const {killed} = await execa('noop');
t.false(killed);
});

test('result.killed is false if not killed, in sync mode', t => {
const {killed} = execa.sync('noop');
t.false(killed);
});

test('result.killed is false on process error', async t => {
const {killed} = await t.throwsAsync(execa('wrong command'));
t.false(killed);
});

test('result.killed is false on process error, in sync mode', t => {
const {killed} = t.throws(() => {
execa.sync('wrong command');
});
t.false(killed);
});

if (process.platform === 'darwin') {
test.cb('sanity check: child_process.exec also has killed.false if killed indirectly', t => {
const {pid} = childProcess.exec('forever', error => {
t.truthy(error);
t.false(error.killed);
t.end();
});

process.kill(pid, 'SIGINT');
});
}

if (process.platform !== 'win32') {
test('error.signal is SIGINT', async t => {
const cp = execa('forever');

process.kill(cp.pid, 'SIGINT');

const {signal} = await t.throwsAsync(cp, {message: /was killed with SIGINT/});
t.is(signal, 'SIGINT');
});

test('error.signal is SIGTERM', async t => {
const cp = execa('forever');

process.kill(cp.pid, 'SIGTERM');

const {signal} = await t.throwsAsync(cp, {message: /was killed with SIGTERM/});
t.is(signal, 'SIGTERM');
});

test('custom error.signal', async t => {
const {signal} = await t.throwsAsync(execa('forever', {killSignal: 'SIGHUP', timeout: 1, message: TIMEOUT_REGEXP}));
t.is(signal, 'SIGHUP');
});
}

test('result.signal is undefined for successful execution', async t => {
const {signal} = await execa('noop');
t.is(signal, undefined);
});

test('result.signal is undefined if process failed, but was not killed', async t => {
const {signal} = await t.throwsAsync(execa('exit', [2]), {message: getExitRegExp('2')});
t.is(signal, undefined);
});

const testExitCode = async (t, num) => {
const {exitCode} = await t.throwsAsync(execa('exit', [`${num}`]), {message: getExitRegExp(num)});
t.is(exitCode, num);
};

test('error.exitCode is 2', testExitCode, 2);
test('error.exitCode is 3', testExitCode, 3);
test('error.exitCode is 4', testExitCode, 4);

const errorMessage = async (t, expected, ...args) => {
await t.throwsAsync(execa('exit', args), {message: expected});
};

errorMessage.title = (message, expected) => `error.message matches: ${expected}`;

test(errorMessage, /Command failed with exit code 2.*: exit 2 foo bar/, 2, 'foo', 'bar');
test(errorMessage, /Command failed with exit code 3.*: exit 3 baz quz/, 3, 'baz', 'quz');
158 changes: 0 additions & 158 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import path from 'path';
import fs from 'fs';
import stream from 'stream';
import childProcess from 'child_process';
import test from 'ava';
import getStream from 'get-stream';
import isRunning from 'is-running';
Expand All @@ -14,8 +13,6 @@ process.env.FOO = 'foo';

const TIMEOUT_REGEXP = /timed out after/;

const getExitRegExp = exitMessage => new RegExp(`failed with exit code ${exitMessage}`);

test('execa()', async t => {
const {stdout} = await execa('noop', ['foo']);
t.is(stdout, 'foo');
Expand Down Expand Up @@ -44,13 +41,6 @@ test.serial('result.all shows both `stdout` and `stderr` intermixed', async t =>
t.is(all, '132');
});

test('stdout/stderr/all available on errors', async t => {
const {stdout, stderr, all} = await t.throwsAsync(execa('exit', ['2']), {message: getExitRegExp('2')});
t.is(typeof stdout, 'string');
t.is(typeof stderr, 'string');
t.is(typeof all, 'string');
});

test('stdout/stderr/all are undefined if ignored', async t => {
const {stdout, stderr, all} = await execa('noop', {stdio: 'ignore'});
t.is(stdout, undefined);
Expand All @@ -65,26 +55,6 @@ test('stdout/stderr/all are undefined if ignored in sync mode', t => {
t.is(all, undefined);
});

const WRONG_COMMAND = process.platform === 'win32' ?
'\'wrong\' is not recognized as an internal or external command,\r\noperable program or batch file.' :
'';

test('stdout/stderr/all on process errors', async t => {
const {stdout, stderr, all} = await t.throwsAsync(execa('wrong command'));
t.is(stdout, '');
t.is(stderr, WRONG_COMMAND);
t.is(all, WRONG_COMMAND);
});

test('stdout/stderr/all on process errors, in sync mode', t => {
const {stdout, stderr, all} = t.throws(() => {
execa.sync('wrong command');
});
t.is(stdout, '');
t.is(stderr, WRONG_COMMAND);
t.is(all, undefined);
});

test('pass `stdout` to a file descriptor', async t => {
const file = tempfile('.txt');
await execa('test/fixtures/noop', ['foo bar'], {stdout: fs.openSync(file, 'w')});
Expand Down Expand Up @@ -360,27 +330,6 @@ test('do not buffer stderr when `buffer` set to `false`', async t => {
t.is(stderr, '.........\n');
});

test('allow unknown exit code', async t => {
const {exitCode, exitCodeName} = await t.throwsAsync(execa('exit', ['255']), {message: /exit code 255 \(Unknown system error -255\)/});
t.is(exitCode, 255);
t.is(exitCodeName, 'Unknown system error -255');
});

test('execa() does not return code and failed properties on success', async t => {
const {exitCode, exitCodeName, failed} = await execa('noop', ['foo']);
t.is(exitCode, 0);
t.is(exitCodeName, 'SUCCESS');
t.false(failed);
});

test('execa() returns code and failed properties', async t => {
const {exitCode, exitCodeName, failed} = await t.throwsAsync(execa('exit', ['2']), {message: getExitRegExp('2')});
t.is(exitCode, 2);
const expectedName = process.platform === 'win32' ? 'Unknown system error -2' : 'ENOENT';
t.is(exitCodeName, expectedName);
t.true(failed);
});

test('use relative path with \'..\' chars', async t => {
const pathViaParentDir = path.join('..', path.basename(path.dirname(__dirname)), 'test', 'fixtures', 'noop');
const {stdout} = await execa(pathViaParentDir, ['foo']);
Expand All @@ -398,104 +347,6 @@ if (process.platform !== 'win32') {
});
}

test('error.killed is true if process was killed directly', async t => {
const cp = execa('forever');

cp.kill();

const {killed} = await t.throwsAsync(cp, {message: /was killed with SIGTERM/});
t.true(killed);
});

test('error.killed is false if process was killed indirectly', async t => {
const cp = execa('forever');

process.kill(cp.pid, 'SIGINT');

// `process.kill()` is emulated by Node.js on Windows
const message = process.platform === 'win32' ? /failed with exit code 1/ : /was killed with SIGINT/;
const {killed} = await t.throwsAsync(cp, {message});
t.false(killed);
});

test('result.killed is false if not killed', async t => {
const {killed} = await execa('noop');
t.false(killed);
});

test('result.killed is false if not killed, in sync mode', t => {
const {killed} = execa.sync('noop');
t.false(killed);
});

test('result.killed is false on process error', async t => {
const {killed} = await t.throwsAsync(execa('wrong command'));
t.false(killed);
});

test('result.killed is false on process error, in sync mode', t => {
const {killed} = t.throws(() => {
execa.sync('wrong command');
});
t.false(killed);
});

if (process.platform === 'darwin') {
test.cb('sanity check: child_process.exec also has killed.false if killed indirectly', t => {
const {pid} = childProcess.exec('forever', error => {
t.truthy(error);
t.false(error.killed);
t.end();
});

process.kill(pid, 'SIGINT');
});
}

if (process.platform !== 'win32') {
test('error.signal is SIGINT', async t => {
const cp = execa('forever');

process.kill(cp.pid, 'SIGINT');

const {signal} = await t.throwsAsync(cp, {message: /was killed with SIGINT/});
t.is(signal, 'SIGINT');
});

test('error.signal is SIGTERM', async t => {
const cp = execa('forever');

process.kill(cp.pid, 'SIGTERM');

const {signal} = await t.throwsAsync(cp, {message: /was killed with SIGTERM/});
t.is(signal, 'SIGTERM');
});

test('custom error.signal', async t => {
const {signal} = await t.throwsAsync(execa('forever', {killSignal: 'SIGHUP', timeout: 1, message: TIMEOUT_REGEXP}));
t.is(signal, 'SIGHUP');
});
}

test('result.signal is undefined for successful execution', async t => {
const {signal} = await execa('noop');
t.is(signal, undefined);
});

test('result.signal is undefined if process failed, but was not killed', async t => {
const {signal} = await t.throwsAsync(execa('exit', [2]), {message: getExitRegExp('2')});
t.is(signal, undefined);
});

const testExitCode = async (t, num) => {
const {exitCode} = await t.throwsAsync(execa('exit', [`${num}`]), {message: getExitRegExp(num)});
t.is(exitCode, num);
};

test('error.exitCode is 2', testExitCode, 2);
test('error.exitCode is 3', testExitCode, 3);
test('error.exitCode is 4', testExitCode, 4);

test('timeout kills the process if it times out', async t => {
const {killed, timedOut} = await t.throwsAsync(execa('forever', {timeout: 1, message: TIMEOUT_REGEXP}));
t.false(killed);
Expand Down Expand Up @@ -525,15 +376,6 @@ test('timedOut will be false if no timeout was set and zero exit code in sync mo
t.false(timedOut);
});

const errorMessage = async (t, expected, ...args) => {
await t.throwsAsync(execa('exit', args), {message: expected});
};

errorMessage.title = (message, expected) => `error.message matches: ${expected}`;

test(errorMessage, /Command failed with exit code 2.*: exit 2 foo bar/, 2, 'foo', 'bar');
test(errorMessage, /Command failed with exit code 3.*: exit 3 baz quz/, 3, 'baz', 'quz');

const command = async (t, expected, ...args) => {
const {command: failCommand} = await t.throwsAsync(execa('fail', args));
t.is(failCommand, `fail${expected}`);
Expand Down

0 comments on commit dd84fbd

Please sign in to comment.